├── icons
├── 1.png
├── send.png
├── user.png
├── smile1.png
├── smile10.png
├── smile11.png
├── smile12.png
├── smile13.png
├── smile14.png
├── smile15.png
├── smile2.png
├── smile3.png
├── smile4.png
├── smile5.png
├── smile6.png
├── smile7.png
├── smile8.png
└── smile9.png
├── data
├── config.json
└── servers.json
├── requirements.txt
├── README.md
├── methods
├── ConnectThreadMonitor.py
├── SettingsPanel.py
└── windows
│ ├── settings.ui
│ └── settings.py
├── server
└── server.py
├── main.py
├── des.py
└── untitled.ui
/icons/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/1.png
--------------------------------------------------------------------------------
/icons/send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/send.png
--------------------------------------------------------------------------------
/icons/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/user.png
--------------------------------------------------------------------------------
/icons/smile1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile1.png
--------------------------------------------------------------------------------
/icons/smile10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile10.png
--------------------------------------------------------------------------------
/icons/smile11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile11.png
--------------------------------------------------------------------------------
/icons/smile12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile12.png
--------------------------------------------------------------------------------
/icons/smile13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile13.png
--------------------------------------------------------------------------------
/icons/smile14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile14.png
--------------------------------------------------------------------------------
/icons/smile15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile15.png
--------------------------------------------------------------------------------
/icons/smile2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile2.png
--------------------------------------------------------------------------------
/icons/smile3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile3.png
--------------------------------------------------------------------------------
/icons/smile4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile4.png
--------------------------------------------------------------------------------
/icons/smile5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile5.png
--------------------------------------------------------------------------------
/icons/smile6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile6.png
--------------------------------------------------------------------------------
/icons/smile7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile7.png
--------------------------------------------------------------------------------
/icons/smile8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile8.png
--------------------------------------------------------------------------------
/icons/smile9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zproger/Messenger/HEAD/icons/smile9.png
--------------------------------------------------------------------------------
/data/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "nick": "admin",
3 | "server_ip": "127.0.0.1",
4 | "server_port": "5555"
5 | }
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | cffi==1.14.5
2 | colorama==0.4.4
3 | cryptography==3.4.7
4 | loguru==0.5.3
5 | pycparser==2.20
6 | PyQt5==5.15.4
7 | PyQt5-Qt5==5.15.2
8 | PyQt5-sip==12.9.0
9 | win32-setctime==1.0.3
10 |
--------------------------------------------------------------------------------
/data/servers.json:
--------------------------------------------------------------------------------
1 | {
2 | "server1": {
3 | "ip": "127.0.0.1",
4 | "port": "5555"
5 | },
6 | "server2": {
7 | "ip": "127.0.0.1",
8 | "port": "4444"
9 | },
10 | "server3": {
11 | "ip": "127.0.0.1",
12 | "port": "6666"
13 | }
14 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Messenger
2 | Мессенджер, который гарантирует приватность вашей переписки.
3 | Вы сами контролируете весь процесс.
4 |
5 | ### Мессенджер находится на этапе тестирования, поэтому возможны ошибки в работе ПО!
6 |
7 | ### Возможности
8 | - Все данные о личных сообщениях хранятся только в памяти и будут уничтожены после завершения сеанса
9 | - Возможность самому контролировать сервер и клиентов, которые подключаются
10 | - Возможность добавлять огромное количество серверов и переключаться между ними
11 | - Доступ к чату разрешен только людям, которые знают IP:PORT вашего сервера и имеют доступ к клиенту
12 | - Ключ шифрования генерируется каждый раз при попытке соединиться с сервером
13 | - Кроссплатформленность, клиент и сервер работают на любых системах с поддержкой Qt, Python, Socket
14 | - Возможность отслеживать входящие соединения
15 |
16 | ### Установка и использование
17 | 1. Устанавливаем Python 3.8
18 | 2. Устанавливаем зависимости и настраиваем проект
19 |
20 | ```sh
21 | git clone https://github.com/Zproger/Messenger.git
22 | cd Messenger
23 | pip3 install -r requirements.txt (на клиенте и сервере)
24 | ```
25 |
26 | 3. Покупаем выделенный Ubuntu сервер с белым IP и отправляем на него файл server.py
27 | 4. Устанавливаем Tmux
28 |
29 | ```sh
30 | sudo apt update
31 | sudo apt install tmux (Для систем на Ubuntu)
32 | sudo pacman -S tmux (Для систем на Arch)
33 | ```
34 |
35 | 5. Открываем сервер и редактируем IP:PORT на значения 0.0.0.0:4444
36 |
37 | ```python3
38 | if __name__ == "__main__":
39 | myserver = Server('0.0.0.0', 4444)
40 | ```
41 |
42 | 6. Создаем сессию Tmux и запускаем сервер
43 |
44 | ```sh
45 | tmux new-session -t server
46 | python3 server.py
47 | Ctrl+B, D
48 | ```
49 |
50 | 7. Запускаем main.py клиент, добавляем белый IP сервера и указываем порт 4444
51 | 8. Производим коннект и пользуемся
52 |
53 | ### Возможные ошибки
54 | 1. Перестал работать сервер
55 | - Проверьте работает ли сессия Tmux и ознакомьтесь с след.видео:
56 | https://www.youtube.com/watch?v=8pfMmx0LF8k
57 | 2. Любые ошибки клиента и сервера сохраняются в папке logs/
58 | - Данная информация не отправляется на наши сервера, а хранится только у вас.
59 | Вы можете передать ошибку нам для дальнейшего исправления.
60 | 3. Клиент не подключается к серверу
61 | - Проверьте правильно ли указан белый IP сервера и порт
62 | - Проверьте установлены ли зависимости на сервере и клиенте
63 | - Смотрите 1-й пункт
64 |
65 | ### Демонстрация ПО
66 | 
67 |
68 |
--------------------------------------------------------------------------------
/methods/ConnectThreadMonitor.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 | import pickle
5 | from PyQt5 import QtCore
6 | from loguru import logger
7 | from cryptography.fernet import Fernet
8 | from methods.windows.settings import *
9 |
10 |
11 | # Мониторинг входящих сообщений
12 | class message_monitor(QtCore.QThread):
13 | mysignal = QtCore.pyqtSignal(list)
14 | server_socket = None
15 | symmetric_key = None
16 |
17 | def __init__(self, parent=None):
18 | QtCore.QThread.__init__(self, parent)
19 |
20 | def run(self):
21 | logger.info("Запущен потоковый обработчик")
22 | while True:
23 | if self.server_socket != None:
24 | try:
25 | message = self.server_socket.recv(7168)
26 | except OSError:
27 | return # Возникает при отключении от сервера, так как поток ожидаем данные по закрытому каналу связи
28 |
29 | pickle_dec = pickle.loads(message)
30 |
31 | # Если это запрос на заполнение ключей
32 | # ["SERVER_OK", "MESSAGE", "KEY"]
33 | if pickle_dec[0] == "SERVER_OK":
34 | logger.info(f"SERVER_OK: {pickle_dec}")
35 | self.symmetric_key = pickle_dec[-1]
36 | self.cipher = Fernet(self.symmetric_key) # Объект шифровальщика
37 | self.mysignal.emit(pickle_dec)
38 |
39 | # Если поступает пользовательское уведомление
40 | # pickle_dec[1] содержит сообщение
41 | elif pickle_dec[0] == "USERS_NOTIFY":
42 | logger.info(f"USERS_NOTIFY: {pickle_dec}")
43 | decrypted_payload = ["USERS_NOTIFY", pickle_dec[1]]
44 | self.mysignal.emit(decrypted_payload)
45 |
46 | # Обработка сообщений от других пользователей
47 | # ['ENCRYPT_MESSAGE', self.nick, smile_num, message_text.encode()]
48 | elif pickle_dec[0] == "ENCRYPT_MESSAGE":
49 | logger.info(f"ENCRYPT_MESSAGE: {pickle_dec}")
50 | decrypted_text = self.cipher.decrypt(pickle_dec[-1]).decode()
51 | decrypted_payload = ["ENCRYPT_MESSAGE", pickle_dec[1], pickle_dec[2], decrypted_text]
52 | self.mysignal.emit(decrypted_payload)
53 | time.sleep(0.5)
54 |
55 | # Отправить зашифрованное сообщение на сервер
56 | def send_encrypt(self, data_list):
57 | # ['ENCRYPT_MESSAGE', self.nick, smile_num, message_text.encode()]
58 | if data_list[0] == "ENCRYPT_MESSAGE":
59 | encrypted_message = self.cipher.encrypt(data_list[-1])
60 | payload = pickle.dumps(['ENCRYPT_MESSAGE', data_list[1], data_list[2], encrypted_message])
61 |
62 | try:
63 | self.server_socket.send(payload)
64 | except Exception as err:
65 | logger.exception("ConnectionError:")
66 | self.mysignal.emit(["CONNECTION_ERROR"])
67 | return
68 |
69 | # Если нужно уведомить всех пользователей
70 | # ["USERS_NOTIFY", "message"]
71 | elif data_list[0] == "USERS_NOTIFY":
72 | payload = pickle.dumps(data_list)
73 | self.server_socket.send(payload)
74 |
75 | # Если клиент разорвал соединение
76 | # ['EXIT', f'{self.nick}', 'вышел из чата!']
77 | elif data_list[0] == "EXIT":
78 | try:
79 | payload = pickle.dumps(data_list)
80 | self.server_socket.send(payload)
81 | # Если сервер отключен и мы пытаемся в этот
82 | # момент из него выйти
83 | except:
84 | pass
--------------------------------------------------------------------------------
/server/server.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 | import pickle
5 | import socket
6 | import threading
7 | from loguru import logger
8 | from cryptography.fernet import Fernet
9 |
10 |
11 | # Настраиваем глобальный логгер для вывода в stdout
12 | # ============================================================================================================
13 | logger.remove()
14 | logger.add(
15 | sink=sys.stdout,
16 | colorize=True,
17 | format="{time:HH:mm:ss} - {name}:{function}:{line} - {message}",
18 | level="DEBUG"
19 | )
20 |
21 | # Конфигурация логгера, который работает с журналом
22 | logger.add(
23 | sink=os.path.join("logs", "server_log.txt"),
24 | format="{time:HH:mm:ss} - {name}:{function}:{line} - {message}",
25 | level="INFO",
26 | rotation="100 MB",
27 | compression="zip",
28 | enqueue=True # Делаем логгер потокобезопасным
29 | )
30 | # ============================================================================================================
31 |
32 |
33 | class Server:
34 | def __init__(self, ip, port):
35 | self.ip = ip
36 | self.port = port
37 | self.all_client = []
38 | self.symmetric_key = None
39 |
40 | # Запускаем прослушивание соединений
41 | self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
42 | self.server.bind((self.ip, self.port))
43 | self.server.listen(0)
44 | threading.Thread(target=self.connect_handler).start()
45 | logger.debug(f"Сервер запущен ({self.ip}:{self.port})")
46 |
47 |
48 | # Обрабатываем входящие соединения
49 | def connect_handler(self):
50 | logger.debug("Запущен обработчик входящих соединений")
51 | while True:
52 | client, address = self.server.accept()
53 | if client not in self.all_client:
54 | self.all_client.append(client)
55 |
56 | # Отправляем запрос на успешное подключение + раздаем симметричные ключи
57 | self.get_key()
58 | payload = ['SERVER_OK', "Успешное подключение к чату!", self.symmetric_key]
59 | client.send(pickle.dumps(payload))
60 | logger.info(f'{address} - Успешное подключение к чату!')
61 | threading.Thread(target=self.message_handler, args=(client,)).start()
62 | time.sleep(2)
63 |
64 |
65 | # Отправляем сообщение всем клиентам кроме текущего
66 | # Принимает client_socket и сообщение для рассылки
67 | def sendall(self, current_socket, message):
68 | for client in self.all_client:
69 | if client != current_socket:
70 | client.send(message)
71 |
72 |
73 | # Обрабатываем отправленный текст
74 | def message_handler(self, client_socket):
75 | while True:
76 | try:
77 | message = client_socket.recv(7168)
78 | pickle_dec = pickle.loads(message)
79 | logger.debug(pickle_dec)
80 | except Exception as err:
81 | logger.exception(f"message_handler error:")
82 | self.all_client.remove(client_socket)
83 | logger.error(f"Неизвестный клиент удален с сервера из-за ошибки")
84 | break
85 |
86 | # Отправляем зашифрованное сообщение всем клиентам
87 | if pickle_dec[0] == "ENCRYPT_MESSAGE":
88 | self.sendall(client_socket, message)
89 |
90 | # Уведомление всем пользователям
91 | elif pickle_dec[0] == "USERS_NOTIFY":
92 | client_msg = pickle.dumps(["USERS_NOTIFY", pickle_dec[1]])
93 | self.sendall(client_socket, client_msg)
94 |
95 | # Если клиент отправил запрос на дисконнект
96 | elif pickle_dec[0] == "EXIT":
97 | logger.debug(f'{pickle_dec[1]} - разорвал соединение!')
98 | logger.debug("=" * 50)
99 |
100 | # Отправляем уведомление всех клиентам
101 | client_msg = pickle.dumps(["USERS_NOTIFY", f"{pickle_dec[1]} - Покинул чат"])
102 | self.sendall(client_socket, client_msg)
103 | self.all_client.remove(client_socket)
104 | break
105 | time.sleep(1)
106 |
107 |
108 | # Генератор сессионного симметричного ключа
109 | def get_key(self) -> None:
110 | if self.symmetric_key is None:
111 | logger.debug("Сгенерирован симметричный ключ шифрования")
112 | self.symmetric_key = Fernet.generate_key()
113 |
114 |
115 | if __name__ == "__main__":
116 | myserver = Server('127.0.0.1', 5555)
117 |
--------------------------------------------------------------------------------
/methods/SettingsPanel.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import json
4 | import shutil
5 | from PyQt5 import QtCore, QtGui, QtWidgets
6 | from methods.windows.settings import *
7 |
8 |
9 | # Окно с настройками клиента
10 | class SettingPanel(QtWidgets.QWidget):
11 | def __init__(self, parent=None, signal=None):
12 | super().__init__(parent, QtCore.Qt.Window)
13 | self.setting = Ui_Form()
14 | self.setting.setupUi(self)
15 | self.setWindowModality(2)
16 |
17 | # Сигнал для возврата в интерфейс
18 | self.signal = signal
19 |
20 | # Отключаем стандартные границы окна программы
21 | self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
22 | self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
23 | self.center()
24 |
25 | # Растягиваем таблицу на всю ширину окна и скрываем нумерацию
26 | header = self.setting.tableWidget.horizontalHeader()
27 | for position in range(3):
28 | header.setSectionResizeMode(position, QtWidgets.QHeaderView.Stretch)
29 | self.setting.tableWidget.verticalHeader().setVisible(False)
30 |
31 | # Инициализируем все параметры окна
32 | self.init_data()
33 |
34 | # Обработчики кнопок
35 | self.setting.pushButton_7.clicked.connect(lambda: self.close())
36 | self.setting.pushButton_6.clicked.connect(self.save_config)
37 | self.setting.pushButton_8.clicked.connect(self.load_image)
38 | self.setting.pushButton_9.clicked.connect(self.delete_image)
39 | self.setting.pushButton_12.clicked.connect(self.add_item)
40 | self.setting.pushButton_10.clicked.connect(self.apply_data)
41 | self.setting.pushButton_11.clicked.connect(self.del_row)
42 |
43 | # Подгружаем настройки если они уже имеются
44 | if os.path.exists(os.path.join("data", "config.json")):
45 | with open(os.path.join("data", "config.json")) as file:
46 | data = json.load(file)
47 | self.setting.lineEdit_4.setText(data['nick'])
48 | self.setting.lineEdit_2.setText(data['server_ip'])
49 | self.setting.lineEdit_3.setText(data['server_port'])
50 |
51 |
52 | # Перетаскивание безрамочного окна
53 | # ==================================================================
54 | def center(self):
55 | qr = self.frameGeometry()
56 | cp = QtWidgets.QDesktopWidget().availableGeometry().center()
57 | qr.moveCenter(cp)
58 | self.move(qr.topLeft())
59 |
60 | def mousePressEvent(self, event):
61 | self.oldPos = event.globalPos()
62 |
63 | def mouseMoveEvent(self, event):
64 | try:
65 | delta = QtCore.QPoint(event.globalPos() - self.oldPos)
66 | self.move(self.x() + delta.x(), self.y() + delta.y())
67 | self.oldPos = event.globalPos()
68 | except AttributeError:
69 | pass
70 | # ==================================================================
71 |
72 |
73 | # Загрузить изображение в качестве аватарки пользователя
74 | def load_image(self):
75 | image_path = QtWidgets.QFileDialog.getOpenFileName(filter="*.png\n*.jpg")
76 |
77 | if image_path[0]: # Если пользователь выбрал изображение
78 | if os.path.getsize(image_path[0]) < 5120:
79 | # В зависимости от формата изображения задаем имя
80 | image_format = "png" if "png" in image_path[1] else "jpg"
81 | shutil.copy(image_path[0], os.path.join("data", f"custom.{image_format}"))
82 |
83 | # Обновляем Label который отвечает за изображение
84 | image_pixmap = QtGui.QPixmap(os.path.join("data", f"custom.{image_format}"))
85 | self.setting.label.setPixmap(image_pixmap)
86 | else:
87 | message = "Изображение не должно быть больше 5кб"
88 | QtWidgets.QMessageBox.about(self, "Ошибка", message)
89 |
90 |
91 | # Инициализация параметров при загрузке окна с настройками
92 | def init_data(self):
93 | # Если установлена аватарка - добавляем в Label
94 | for filename in os.listdir("data"):
95 | if "custom" in filename:
96 | image_pixmap = QtGui.QPixmap(os.path.join("data", filename))
97 | self.setting.label.setPixmap(image_pixmap)
98 |
99 | # Обновляем список серверов
100 | with open(os.path.join("data", "servers.json")) as file:
101 | server_list = json.load(file)
102 |
103 | # Бежим по ключам сервера, которые являются именем и заполняем таблицу
104 | for server in server_list:
105 | # Добавляем новую пустую строку
106 | rowPosition = self.setting.tableWidget.rowCount()
107 | self.setting.tableWidget.insertRow(rowPosition)
108 |
109 | # Заполняем её данными из конфига (имя, адрес, порт)
110 | self.setting.tableWidget.setItem(rowPosition, 0, QtWidgets.QTableWidgetItem(server))
111 | self.setting.tableWidget.setItem(rowPosition, 1, QtWidgets.QTableWidgetItem(server_list[server]["ip"]))
112 | self.setting.tableWidget.setItem(rowPosition, 2, QtWidgets.QTableWidgetItem(server_list[server]["port"]))
113 |
114 |
115 | # Удалить аватарку
116 | def delete_image(self):
117 | for filename in os.listdir("data"):
118 | if "custom" in filename:
119 | os.remove(os.path.join("data", filename))
120 | self.setting.label.setText("64x64")
121 |
122 |
123 | # Добавить новую строку в список с серверами
124 | def add_item(self):
125 | dataline = self.setting.lineEdit_5.text()
126 |
127 | # Если строка не пустая и содержит 3 элемента
128 | if dataline and len(dataline.split(":")) == 3:
129 | parsed = dataline.split(":") # [name, ip, port]
130 |
131 | # Проверяем данные на валидность
132 | if not (self.check_ip(parsed[1]) and self.check_port(parsed[2])):
133 | QtWidgets.QMessageBox.about(self, "Ошибка", "Проверьте правильность ввода данных!")
134 | return
135 |
136 | # Проверяем нет ли таких значений в таблице
137 | # Бежим по всем строкам таблицы
138 | for row in range(self.setting.tableWidget.rowCount()):
139 | name = self.setting.tableWidget.item(row, 0).text()
140 | ip = self.setting.tableWidget.item(row, 1).text()
141 | port = self.setting.tableWidget.item(row, 2).text()
142 |
143 | # Проверяем уникальность имени и связки ip & port
144 | if (parsed[0] == name) or (parsed[1] == ip and parsed[2] == port):
145 | QtWidgets.QMessageBox.about(self, "Ошибка", "Такие данные уже существуют!")
146 | return
147 |
148 | # Если совпадение не найдено добавляем в таблицу пустую строку
149 | rowPosition = self.setting.tableWidget.rowCount()
150 | self.setting.tableWidget.insertRow(rowPosition)
151 |
152 | # Заполняем пустую строку значениями
153 | for num, text in enumerate(parsed):
154 | self.setting.tableWidget.setItem(rowPosition, num, QtWidgets.QTableWidgetItem(text))
155 |
156 |
157 | # Проверяем валидность ip адреса
158 | def check_ip(self, ip_line):
159 | regular_ip = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
160 | if not re.match(regular_ip, ip_line) is None:
161 | return True
162 | else:
163 | return False
164 |
165 |
166 | # Проверяем валидность порта
167 | def check_port(self, port_line):
168 | if port_line.isdecimal() and int(port_line) <= 65535:
169 | return True
170 | else:
171 | return False
172 |
173 |
174 | # Применить настройки из таблицы серверов
175 | def apply_data(self):
176 | row_index = self.setting.tableWidget.currentRow() # Индекс строки
177 |
178 | # Заменяем основные настройки на выбранные
179 | if row_index != -1:
180 | ip = self.setting.tableWidget.item(row_index, 1).text()
181 | port = self.setting.tableWidget.item(row_index, 2).text()
182 | self.setting.lineEdit_2.setText(ip)
183 | self.setting.lineEdit_3.setText(port)
184 |
185 |
186 | # Удалить строку из таблицы
187 | def del_row(self):
188 | row_index = self.setting.tableWidget.currentRow() # Индекс строки
189 | if row_index != -1:
190 | self.setting.tableWidget.removeRow(row_index)
191 |
192 |
193 | # Сохранить настройки пользователя
194 | def save_config(self):
195 | nick = self.setting.lineEdit_4.text()
196 | server_ip = self.setting.lineEdit_2.text()
197 | server_port = self.setting.lineEdit_3.text()
198 |
199 | # Обновляем датчики, для того чтобы пользователь видел какие поля правильные
200 | self.setting.lineEdit_2.setStyleSheet("border-radius: 7px;")
201 | self.setting.lineEdit_3.setStyleSheet("border-radius: 7px;")
202 | self.setting.lineEdit_4.setStyleSheet("border-radius: 7px;")
203 |
204 | # Проверяем корректность ввода пользователя
205 | if len(nick) >= 3 and len(nick) <= 20:
206 | if self.check_ip(server_ip):
207 | if self.check_port(server_port):
208 | # Перезаписываем конфиг с настройками пользователя
209 | with open(os.path.join("data", "config.json"), "w") as file:
210 | data = {"nick": nick, "server_ip": server_ip, "server_port": server_port}
211 | json.dump(data, file, indent=6)
212 |
213 | # Перезаписываем конфиг с настройками серверов
214 | all_server = {}
215 | for row in range(self.setting.tableWidget.rowCount()):
216 | name = self.setting.tableWidget.item(row, 0).text()
217 | ip = self.setting.tableWidget.item(row, 1).text()
218 | port = self.setting.tableWidget.item(row, 2).text()
219 |
220 | all_server[name] = {
221 | "ip": ip,
222 | "port": port
223 | }
224 |
225 | with open(os.path.join("data", "servers.json"), "w") as file:
226 | json.dump(all_server, file, indent=6)
227 |
228 | # Закрываем окно с настройками
229 | self.close()
230 | self.signal.emit(['update_config'])
231 | else:
232 | self.setting.lineEdit_3.setStyleSheet("border: 2px solid red; border-radius: 7px;")
233 | self.setting.lineEdit_3.setText("Проверьте правильность ввода SERVER_PORT")
234 | else:
235 | self.setting.lineEdit_2.setStyleSheet("border: 2px solid red; border-radius: 7px;")
236 | self.setting.lineEdit_2.setText("Проверьте правильность ввода SERVER_IP")
237 | else:
238 | self.setting.lineEdit_4.setStyleSheet("border: 2px solid red; border-radius: 7px;")
239 | self.setting.lineEdit_4.setText("Слишком длинный либо слишком короткий ник")
240 |
--------------------------------------------------------------------------------
/methods/windows/settings.ui:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/methods/windows/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'settings.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.1
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(961, 350)
18 | self.frame = QtWidgets.QFrame(Form)
19 | self.frame.setGeometry(QtCore.QRect(10, 10, 941, 331))
20 | self.frame.setStyleSheet("QFrame{\n"
21 | " border: 2px solid #434965;\n"
22 | " border-radius: 7px;\n"
23 | " background-color: #2A2F41;\n"
24 | "}")
25 | self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
26 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
27 | self.frame.setObjectName("frame")
28 | self.lineEdit_2 = QtWidgets.QLineEdit(self.frame)
29 | self.lineEdit_2.setGeometry(QtCore.QRect(10, 180, 321, 41))
30 | font = QtGui.QFont()
31 | font.setPointSize(11)
32 | font.setBold(True)
33 | font.setWeight(75)
34 | self.lineEdit_2.setFont(font)
35 | self.lineEdit_2.setFocusPolicy(QtCore.Qt.ClickFocus)
36 | self.lineEdit_2.setStyleSheet("QLineEdit{\n"
37 | " border-radius: 7px;\n"
38 | "}")
39 | self.lineEdit_2.setText("")
40 | self.lineEdit_2.setAlignment(QtCore.Qt.AlignCenter)
41 | self.lineEdit_2.setClearButtonEnabled(False)
42 | self.lineEdit_2.setObjectName("lineEdit_2")
43 | self.lineEdit_3 = QtWidgets.QLineEdit(self.frame)
44 | self.lineEdit_3.setGeometry(QtCore.QRect(10, 230, 321, 41))
45 | font = QtGui.QFont()
46 | font.setPointSize(11)
47 | font.setBold(True)
48 | font.setWeight(75)
49 | self.lineEdit_3.setFont(font)
50 | self.lineEdit_3.setFocusPolicy(QtCore.Qt.ClickFocus)
51 | self.lineEdit_3.setStyleSheet("QLineEdit{\n"
52 | " border-radius: 7px;\n"
53 | "}")
54 | self.lineEdit_3.setText("")
55 | self.lineEdit_3.setAlignment(QtCore.Qt.AlignCenter)
56 | self.lineEdit_3.setClearButtonEnabled(False)
57 | self.lineEdit_3.setObjectName("lineEdit_3")
58 | self.pushButton_6 = QtWidgets.QPushButton(self.frame)
59 | self.pushButton_6.setGeometry(QtCore.QRect(150, 280, 181, 41))
60 | font = QtGui.QFont()
61 | font.setPointSize(11)
62 | font.setBold(True)
63 | font.setWeight(75)
64 | self.pushButton_6.setFont(font)
65 | self.pushButton_6.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
66 | self.pushButton_6.setStyleSheet("QPushButton{\n"
67 | " color: white;\n"
68 | " border-radius: 7px;\n"
69 | " background-color: #595F76;\n"
70 | "}\n"
71 | "\n"
72 | "QPushButton:hover{\n"
73 | " background-color: #50566E;\n"
74 | "}\n"
75 | "\n"
76 | "QPushButton:pressed{\n"
77 | " background-color: #434965;\n"
78 | "}")
79 | self.pushButton_6.setObjectName("pushButton_6")
80 | self.pushButton_7 = QtWidgets.QPushButton(self.frame)
81 | self.pushButton_7.setGeometry(QtCore.QRect(10, 280, 131, 41))
82 | font = QtGui.QFont()
83 | font.setPointSize(11)
84 | font.setBold(True)
85 | font.setWeight(75)
86 | self.pushButton_7.setFont(font)
87 | self.pushButton_7.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
88 | self.pushButton_7.setStyleSheet("QPushButton{\n"
89 | " color: white;\n"
90 | " border-radius: 7px;\n"
91 | " background-color: #595F76;\n"
92 | "}\n"
93 | "\n"
94 | "QPushButton:hover{\n"
95 | " background-color: #50566E;\n"
96 | "}\n"
97 | "\n"
98 | "QPushButton:pressed{\n"
99 | " background-color: #434965;\n"
100 | "}")
101 | self.pushButton_7.setObjectName("pushButton_7")
102 | self.lineEdit_4 = QtWidgets.QLineEdit(self.frame)
103 | self.lineEdit_4.setGeometry(QtCore.QRect(10, 130, 321, 41))
104 | font = QtGui.QFont()
105 | font.setPointSize(11)
106 | font.setBold(True)
107 | font.setWeight(75)
108 | self.lineEdit_4.setFont(font)
109 | self.lineEdit_4.setFocusPolicy(QtCore.Qt.ClickFocus)
110 | self.lineEdit_4.setStyleSheet("QLineEdit{\n"
111 | " border-radius: 7px;\n"
112 | "}")
113 | self.lineEdit_4.setText("")
114 | self.lineEdit_4.setAlignment(QtCore.Qt.AlignCenter)
115 | self.lineEdit_4.setClearButtonEnabled(False)
116 | self.lineEdit_4.setObjectName("lineEdit_4")
117 | self.label = QtWidgets.QLabel(self.frame)
118 | self.label.setGeometry(QtCore.QRect(10, 10, 131, 111))
119 | font = QtGui.QFont()
120 | font.setPointSize(11)
121 | font.setBold(True)
122 | font.setWeight(75)
123 | self.label.setFont(font)
124 | self.label.setStyleSheet("QLabel {\n"
125 | " color: white;\n"
126 | " border-radius: 0px;\n"
127 | " border: 2px solid #434965;;\n"
128 | "}")
129 | self.label.setAlignment(QtCore.Qt.AlignCenter)
130 | self.label.setObjectName("label")
131 | self.pushButton_8 = QtWidgets.QPushButton(self.frame)
132 | self.pushButton_8.setGeometry(QtCore.QRect(150, 20, 181, 41))
133 | font = QtGui.QFont()
134 | font.setPointSize(11)
135 | font.setBold(True)
136 | font.setWeight(75)
137 | self.pushButton_8.setFont(font)
138 | self.pushButton_8.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
139 | self.pushButton_8.setStyleSheet("QPushButton{\n"
140 | " color: white;\n"
141 | " border-radius: 7px;\n"
142 | " background-color: #595F76;\n"
143 | "}\n"
144 | "\n"
145 | "QPushButton:hover{\n"
146 | " background-color: #50566E;\n"
147 | "}\n"
148 | "\n"
149 | "QPushButton:pressed{\n"
150 | " background-color: #434965;\n"
151 | "}")
152 | self.pushButton_8.setObjectName("pushButton_8")
153 | self.pushButton_9 = QtWidgets.QPushButton(self.frame)
154 | self.pushButton_9.setGeometry(QtCore.QRect(150, 70, 181, 41))
155 | font = QtGui.QFont()
156 | font.setPointSize(11)
157 | font.setBold(True)
158 | font.setWeight(75)
159 | self.pushButton_9.setFont(font)
160 | self.pushButton_9.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
161 | self.pushButton_9.setStyleSheet("QPushButton{\n"
162 | " color: white;\n"
163 | " border-radius: 7px;\n"
164 | " background-color: #595F76;\n"
165 | "}\n"
166 | "\n"
167 | "QPushButton:hover{\n"
168 | " background-color: #50566E;\n"
169 | "}\n"
170 | "\n"
171 | "QPushButton:pressed{\n"
172 | " background-color: #434965;\n"
173 | "}")
174 | self.pushButton_9.setObjectName("pushButton_9")
175 | self.tableWidget = QtWidgets.QTableWidget(self.frame)
176 | self.tableWidget.setGeometry(QtCore.QRect(350, 60, 581, 211))
177 | font = QtGui.QFont()
178 | font.setPointSize(11)
179 | font.setBold(True)
180 | font.setWeight(75)
181 | self.tableWidget.setFont(font)
182 | self.tableWidget.setStyleSheet("QTableWidget {\n"
183 | " color: white;\n"
184 | "}")
185 | self.tableWidget.setObjectName("tableWidget")
186 | self.tableWidget.setColumnCount(3)
187 | self.tableWidget.setRowCount(0)
188 | item = QtWidgets.QTableWidgetItem()
189 | item.setTextAlignment(QtCore.Qt.AlignCenter)
190 | font = QtGui.QFont()
191 | font.setPointSize(11)
192 | font.setBold(False)
193 | font.setWeight(50)
194 | font.setKerning(True)
195 | font.setStyleStrategy(QtGui.QFont.PreferDefault)
196 | item.setFont(font)
197 | self.tableWidget.setHorizontalHeaderItem(0, item)
198 | item = QtWidgets.QTableWidgetItem()
199 | item.setTextAlignment(QtCore.Qt.AlignCenter)
200 | font = QtGui.QFont()
201 | font.setPointSize(11)
202 | item.setFont(font)
203 | self.tableWidget.setHorizontalHeaderItem(1, item)
204 | item = QtWidgets.QTableWidgetItem()
205 | item.setTextAlignment(QtCore.Qt.AlignCenter)
206 | font = QtGui.QFont()
207 | font.setPointSize(11)
208 | item.setFont(font)
209 | self.tableWidget.setHorizontalHeaderItem(2, item)
210 | self.label_2 = QtWidgets.QLabel(self.frame)
211 | self.label_2.setGeometry(QtCore.QRect(350, 20, 581, 31))
212 | font = QtGui.QFont()
213 | font.setPointSize(11)
214 | font.setBold(True)
215 | font.setWeight(75)
216 | self.label_2.setFont(font)
217 | self.label_2.setStyleSheet("QLabel {\n"
218 | " color: white;\n"
219 | " border: none;\n"
220 | " background-color: #595F76;\n"
221 | "}")
222 | self.label_2.setAlignment(QtCore.Qt.AlignCenter)
223 | self.label_2.setObjectName("label_2")
224 | self.pushButton_10 = QtWidgets.QPushButton(self.frame)
225 | self.pushButton_10.setGeometry(QtCore.QRect(800, 280, 131, 41))
226 | font = QtGui.QFont()
227 | font.setPointSize(11)
228 | font.setBold(True)
229 | font.setWeight(75)
230 | self.pushButton_10.setFont(font)
231 | self.pushButton_10.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
232 | self.pushButton_10.setStyleSheet("QPushButton{\n"
233 | " color: white;\n"
234 | " border-radius: 7px;\n"
235 | " background-color: #595F76;\n"
236 | "}\n"
237 | "\n"
238 | "QPushButton:hover{\n"
239 | " background-color: #50566E;\n"
240 | "}\n"
241 | "\n"
242 | "QPushButton:pressed{\n"
243 | " background-color: #434965;\n"
244 | "}")
245 | self.pushButton_10.setObjectName("pushButton_10")
246 | self.lineEdit_5 = QtWidgets.QLineEdit(self.frame)
247 | self.lineEdit_5.setGeometry(QtCore.QRect(410, 280, 321, 41))
248 | font = QtGui.QFont()
249 | font.setPointSize(11)
250 | font.setBold(True)
251 | font.setWeight(75)
252 | self.lineEdit_5.setFont(font)
253 | self.lineEdit_5.setFocusPolicy(QtCore.Qt.ClickFocus)
254 | self.lineEdit_5.setStyleSheet("QLineEdit{\n"
255 | " border-radius: 7px;\n"
256 | "}")
257 | self.lineEdit_5.setText("")
258 | self.lineEdit_5.setAlignment(QtCore.Qt.AlignCenter)
259 | self.lineEdit_5.setClearButtonEnabled(False)
260 | self.lineEdit_5.setObjectName("lineEdit_5")
261 | self.pushButton_11 = QtWidgets.QPushButton(self.frame)
262 | self.pushButton_11.setGeometry(QtCore.QRect(350, 280, 51, 41))
263 | font = QtGui.QFont()
264 | font.setPointSize(11)
265 | font.setBold(True)
266 | font.setWeight(75)
267 | self.pushButton_11.setFont(font)
268 | self.pushButton_11.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
269 | self.pushButton_11.setStyleSheet("QPushButton{\n"
270 | " color: white;\n"
271 | " border-radius: 7px;\n"
272 | " background-color: #595F76;\n"
273 | "}\n"
274 | "\n"
275 | "QPushButton:hover{\n"
276 | " background-color: #50566E;\n"
277 | "}\n"
278 | "\n"
279 | "QPushButton:pressed{\n"
280 | " background-color: #434965;\n"
281 | "}")
282 | self.pushButton_11.setObjectName("pushButton_11")
283 | self.pushButton_12 = QtWidgets.QPushButton(self.frame)
284 | self.pushButton_12.setGeometry(QtCore.QRect(740, 280, 51, 41))
285 | font = QtGui.QFont()
286 | font.setPointSize(14)
287 | font.setBold(True)
288 | font.setWeight(75)
289 | self.pushButton_12.setFont(font)
290 | self.pushButton_12.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
291 | self.pushButton_12.setStyleSheet("QPushButton{\n"
292 | " color: white;\n"
293 | " border-radius: 7px;\n"
294 | " background-color: #595F76;\n"
295 | "}\n"
296 | "\n"
297 | "QPushButton:hover{\n"
298 | " background-color: #50566E;\n"
299 | "}\n"
300 | "\n"
301 | "QPushButton:pressed{\n"
302 | " background-color: #434965;\n"
303 | "}")
304 | self.pushButton_12.setObjectName("pushButton_12")
305 |
306 | self.retranslateUi(Form)
307 | QtCore.QMetaObject.connectSlotsByName(Form)
308 |
309 | def retranslateUi(self, Form):
310 | _translate = QtCore.QCoreApplication.translate
311 | Form.setWindowTitle(_translate("Form", "Form"))
312 | self.lineEdit_2.setPlaceholderText(_translate("Form", "IP Сервера"))
313 | self.lineEdit_3.setPlaceholderText(_translate("Form", "PORT Сервера"))
314 | self.pushButton_6.setText(_translate("Form", "Сохранить и выйти"))
315 | self.pushButton_7.setText(_translate("Form", "Отменить"))
316 | self.lineEdit_4.setPlaceholderText(_translate("Form", "Никнейм"))
317 | self.label.setText(_translate("Form", "64x64"))
318 | self.pushButton_8.setText(_translate("Form", "Загрузить"))
319 | self.pushButton_9.setText(_translate("Form", "Удалить"))
320 | item = self.tableWidget.horizontalHeaderItem(0)
321 | item.setText(_translate("Form", "Название"))
322 | item = self.tableWidget.horizontalHeaderItem(1)
323 | item.setText(_translate("Form", "Адрес"))
324 | item = self.tableWidget.horizontalHeaderItem(2)
325 | item.setText(_translate("Form", "Порт"))
326 | self.label_2.setText(_translate("Form", "Список серверов"))
327 | self.pushButton_10.setText(_translate("Form", "Применить"))
328 | self.lineEdit_5.setPlaceholderText(_translate("Form", "Name:Ip:Port"))
329 | self.pushButton_11.setText(_translate("Form", "X"))
330 | self.pushButton_12.setText(_translate("Form", "+"))
331 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 | import json
5 | import socket
6 | from loguru import logger
7 | from PyQt5 import QtCore, QtGui, QtWidgets
8 | from des import *
9 |
10 | from methods.SettingsPanel import *
11 | from methods.ConnectThreadMonitor import *
12 |
13 |
14 | # Настраиваем глобальный логгер для вывода в stdout
15 | # ============================================================================================================
16 | logger.remove()
17 | logger.add(
18 | sink=sys.stdout,
19 | colorize=True,
20 | format="{time:HH:mm:ss} - {name}:{function}:{line} - {message}",
21 | level="DEBUG"
22 | )
23 |
24 | # Конфигурация логгера, который работает с журналом
25 | logger.add(
26 | sink=os.path.join("logs", "client_log.txt"),
27 | format="{time:HH:mm:ss} - {name}:{function}:{line} - {message}",
28 | level="INFO",
29 | rotation="100 MB",
30 | compression="zip",
31 | enqueue=True # Делаем логгер потокобезопасным
32 | )
33 | # ============================================================================================================
34 |
35 |
36 | # Интерфейс программы и обработчик событий внутри него
37 | class Client(QtWidgets.QMainWindow):
38 | def __init__(self, parent=None):
39 | QtWidgets.QWidget.__init__(self, parent)
40 | self.ui = Ui_MainWindow()
41 | self.ui.setupUi(self)
42 |
43 | # Данные из конфига (симметричный ключ получаем в ответе от сервера)
44 | self.nick = None
45 | self.ip = None
46 | self.port = None
47 | self.smile_type = None
48 | self.connect_status = False
49 |
50 | # Экземпляр класса для обработки соединений и сигналов
51 | self.connect_monitor = message_monitor()
52 | self.connect_monitor.mysignal.connect(self.signal_handler)
53 |
54 | # Отключаем стандартные границы окна программы
55 | self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
56 | self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
57 | self.center()
58 |
59 | # Блокируем кнопку "Выйти из чата"
60 | self.btn_locker(self.ui.pushButton_19, True)
61 |
62 | # Обработчики основных кнопок + кнопок с панели
63 | self.ui.pushButton.clicked.connect(self.send_message)
64 | self.ui.lineEdit.returnPressed.connect(self.send_message)
65 | self.ui.pushButton_2.clicked.connect(self.connect_to_server)
66 | self.ui.pushButton_3.clicked.connect(lambda: self.close())
67 | self.ui.pushButton_4.clicked.connect(lambda: self.ui.listWidget.clear())
68 | self.ui.pushButton_5.clicked.connect(lambda: self.showMinimized())
69 | self.ui.pushButton_7.clicked.connect(self.setting_panel)
70 | self.ui.pushButton_19.clicked.connect(self.server_disconnect)
71 |
72 | # Обработчик смайликов
73 | self.ui.pushButton_10.clicked.connect(lambda: self.smile_send('1'))
74 | self.ui.pushButton_11.clicked.connect(lambda: self.smile_send('2'))
75 | self.ui.pushButton_12.clicked.connect(lambda: self.smile_send('3'))
76 | self.ui.pushButton_6.clicked.connect(lambda: self.smile_send('4'))
77 | self.ui.pushButton_8.clicked.connect(lambda: self.smile_send('5'))
78 | self.ui.pushButton_9.clicked.connect(lambda: self.smile_send('6'))
79 | self.ui.pushButton_13.clicked.connect(lambda: self.smile_send('7'))
80 | self.ui.pushButton_17.clicked.connect(lambda: self.smile_send('8'))
81 | self.ui.pushButton_16.clicked.connect(lambda: self.smile_send('9'))
82 | self.ui.pushButton_14.clicked.connect(lambda: self.smile_send('10'))
83 | self.ui.pushButton_15.clicked.connect(lambda: self.smile_send('11'))
84 | self.ui.pushButton_18.clicked.connect(lambda: self.smile_send('12'))
85 | self.ui.pushButton_21.clicked.connect(lambda: self.smile_send('13'))
86 | self.ui.pushButton_22.clicked.connect(lambda: self.smile_send('14'))
87 | self.ui.pushButton_24.clicked.connect(lambda: self.smile_send('15'))
88 |
89 |
90 | # Перетаскивание безрамочного окна
91 | # ==================================================================
92 | def center(self):
93 | qr = self.frameGeometry()
94 | cp = QtWidgets.QDesktopWidget().availableGeometry().center()
95 | qr.moveCenter(cp)
96 | self.move(qr.topLeft())
97 |
98 | def mousePressEvent(self, event):
99 | self.oldPos = event.globalPos()
100 |
101 | def mouseMoveEvent(self, event):
102 | try:
103 | delta = QtCore.QPoint(event.globalPos() - self.oldPos)
104 | self.move(self.x() + delta.x(), self.y() + delta.y())
105 | self.oldPos = event.globalPos()
106 | except AttributeError:
107 | pass
108 | # ==================================================================
109 |
110 |
111 | # Отправить смайлик
112 | def smile_send(self, smile_number: str):
113 | btn_base = {'1': self.ui.pushButton_10,
114 | '2': self.ui.pushButton_11,
115 | '3': self.ui.pushButton_12,
116 | '4': self.ui.pushButton_6,
117 | '5': self.ui.pushButton_8,
118 | '6': self.ui.pushButton_9,
119 | '7': self.ui.pushButton_13,
120 | '8': self.ui.pushButton_17,
121 | '9': self.ui.pushButton_16,
122 | '10': self.ui.pushButton_14,
123 | '11': self.ui.pushButton_15,
124 | '12': self.ui.pushButton_18,
125 | '13': self.ui.pushButton_21,
126 | '14': self.ui.pushButton_22,
127 | '15': self.ui.pushButton_24}
128 |
129 | change_style = """
130 | border-radius: 35px;
131 | border: 2px solid white;
132 | """
133 |
134 | default_style = """
135 | border: none;
136 | """
137 |
138 | if self.smile_type == None:
139 | btn_base[smile_number].setStyleSheet(change_style)
140 | self.smile_type = smile_number
141 |
142 | elif self.smile_type != None and self.smile_type != smile_number:
143 | btn_base[self.smile_type].setStyleSheet(default_style)
144 | btn_base[smile_number].setStyleSheet(change_style)
145 | self.smile_type = smile_number
146 |
147 | elif self.smile_type != None and self.smile_type == smile_number:
148 | btn_base[smile_number].setStyleSheet(default_style)
149 | self.smile_type = None
150 |
151 | logger.debug(f"SELF.SMILE_TYPE: {self.smile_type}")
152 |
153 |
154 | # Открыть окно для настройки клиента
155 | def setting_panel(self):
156 | setting_win = SettingPanel(self, self.connect_monitor.mysignal)
157 | setting_win.show()
158 |
159 |
160 | # Обновление конфигов клиента
161 | def update_config(self):
162 | """
163 | Используется для обновления значений на лету, без необходимости
164 | перезапускать клиент (В случае если пользователь отредактировал настройки
165 | либо же запустил софт и необходимо проинициализировать значения)
166 | """
167 | # Если конфиг уже был создан
168 | if os.path.exists(os.path.join("data", "config.json")):
169 | with open(os.path.join("data", "config.json")) as file:
170 | data = json.load(file)
171 | self.nick = data['nick']
172 | self.ip = data['server_ip']
173 | self.port = int(data['server_port'])
174 |
175 |
176 | # Обработчик сигналов из потока
177 | def signal_handler(self, value: list):
178 | # Обновление параметров конфига
179 | if value[0] == "update_config":
180 | self.update_config()
181 |
182 | # Обновление симметричного ключа
183 | elif value[0] == "SERVER_OK":
184 | self.connect_status = True
185 | item = QtWidgets.QListWidgetItem()
186 | item.setTextAlignment(QtCore.Qt.AlignHCenter)
187 | item.setText(f"SERVER: {value[1]}\n")
188 | self.ui.listWidget.addItem(item)
189 | logger.info(value)
190 |
191 | # Отправляем уведомление о том, что мы вступили в чат
192 | payload = ['USERS_NOTIFY', f"{self.nick} - Вступил в чат"]
193 | self.connect_monitor.send_encrypt(payload)
194 |
195 | # Если пользователь вступил или вышел из чата
196 | elif value[0] == "USERS_NOTIFY":
197 | item = QtWidgets.QListWidgetItem()
198 | item.setTextAlignment(QtCore.Qt.AlignHCenter)
199 | item.setText(f"SERVER: {value[1]}\n")
200 | self.ui.listWidget.addItem(item)
201 | logger.info(value)
202 |
203 | # Обработка сообщений других пользователей
204 | # ['ENCRYPT_MESSAGE', self.nick, avatar, message_text.encode()]
205 | elif value[0] == "ENCRYPT_MESSAGE":
206 | item = QtWidgets.QListWidgetItem()
207 | item.setTextAlignment(QtCore.Qt.AlignLeft)
208 |
209 | # Используем аватарку пользователя
210 | size = QtCore.QSize(45, 45)
211 |
212 | # Если отправили смайлик вместо аватарки
213 | if str(value[2]).isdecimal():
214 | icon = QtGui.QIcon(os.path.join("icons", f"smile{value[2]}.png"))
215 |
216 | # Если отправлена аватарка
217 | elif isinstance(value[2], bytes):
218 | pixmap_obj = QtGui.QPixmap()
219 | pixmap_obj.loadFromData(value[2])
220 | icon = QtGui.QIcon(pixmap_obj)
221 |
222 | # Если нет аватарки и не выбран смайлик
223 | else:
224 | icon = QtGui.QIcon(os.path.join("icons", "user.png"))
225 |
226 | # Задаем иконку в строку
227 | item.setIcon(icon)
228 | self.ui.listWidget.setIconSize(size)
229 | self.ui.listWidget.addItem(item)
230 | item.setText(f"{value[1]}:\n{value[-1]}")
231 | logger.info(value)
232 |
233 | elif value[0] == "CONNECTION_ERROR":
234 | message = "Сервер разорвал соединение\nповторите попытку через несколько минут"
235 | QtWidgets.QMessageBox.about(self, "Ошибка Соединения", message)
236 | self.btn_locker(self.ui.pushButton_2, False)
237 | self.btn_locker(self.ui.pushButton_7, False)
238 | self.btn_locker(self.ui.pushButton_19, True)
239 | self.connect_status = False
240 | logger.info(value)
241 |
242 |
243 | # Отправить сообщение на сервер
244 | def send_message(self):
245 | if self.connect_status:
246 | message_text = self.ui.lineEdit.text()
247 | profile_photo = None # Путь к фото пользователя
248 |
249 | # Если пользователь выбрал смайлик
250 | if self.smile_type:
251 | profile_photo = self.smile_type
252 |
253 | # Если не выбрал смайлик но есть аватарка
254 | elif os.path.exists(os.path.join("data", "custom.png")):
255 | with open(os.path.join("data", "custom.png"), "rb") as photo:
256 | profile_photo = photo.read()
257 |
258 | # Если нет аватарки и нет смайлика
259 | else:
260 | profile_photo = None
261 |
262 | # Если поле с текстом не пустое - шифруем сообщение и передаем на сервер
263 | if len(message_text) > 0:
264 | payload = ['ENCRYPT_MESSAGE', self.nick, profile_photo, message_text.encode()]
265 | self.connect_monitor.send_encrypt(payload)
266 |
267 | # Добавляем свое сообщение в ListWidget
268 | item = QtWidgets.QListWidgetItem()
269 | item.setTextAlignment(QtCore.Qt.AlignLeft)
270 | size = QtCore.QSize(45, 45)
271 |
272 | # Прикрепляем к нему нашу аватарку
273 | if self.smile_type:
274 | icon = QtGui.QIcon(os.path.join("icons", f"smile{self.smile_type}.png"))
275 |
276 | elif os.path.exists(os.path.join("data", "custom.png")):
277 | icon = QtGui.QIcon(os.path.join("data", "custom.png"))
278 |
279 | else:
280 | icon = QtGui.QIcon(os.path.join("icons", "user.png"))
281 | self.ui.listWidget.setIconSize(size)
282 | item.setIcon(icon)
283 |
284 | # Выводим свое сообщение в панель и удаляем с поля воода
285 | item.setText(f"{self.nick} (ВЫ):\n{message_text}")
286 | self.ui.listWidget.addItem(item)
287 | self.ui.lineEdit.clear()
288 | else:
289 | message = "Проверьте соединение с сервером"
290 | QtWidgets.QMessageBox.about(self, "Оповещение", message)
291 |
292 |
293 | # Покдлючаемся к общему серверу
294 | def connect_to_server(self):
295 | self.update_config() # Обновляем данные пользователя
296 |
297 | if self.nick != None:
298 | try:
299 | self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
300 | self.client.connect((self.ip, self.port))
301 |
302 | # Запускаем мониторинг входящих сообщений
303 | self.connect_monitor.server_socket = self.client
304 | self.connect_monitor.start()
305 |
306 | # Блокируем кнопки и разблокируем кнопку "Выйти из чата"
307 | self.btn_locker(self.ui.pushButton_2, True)
308 | self.btn_locker(self.ui.pushButton_7, True)
309 | self.btn_locker(self.ui.pushButton_19, False)
310 | except Exception as err:
311 | message = "Ошибка соединения с сервером.\nПроверьте правильность ввода данных"
312 | QtWidgets.QMessageBox.about(self, "Оповещение", message)
313 | else: # Если пользователь не заполнил данные
314 | message = "Для начала заполните данные во вкладке 'Настройки'"
315 | QtWidgets.QMessageBox.about(self, "Оповещение", message)
316 |
317 |
318 | # Отключиться от сервера
319 | def server_disconnect(self):
320 | payload = ['EXIT', f'{self.nick}']
321 | self.connect_monitor.send_encrypt(payload)
322 | self.client.close()
323 | self.ui.listWidget.clear()
324 |
325 | # Снимаем блокировку с основных кнопок и блокируем кнопку для отключения
326 | self.btn_locker(self.ui.pushButton_19, True)
327 | self.btn_locker(self.ui.pushButton_2, False)
328 | self.btn_locker(self.ui.pushButton_7, False)
329 |
330 |
331 | # Блокировщик кнопок
332 | def btn_locker(self, btn: object, lock_status: bool) -> None:
333 | default_style = """
334 | QPushButton{
335 | color: white;
336 | border-radius: 7px;
337 | background-color: #595F76;
338 | }
339 | QPushButton:hover{
340 | background-color: #50566E;
341 | }
342 | QPushButton:pressed{
343 | background-color: #434965;
344 | }
345 | """
346 |
347 | lock_style = """
348 | color: #9EA2AB;
349 | border-radius: 7px;
350 | background-color: #2C313C;
351 | """
352 |
353 | if lock_status:
354 | btn.setDisabled(True)
355 | btn.setStyleSheet(lock_style)
356 | else:
357 | btn.setDisabled(False)
358 | btn.setStyleSheet(default_style)
359 |
360 |
361 | if __name__ == "__main__":
362 | app = QtWidgets.QApplication(sys.argv)
363 | myapp = Client()
364 | myapp.show()
365 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/des.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'untitled.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.1
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_MainWindow(object):
15 | def setupUi(self, MainWindow):
16 | MainWindow.setObjectName("MainWindow")
17 | MainWindow.resize(851, 521)
18 | self.centralwidget = QtWidgets.QWidget(MainWindow)
19 | self.centralwidget.setObjectName("centralwidget")
20 | self.frame = QtWidgets.QFrame(self.centralwidget)
21 | self.frame.setGeometry(QtCore.QRect(10, 10, 831, 501))
22 | self.frame.setStyleSheet("QFrame{\n"
23 | " border-radius: 7px;\n"
24 | " background-color: #1B1D23;\n"
25 | "}")
26 | self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
27 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
28 | self.frame.setObjectName("frame")
29 | self.lineEdit = QtWidgets.QLineEdit(self.frame)
30 | self.lineEdit.setGeometry(QtCore.QRect(290, 350, 481, 41))
31 | font = QtGui.QFont()
32 | font.setPointSize(11)
33 | font.setBold(True)
34 | font.setWeight(75)
35 | self.lineEdit.setFont(font)
36 | self.lineEdit.setStyleSheet("QLineEdit{\n"
37 | " border-radius: 7px;\n"
38 | "}")
39 | self.lineEdit.setText("")
40 | self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
41 | self.lineEdit.setPlaceholderText("")
42 | self.lineEdit.setClearButtonEnabled(True)
43 | self.lineEdit.setObjectName("lineEdit")
44 | self.pushButton = QtWidgets.QPushButton(self.frame)
45 | self.pushButton.setGeometry(QtCore.QRect(780, 350, 41, 41))
46 | font = QtGui.QFont()
47 | font.setPointSize(11)
48 | font.setBold(True)
49 | font.setWeight(75)
50 | self.pushButton.setFont(font)
51 | self.pushButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
52 | self.pushButton.setStyleSheet("QPushButton{\n"
53 | " color: white;\n"
54 | " border-radius: 7px;\n"
55 | " background-color: #595F76;\n"
56 | "}\n"
57 | "\n"
58 | "QPushButton:hover{\n"
59 | " background-color: #50566E;\n"
60 | "}\n"
61 | "\n"
62 | "QPushButton:pressed{\n"
63 | " background-color: #434965;\n"
64 | "}")
65 | self.pushButton.setText("")
66 | icon = QtGui.QIcon()
67 | icon.addPixmap(QtGui.QPixmap("icons/send.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
68 | self.pushButton.setIcon(icon)
69 | self.pushButton.setIconSize(QtCore.QSize(32, 32))
70 | self.pushButton.setObjectName("pushButton")
71 | self.pushButton_2 = QtWidgets.QPushButton(self.frame)
72 | self.pushButton_2.setGeometry(QtCore.QRect(290, 400, 261, 41))
73 | font = QtGui.QFont()
74 | font.setPointSize(11)
75 | font.setBold(True)
76 | font.setWeight(75)
77 | self.pushButton_2.setFont(font)
78 | self.pushButton_2.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
79 | self.pushButton_2.setStyleSheet("QPushButton{\n"
80 | " color: white;\n"
81 | " border-radius: 7px;\n"
82 | " background-color: #595F76;\n"
83 | "}\n"
84 | "\n"
85 | "QPushButton:hover{\n"
86 | " background-color: #50566E;\n"
87 | "}\n"
88 | "\n"
89 | "QPushButton:pressed{\n"
90 | " background-color: #434965;\n"
91 | "}")
92 | self.pushButton_2.setObjectName("pushButton_2")
93 | self.pushButton_4 = QtWidgets.QPushButton(self.frame)
94 | self.pushButton_4.setGeometry(QtCore.QRect(560, 450, 261, 41))
95 | font = QtGui.QFont()
96 | font.setPointSize(11)
97 | font.setBold(True)
98 | font.setWeight(75)
99 | self.pushButton_4.setFont(font)
100 | self.pushButton_4.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
101 | self.pushButton_4.setStyleSheet("QPushButton{\n"
102 | " color: white;\n"
103 | " border-radius: 7px;\n"
104 | " background-color: #595F76;\n"
105 | "}\n"
106 | "\n"
107 | "QPushButton:hover{\n"
108 | " background-color: #50566E;\n"
109 | "}\n"
110 | "\n"
111 | "QPushButton:pressed{\n"
112 | " background-color: #434965;\n"
113 | "}")
114 | self.pushButton_4.setObjectName("pushButton_4")
115 | self.frame_3 = QtWidgets.QFrame(self.frame)
116 | self.frame_3.setGeometry(QtCore.QRect(-1, 0, 831, 31))
117 | self.frame_3.setStyleSheet("QFrame{\n"
118 | " border-bottom-left-radius: 0px;\n"
119 | " border-bottom-right-radius: 0px;\n"
120 | " background-color: #2C313C;\n"
121 | "}")
122 | self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)
123 | self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)
124 | self.frame_3.setObjectName("frame_3")
125 | self.pushButton_3 = QtWidgets.QPushButton(self.frame_3)
126 | self.pushButton_3.setGeometry(QtCore.QRect(790, 0, 41, 31))
127 | font = QtGui.QFont()
128 | font.setPointSize(11)
129 | font.setBold(True)
130 | font.setWeight(75)
131 | self.pushButton_3.setFont(font)
132 | self.pushButton_3.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
133 | self.pushButton_3.setStyleSheet("QPushButton{\n"
134 | " color: white;\n"
135 | " border: none;\n"
136 | " border-top-right-radius: 7px;\n"
137 | " background-color: #2C313C;\n"
138 | "}\n"
139 | "\n"
140 | "QPushButton:hover{\n"
141 | " background-color: #45494D;\n"
142 | "}\n"
143 | "\n"
144 | "QPushButton:pressed{\n"
145 | " color: #EA2F4E;\n"
146 | "}")
147 | self.pushButton_3.setObjectName("pushButton_3")
148 | self.pushButton_5 = QtWidgets.QPushButton(self.frame_3)
149 | self.pushButton_5.setGeometry(QtCore.QRect(750, 0, 41, 31))
150 | font = QtGui.QFont()
151 | font.setPointSize(10)
152 | font.setBold(True)
153 | font.setWeight(75)
154 | self.pushButton_5.setFont(font)
155 | self.pushButton_5.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
156 | self.pushButton_5.setStyleSheet("QPushButton{\n"
157 | " color: white;\n"
158 | " border: none;\n"
159 | " border-top-right-radius: 7px;\n"
160 | " background-color: #2C313C;\n"
161 | "}\n"
162 | "\n"
163 | "QPushButton:hover{\n"
164 | " background-color: #45494D;\n"
165 | "}\n"
166 | "\n"
167 | "QPushButton:pressed{\n"
168 | " color: #EA2F4E;\n"
169 | "}")
170 | self.pushButton_5.setDefault(False)
171 | self.pushButton_5.setObjectName("pushButton_5")
172 | self.frame_4 = QtWidgets.QFrame(self.frame)
173 | self.frame_4.setGeometry(QtCore.QRect(10, 40, 271, 451))
174 | self.frame_4.setStyleSheet("QFrame{\n"
175 | " color: white;\n"
176 | " border-radius: 7px;\n"
177 | " background-color: #2C313C;\n"
178 | "}")
179 | self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)
180 | self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
181 | self.frame_4.setObjectName("frame_4")
182 | self.pushButton_6 = QtWidgets.QPushButton(self.frame_4)
183 | self.pushButton_6.setGeometry(QtCore.QRect(10, 130, 71, 71))
184 | self.pushButton_6.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
185 | self.pushButton_6.setStyleSheet("QPushButton{\n"
186 | " border: none;\n"
187 | "}")
188 | self.pushButton_6.setText("")
189 | icon1 = QtGui.QIcon()
190 | icon1.addPixmap(QtGui.QPixmap("icons/smile4.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
191 | self.pushButton_6.setIcon(icon1)
192 | self.pushButton_6.setIconSize(QtCore.QSize(64, 64))
193 | self.pushButton_6.setObjectName("pushButton_6")
194 | self.pushButton_8 = QtWidgets.QPushButton(self.frame_4)
195 | self.pushButton_8.setGeometry(QtCore.QRect(100, 130, 71, 71))
196 | self.pushButton_8.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
197 | self.pushButton_8.setStyleSheet("QPushButton{\n"
198 | " border: none;\n"
199 | "}")
200 | self.pushButton_8.setText("")
201 | icon2 = QtGui.QIcon()
202 | icon2.addPixmap(QtGui.QPixmap("icons/smile5.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
203 | self.pushButton_8.setIcon(icon2)
204 | self.pushButton_8.setIconSize(QtCore.QSize(64, 64))
205 | self.pushButton_8.setObjectName("pushButton_8")
206 | self.pushButton_9 = QtWidgets.QPushButton(self.frame_4)
207 | self.pushButton_9.setGeometry(QtCore.QRect(190, 130, 71, 71))
208 | self.pushButton_9.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
209 | self.pushButton_9.setStyleSheet("QPushButton{\n"
210 | " border: none;\n"
211 | "}")
212 | self.pushButton_9.setText("")
213 | icon3 = QtGui.QIcon()
214 | icon3.addPixmap(QtGui.QPixmap("icons/smile6.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
215 | self.pushButton_9.setIcon(icon3)
216 | self.pushButton_9.setIconSize(QtCore.QSize(64, 64))
217 | self.pushButton_9.setObjectName("pushButton_9")
218 | self.pushButton_10 = QtWidgets.QPushButton(self.frame_4)
219 | self.pushButton_10.setGeometry(QtCore.QRect(10, 50, 71, 71))
220 | self.pushButton_10.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
221 | self.pushButton_10.setStyleSheet("QPushButton{\n"
222 | " border: none;\n"
223 | "}")
224 | self.pushButton_10.setText("")
225 | icon4 = QtGui.QIcon()
226 | icon4.addPixmap(QtGui.QPixmap("icons/smile1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
227 | self.pushButton_10.setIcon(icon4)
228 | self.pushButton_10.setIconSize(QtCore.QSize(64, 64))
229 | self.pushButton_10.setObjectName("pushButton_10")
230 | self.pushButton_11 = QtWidgets.QPushButton(self.frame_4)
231 | self.pushButton_11.setGeometry(QtCore.QRect(100, 50, 71, 71))
232 | self.pushButton_11.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
233 | self.pushButton_11.setStyleSheet("QPushButton{\n"
234 | " border: none;\n"
235 | "}")
236 | self.pushButton_11.setText("")
237 | icon5 = QtGui.QIcon()
238 | icon5.addPixmap(QtGui.QPixmap("icons/smile2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
239 | self.pushButton_11.setIcon(icon5)
240 | self.pushButton_11.setIconSize(QtCore.QSize(64, 64))
241 | self.pushButton_11.setObjectName("pushButton_11")
242 | self.pushButton_12 = QtWidgets.QPushButton(self.frame_4)
243 | self.pushButton_12.setGeometry(QtCore.QRect(190, 50, 71, 71))
244 | self.pushButton_12.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
245 | self.pushButton_12.setStyleSheet("QPushButton{\n"
246 | " border: none;\n"
247 | "}")
248 | self.pushButton_12.setText("")
249 | icon6 = QtGui.QIcon()
250 | icon6.addPixmap(QtGui.QPixmap("icons/smile3.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
251 | self.pushButton_12.setIcon(icon6)
252 | self.pushButton_12.setIconSize(QtCore.QSize(64, 64))
253 | self.pushButton_12.setObjectName("pushButton_12")
254 | self.pushButton_13 = QtWidgets.QPushButton(self.frame_4)
255 | self.pushButton_13.setGeometry(QtCore.QRect(10, 210, 71, 71))
256 | self.pushButton_13.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
257 | self.pushButton_13.setStyleSheet("QPushButton{\n"
258 | " border: none;\n"
259 | "}")
260 | self.pushButton_13.setText("")
261 | icon7 = QtGui.QIcon()
262 | icon7.addPixmap(QtGui.QPixmap("icons/smile7.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
263 | self.pushButton_13.setIcon(icon7)
264 | self.pushButton_13.setIconSize(QtCore.QSize(64, 64))
265 | self.pushButton_13.setObjectName("pushButton_13")
266 | self.pushButton_14 = QtWidgets.QPushButton(self.frame_4)
267 | self.pushButton_14.setGeometry(QtCore.QRect(10, 290, 71, 71))
268 | self.pushButton_14.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
269 | self.pushButton_14.setStyleSheet("QPushButton{\n"
270 | " border: none;\n"
271 | "}")
272 | self.pushButton_14.setText("")
273 | icon8 = QtGui.QIcon()
274 | icon8.addPixmap(QtGui.QPixmap("icons/smile10.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
275 | self.pushButton_14.setIcon(icon8)
276 | self.pushButton_14.setIconSize(QtCore.QSize(64, 64))
277 | self.pushButton_14.setObjectName("pushButton_14")
278 | self.pushButton_15 = QtWidgets.QPushButton(self.frame_4)
279 | self.pushButton_15.setGeometry(QtCore.QRect(100, 290, 71, 71))
280 | self.pushButton_15.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
281 | self.pushButton_15.setStyleSheet("QPushButton{\n"
282 | " border: none;\n"
283 | "}")
284 | self.pushButton_15.setText("")
285 | icon9 = QtGui.QIcon()
286 | icon9.addPixmap(QtGui.QPixmap("icons/smile11.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
287 | self.pushButton_15.setIcon(icon9)
288 | self.pushButton_15.setIconSize(QtCore.QSize(64, 64))
289 | self.pushButton_15.setObjectName("pushButton_15")
290 | self.pushButton_16 = QtWidgets.QPushButton(self.frame_4)
291 | self.pushButton_16.setGeometry(QtCore.QRect(190, 210, 71, 71))
292 | self.pushButton_16.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
293 | self.pushButton_16.setStyleSheet("QPushButton{\n"
294 | " border: none;\n"
295 | "}")
296 | self.pushButton_16.setText("")
297 | icon10 = QtGui.QIcon()
298 | icon10.addPixmap(QtGui.QPixmap("icons/smile9.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
299 | self.pushButton_16.setIcon(icon10)
300 | self.pushButton_16.setIconSize(QtCore.QSize(64, 64))
301 | self.pushButton_16.setObjectName("pushButton_16")
302 | self.pushButton_17 = QtWidgets.QPushButton(self.frame_4)
303 | self.pushButton_17.setGeometry(QtCore.QRect(100, 210, 71, 71))
304 | self.pushButton_17.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
305 | self.pushButton_17.setStyleSheet("QPushButton{\n"
306 | " border: none;\n"
307 | "}")
308 | self.pushButton_17.setText("")
309 | icon11 = QtGui.QIcon()
310 | icon11.addPixmap(QtGui.QPixmap("icons/smile8.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
311 | self.pushButton_17.setIcon(icon11)
312 | self.pushButton_17.setIconSize(QtCore.QSize(64, 64))
313 | self.pushButton_17.setObjectName("pushButton_17")
314 | self.pushButton_18 = QtWidgets.QPushButton(self.frame_4)
315 | self.pushButton_18.setGeometry(QtCore.QRect(190, 290, 71, 71))
316 | self.pushButton_18.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
317 | self.pushButton_18.setStyleSheet("QPushButton{\n"
318 | " border: none;\n"
319 | "}")
320 | self.pushButton_18.setText("")
321 | icon12 = QtGui.QIcon()
322 | icon12.addPixmap(QtGui.QPixmap("icons/smile12.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
323 | self.pushButton_18.setIcon(icon12)
324 | self.pushButton_18.setIconSize(QtCore.QSize(64, 64))
325 | self.pushButton_18.setObjectName("pushButton_18")
326 | self.pushButton_21 = QtWidgets.QPushButton(self.frame_4)
327 | self.pushButton_21.setGeometry(QtCore.QRect(10, 370, 71, 71))
328 | self.pushButton_21.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
329 | self.pushButton_21.setStyleSheet("QPushButton{\n"
330 | " border: none;\n"
331 | "}")
332 | self.pushButton_21.setText("")
333 | icon13 = QtGui.QIcon()
334 | icon13.addPixmap(QtGui.QPixmap("icons/smile13.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
335 | self.pushButton_21.setIcon(icon13)
336 | self.pushButton_21.setIconSize(QtCore.QSize(64, 64))
337 | self.pushButton_21.setObjectName("pushButton_21")
338 | self.pushButton_22 = QtWidgets.QPushButton(self.frame_4)
339 | self.pushButton_22.setGeometry(QtCore.QRect(100, 370, 71, 71))
340 | self.pushButton_22.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
341 | self.pushButton_22.setStyleSheet("QPushButton{\n"
342 | " border: none;\n"
343 | "}")
344 | self.pushButton_22.setText("")
345 | icon14 = QtGui.QIcon()
346 | icon14.addPixmap(QtGui.QPixmap("icons/smile14.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
347 | self.pushButton_22.setIcon(icon14)
348 | self.pushButton_22.setIconSize(QtCore.QSize(64, 64))
349 | self.pushButton_22.setObjectName("pushButton_22")
350 | self.pushButton_24 = QtWidgets.QPushButton(self.frame_4)
351 | self.pushButton_24.setGeometry(QtCore.QRect(190, 370, 71, 71))
352 | self.pushButton_24.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
353 | self.pushButton_24.setStyleSheet("QPushButton{\n"
354 | " border: none;\n"
355 | "}")
356 | self.pushButton_24.setText("")
357 | icon15 = QtGui.QIcon()
358 | icon15.addPixmap(QtGui.QPixmap("icons/smile15.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
359 | self.pushButton_24.setIcon(icon15)
360 | self.pushButton_24.setIconSize(QtCore.QSize(64, 64))
361 | self.pushButton_24.setObjectName("pushButton_24")
362 | self.label = QtWidgets.QLabel(self.frame_4)
363 | self.label.setGeometry(QtCore.QRect(0, 0, 271, 41))
364 | font = QtGui.QFont()
365 | font.setPointSize(11)
366 | font.setBold(True)
367 | font.setWeight(75)
368 | self.label.setFont(font)
369 | self.label.setAlignment(QtCore.Qt.AlignCenter)
370 | self.label.setObjectName("label")
371 | self.pushButton_7 = QtWidgets.QPushButton(self.frame)
372 | self.pushButton_7.setGeometry(QtCore.QRect(290, 450, 261, 41))
373 | font = QtGui.QFont()
374 | font.setPointSize(11)
375 | font.setBold(True)
376 | font.setWeight(75)
377 | self.pushButton_7.setFont(font)
378 | self.pushButton_7.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
379 | self.pushButton_7.setStyleSheet("QPushButton{\n"
380 | " color: white;\n"
381 | " border-radius: 7px;\n"
382 | " background-color: #595F76;\n"
383 | "}\n"
384 | "\n"
385 | "QPushButton:hover{\n"
386 | " background-color: #50566E;\n"
387 | "}\n"
388 | "\n"
389 | "QPushButton:pressed{\n"
390 | " background-color: #434965;\n"
391 | "}")
392 | self.pushButton_7.setObjectName("pushButton_7")
393 | self.listWidget = QtWidgets.QListWidget(self.frame)
394 | self.listWidget.setGeometry(QtCore.QRect(290, 41, 531, 301))
395 | font = QtGui.QFont()
396 | font.setPointSize(11)
397 | font.setBold(True)
398 | font.setWeight(75)
399 | self.listWidget.setFont(font)
400 | self.listWidget.setTabletTracking(False)
401 | self.listWidget.setAutoFillBackground(False)
402 | self.listWidget.setStyleSheet("color: white;\n"
403 | "border-radius: 7px;\n"
404 | "background-color: #2C313C;\n"
405 | "")
406 | self.listWidget.setFrameShape(QtWidgets.QFrame.StyledPanel)
407 | self.listWidget.setFrameShadow(QtWidgets.QFrame.Sunken)
408 | self.listWidget.setLineWidth(1)
409 | self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
410 | self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
411 | self.listWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored)
412 | self.listWidget.setAutoScroll(True)
413 | self.listWidget.setTabKeyNavigation(False)
414 | self.listWidget.setProperty("showDropIndicator", True)
415 | self.listWidget.setDragDropOverwriteMode(False)
416 | self.listWidget.setAlternatingRowColors(False)
417 | self.listWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem)
418 | self.listWidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem)
419 | self.listWidget.setMovement(QtWidgets.QListView.Static)
420 | self.listWidget.setFlow(QtWidgets.QListView.TopToBottom)
421 | self.listWidget.setProperty("isWrapping", False)
422 | self.listWidget.setResizeMode(QtWidgets.QListView.Fixed)
423 | self.listWidget.setLayoutMode(QtWidgets.QListView.SinglePass)
424 | self.listWidget.setViewMode(QtWidgets.QListView.ListMode)
425 | self.listWidget.setUniformItemSizes(False)
426 | self.listWidget.setWordWrap(False)
427 | self.listWidget.setSelectionRectVisible(False)
428 | self.listWidget.setObjectName("listWidget")
429 | self.pushButton_19 = QtWidgets.QPushButton(self.frame)
430 | self.pushButton_19.setGeometry(QtCore.QRect(560, 400, 261, 41))
431 | font = QtGui.QFont()
432 | font.setPointSize(11)
433 | font.setBold(True)
434 | font.setWeight(75)
435 | self.pushButton_19.setFont(font)
436 | self.pushButton_19.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
437 | self.pushButton_19.setStyleSheet("QPushButton{\n"
438 | " color: white;\n"
439 | " border-radius: 7px;\n"
440 | " background-color: #595F76;\n"
441 | "}\n"
442 | "\n"
443 | "QPushButton:hover{\n"
444 | " background-color: #50566E;\n"
445 | "}\n"
446 | "\n"
447 | "QPushButton:pressed{\n"
448 | " background-color: #434965;\n"
449 | "}")
450 | self.pushButton_19.setObjectName("pushButton_19")
451 | MainWindow.setCentralWidget(self.centralwidget)
452 |
453 | self.retranslateUi(MainWindow)
454 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
455 |
456 | def retranslateUi(self, MainWindow):
457 | _translate = QtCore.QCoreApplication.translate
458 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
459 | self.pushButton_2.setText(_translate("MainWindow", "Подключиться"))
460 | self.pushButton_4.setText(_translate("MainWindow", "Очистить"))
461 | self.pushButton_3.setText(_translate("MainWindow", "X"))
462 | self.pushButton_5.setText(_translate("MainWindow", "_"))
463 | self.label.setText(_translate("MainWindow", "Emojis Icon Pack"))
464 | self.pushButton_7.setText(_translate("MainWindow", "Настройки"))
465 | self.listWidget.setSortingEnabled(False)
466 | self.pushButton_19.setText(_translate("MainWindow", "Выйти из чата"))
467 |
--------------------------------------------------------------------------------
/untitled.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 851
10 | 521
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 |
19 |
20 | 10
21 | 10
22 | 831
23 | 501
24 |
25 |
26 |
27 | QFrame{
28 | border-radius: 7px;
29 | background-color: #1B1D23;
30 | }
31 |
32 |
33 | QFrame::StyledPanel
34 |
35 |
36 | QFrame::Raised
37 |
38 |
39 |
40 |
41 | 290
42 | 350
43 | 481
44 | 41
45 |
46 |
47 |
48 |
49 | 11
50 | 75
51 | true
52 |
53 |
54 |
55 | QLineEdit{
56 | border-radius: 7px;
57 | }
58 |
59 |
60 |
61 |
62 |
63 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
64 |
65 |
66 |
67 |
68 |
69 | true
70 |
71 |
72 |
73 |
74 |
75 | 780
76 | 350
77 | 41
78 | 41
79 |
80 |
81 |
82 |
83 | 11
84 | 75
85 | true
86 |
87 |
88 |
89 | PointingHandCursor
90 |
91 |
92 | QPushButton{
93 | color: white;
94 | border-radius: 7px;
95 | background-color: #595F76;
96 | }
97 |
98 | QPushButton:hover{
99 | background-color: #50566E;
100 | }
101 |
102 | QPushButton:pressed{
103 | background-color: #434965;
104 | }
105 |
106 |
107 |
108 |
109 |
110 |
111 | icons/send.pngicons/send.png
112 |
113 |
114 |
115 | 32
116 | 32
117 |
118 |
119 |
120 |
121 |
122 |
123 | 290
124 | 400
125 | 261
126 | 41
127 |
128 |
129 |
130 |
131 | 11
132 | 75
133 | true
134 |
135 |
136 |
137 | PointingHandCursor
138 |
139 |
140 | QPushButton{
141 | color: white;
142 | border-radius: 7px;
143 | background-color: #595F76;
144 | }
145 |
146 | QPushButton:hover{
147 | background-color: #50566E;
148 | }
149 |
150 | QPushButton:pressed{
151 | background-color: #434965;
152 | }
153 |
154 |
155 | Подключиться
156 |
157 |
158 |
159 |
160 |
161 | 560
162 | 450
163 | 261
164 | 41
165 |
166 |
167 |
168 |
169 | 11
170 | 75
171 | true
172 |
173 |
174 |
175 | PointingHandCursor
176 |
177 |
178 | QPushButton{
179 | color: white;
180 | border-radius: 7px;
181 | background-color: #595F76;
182 | }
183 |
184 | QPushButton:hover{
185 | background-color: #50566E;
186 | }
187 |
188 | QPushButton:pressed{
189 | background-color: #434965;
190 | }
191 |
192 |
193 | Очистить
194 |
195 |
196 |
197 |
198 |
199 | -1
200 | 0
201 | 831
202 | 31
203 |
204 |
205 |
206 | QFrame{
207 | border-bottom-left-radius: 0px;
208 | border-bottom-right-radius: 0px;
209 | background-color: #2C313C;
210 | }
211 |
212 |
213 | QFrame::StyledPanel
214 |
215 |
216 | QFrame::Raised
217 |
218 |
219 |
220 |
221 | 790
222 | 0
223 | 41
224 | 31
225 |
226 |
227 |
228 |
229 | 11
230 | 75
231 | true
232 |
233 |
234 |
235 | PointingHandCursor
236 |
237 |
238 | QPushButton{
239 | color: white;
240 | border: none;
241 | border-top-right-radius: 7px;
242 | background-color: #2C313C;
243 | }
244 |
245 | QPushButton:hover{
246 | background-color: #45494D;
247 | }
248 |
249 | QPushButton:pressed{
250 | color: #EA2F4E;
251 | }
252 |
253 |
254 | X
255 |
256 |
257 |
258 |
259 |
260 | 750
261 | 0
262 | 41
263 | 31
264 |
265 |
266 |
267 |
268 | 10
269 | 75
270 | true
271 |
272 |
273 |
274 | PointingHandCursor
275 |
276 |
277 | QPushButton{
278 | color: white;
279 | border: none;
280 | border-top-right-radius: 7px;
281 | background-color: #2C313C;
282 | }
283 |
284 | QPushButton:hover{
285 | background-color: #45494D;
286 | }
287 |
288 | QPushButton:pressed{
289 | color: #EA2F4E;
290 | }
291 |
292 |
293 | _
294 |
295 |
296 | false
297 |
298 |
299 |
300 |
301 |
302 |
303 | 10
304 | 40
305 | 271
306 | 451
307 |
308 |
309 |
310 | QFrame{
311 | color: white;
312 | border-radius: 7px;
313 | background-color: #2C313C;
314 | }
315 |
316 |
317 | QFrame::StyledPanel
318 |
319 |
320 | QFrame::Raised
321 |
322 |
323 |
324 |
325 | 10
326 | 130
327 | 71
328 | 71
329 |
330 |
331 |
332 | PointingHandCursor
333 |
334 |
335 | QPushButton{
336 | border: none;
337 | }
338 |
339 |
340 |
341 |
342 |
343 |
344 | icons/smile4.pngicons/smile4.png
345 |
346 |
347 |
348 | 64
349 | 64
350 |
351 |
352 |
353 |
354 |
355 |
356 | 100
357 | 130
358 | 71
359 | 71
360 |
361 |
362 |
363 | PointingHandCursor
364 |
365 |
366 | QPushButton{
367 | border: none;
368 | }
369 |
370 |
371 |
372 |
373 |
374 |
375 | icons/smile5.pngicons/smile5.png
376 |
377 |
378 |
379 | 64
380 | 64
381 |
382 |
383 |
384 |
385 |
386 |
387 | 190
388 | 130
389 | 71
390 | 71
391 |
392 |
393 |
394 | PointingHandCursor
395 |
396 |
397 | QPushButton{
398 | border: none;
399 | }
400 |
401 |
402 |
403 |
404 |
405 |
406 | icons/smile6.pngicons/smile6.png
407 |
408 |
409 |
410 | 64
411 | 64
412 |
413 |
414 |
415 |
416 |
417 |
418 | 10
419 | 50
420 | 71
421 | 71
422 |
423 |
424 |
425 | PointingHandCursor
426 |
427 |
428 | QPushButton{
429 | border: none;
430 | }
431 |
432 |
433 |
434 |
435 |
436 |
437 | icons/smile1.pngicons/smile1.png
438 |
439 |
440 |
441 | 64
442 | 64
443 |
444 |
445 |
446 |
447 |
448 |
449 | 100
450 | 50
451 | 71
452 | 71
453 |
454 |
455 |
456 | PointingHandCursor
457 |
458 |
459 | QPushButton{
460 | border: none;
461 | }
462 |
463 |
464 |
465 |
466 |
467 |
468 | icons/smile2.pngicons/smile2.png
469 |
470 |
471 |
472 | 64
473 | 64
474 |
475 |
476 |
477 |
478 |
479 |
480 | 190
481 | 50
482 | 71
483 | 71
484 |
485 |
486 |
487 | PointingHandCursor
488 |
489 |
490 | QPushButton{
491 | border: none;
492 | }
493 |
494 |
495 |
496 |
497 |
498 |
499 | icons/smile3.pngicons/smile3.png
500 |
501 |
502 |
503 | 64
504 | 64
505 |
506 |
507 |
508 |
509 |
510 |
511 | 10
512 | 210
513 | 71
514 | 71
515 |
516 |
517 |
518 | PointingHandCursor
519 |
520 |
521 | QPushButton{
522 | border: none;
523 | }
524 |
525 |
526 |
527 |
528 |
529 |
530 | icons/smile7.pngicons/smile7.png
531 |
532 |
533 |
534 | 64
535 | 64
536 |
537 |
538 |
539 |
540 |
541 |
542 | 10
543 | 290
544 | 71
545 | 71
546 |
547 |
548 |
549 | PointingHandCursor
550 |
551 |
552 | QPushButton{
553 | border: none;
554 | }
555 |
556 |
557 |
558 |
559 |
560 |
561 | icons/smile10.pngicons/smile10.png
562 |
563 |
564 |
565 | 64
566 | 64
567 |
568 |
569 |
570 |
571 |
572 |
573 | 100
574 | 290
575 | 71
576 | 71
577 |
578 |
579 |
580 | PointingHandCursor
581 |
582 |
583 | QPushButton{
584 | border: none;
585 | }
586 |
587 |
588 |
589 |
590 |
591 |
592 | icons/smile11.pngicons/smile11.png
593 |
594 |
595 |
596 | 64
597 | 64
598 |
599 |
600 |
601 |
602 |
603 |
604 | 190
605 | 210
606 | 71
607 | 71
608 |
609 |
610 |
611 | PointingHandCursor
612 |
613 |
614 | QPushButton{
615 | border: none;
616 | }
617 |
618 |
619 |
620 |
621 |
622 |
623 | icons/smile9.pngicons/smile9.png
624 |
625 |
626 |
627 | 64
628 | 64
629 |
630 |
631 |
632 |
633 |
634 |
635 | 100
636 | 210
637 | 71
638 | 71
639 |
640 |
641 |
642 | PointingHandCursor
643 |
644 |
645 | QPushButton{
646 | border: none;
647 | }
648 |
649 |
650 |
651 |
652 |
653 |
654 | icons/smile8.pngicons/smile8.png
655 |
656 |
657 |
658 | 64
659 | 64
660 |
661 |
662 |
663 |
664 |
665 |
666 | 190
667 | 290
668 | 71
669 | 71
670 |
671 |
672 |
673 | PointingHandCursor
674 |
675 |
676 | QPushButton{
677 | border: none;
678 | }
679 |
680 |
681 |
682 |
683 |
684 |
685 | icons/smile12.pngicons/smile12.png
686 |
687 |
688 |
689 | 64
690 | 64
691 |
692 |
693 |
694 |
695 |
696 |
697 | 10
698 | 370
699 | 71
700 | 71
701 |
702 |
703 |
704 | PointingHandCursor
705 |
706 |
707 | QPushButton{
708 | border: none;
709 | }
710 |
711 |
712 |
713 |
714 |
715 |
716 | icons/smile13.pngicons/smile13.png
717 |
718 |
719 |
720 | 64
721 | 64
722 |
723 |
724 |
725 |
726 |
727 |
728 | 100
729 | 370
730 | 71
731 | 71
732 |
733 |
734 |
735 | PointingHandCursor
736 |
737 |
738 | QPushButton{
739 | border: none;
740 | }
741 |
742 |
743 |
744 |
745 |
746 |
747 | icons/smile14.pngicons/smile14.png
748 |
749 |
750 |
751 | 64
752 | 64
753 |
754 |
755 |
756 |
757 |
758 |
759 | 190
760 | 370
761 | 71
762 | 71
763 |
764 |
765 |
766 | PointingHandCursor
767 |
768 |
769 | QPushButton{
770 | border: none;
771 | }
772 |
773 |
774 |
775 |
776 |
777 |
778 | icons/smile15.pngicons/smile15.png
779 |
780 |
781 |
782 | 64
783 | 64
784 |
785 |
786 |
787 |
788 |
789 |
790 | 0
791 | 0
792 | 271
793 | 41
794 |
795 |
796 |
797 |
798 | 11
799 | 75
800 | true
801 |
802 |
803 |
804 | Emojis Icon Pack
805 |
806 |
807 | Qt::AlignCenter
808 |
809 |
810 |
811 |
812 |
813 |
814 | 290
815 | 450
816 | 261
817 | 41
818 |
819 |
820 |
821 |
822 | 11
823 | 75
824 | true
825 |
826 |
827 |
828 | PointingHandCursor
829 |
830 |
831 | QPushButton{
832 | color: white;
833 | border-radius: 7px;
834 | background-color: #595F76;
835 | }
836 |
837 | QPushButton:hover{
838 | background-color: #50566E;
839 | }
840 |
841 | QPushButton:pressed{
842 | background-color: #434965;
843 | }
844 |
845 |
846 | Настройки
847 |
848 |
849 |
850 |
851 |
852 | 290
853 | 41
854 | 531
855 | 301
856 |
857 |
858 |
859 |
860 | 11
861 | 75
862 | true
863 |
864 |
865 |
866 | false
867 |
868 |
869 | false
870 |
871 |
872 | color: white;
873 | border-radius: 7px;
874 | background-color: #2C313C;
875 |
876 |
877 |
878 | QFrame::StyledPanel
879 |
880 |
881 | QFrame::Sunken
882 |
883 |
884 | 1
885 |
886 |
887 | Qt::ScrollBarAsNeeded
888 |
889 |
890 | Qt::ScrollBarAsNeeded
891 |
892 |
893 | QAbstractScrollArea::AdjustIgnored
894 |
895 |
896 | true
897 |
898 |
899 | false
900 |
901 |
902 | true
903 |
904 |
905 | false
906 |
907 |
908 | false
909 |
910 |
911 | QAbstractItemView::ScrollPerItem
912 |
913 |
914 | QAbstractItemView::ScrollPerItem
915 |
916 |
917 | QListView::Static
918 |
919 |
920 | QListView::TopToBottom
921 |
922 |
923 | false
924 |
925 |
926 | QListView::Fixed
927 |
928 |
929 | QListView::SinglePass
930 |
931 |
932 | QListView::ListMode
933 |
934 |
935 | false
936 |
937 |
938 | false
939 |
940 |
941 | false
942 |
943 |
944 | false
945 |
946 |
947 |
948 |
949 |
950 | 560
951 | 400
952 | 261
953 | 41
954 |
955 |
956 |
957 |
958 | 11
959 | 75
960 | true
961 |
962 |
963 |
964 | PointingHandCursor
965 |
966 |
967 | QPushButton{
968 | color: white;
969 | border-radius: 7px;
970 | background-color: #595F76;
971 | }
972 |
973 | QPushButton:hover{
974 | background-color: #50566E;
975 | }
976 |
977 | QPushButton:pressed{
978 | background-color: #434965;
979 | }
980 |
981 |
982 | Выйти из чата
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
--------------------------------------------------------------------------------