-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from Schrodinger71/dev
Базовый клиент на GUI
- Loading branch information
Showing
68 changed files
with
1,054 additions
and
1,212 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm | ||
__pypackages__/ | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# VS and VSC | ||
.vs/* | ||
.vscode/* | ||
|
||
# Data | ||
[Dd]ata/* | ||
Content/* | ||
|
||
# Texture | ||
Sprites/ | ||
|
||
# PyQT6 | ||
*_ui.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import sys | ||
|
||
from gui import LoginWindow | ||
from PyQt6.QtWidgets import QApplication | ||
|
||
if __name__ == "__main__": | ||
app = QApplication(sys.argv) | ||
window: LoginWindow = LoginWindow.get_instance() | ||
window.show() | ||
sys.exit(app.exec()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from gui.login_window import LoginWindow |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
import os | ||
|
||
from gui.main_window import MainApplicationWindow | ||
from PyQt6.QtGui import QPixmap | ||
from PyQt6.QtWidgets import (QDialog, QLabel, QLineEdit, QMainWindow, | ||
QMessageBox, QProgressBar, QPushButton) | ||
from PyQt6.uic import loadUi | ||
from root_path import ROOT_PATH | ||
from systems.decorators import global_class | ||
from systems.network import ClientUnit | ||
from PyQt6.QtCore import QThread, pyqtSignal | ||
|
||
@global_class | ||
class LoginWindow(QMainWindow): | ||
def __init__(self): | ||
super().__init__() | ||
loadUi(os.path.join(ROOT_PATH, 'GUI', 'windows', 'LoginWindow.ui'), self) | ||
|
||
# Привязка виджетов | ||
self.ip_line = self.findChild(QLineEdit, 'IPLine') | ||
self.port_line = self.findChild(QLineEdit, 'PortLine') | ||
self.server_check_button = self.findChild(QPushButton, 'ServerCheckButton') | ||
self.server_check_label = self.findChild(QLabel, 'ServerCheckLabel') | ||
|
||
self.login_button = self.findChild(QPushButton, 'LoginButton') | ||
self.register_button = self.findChild(QPushButton, 'RegisterButton') | ||
|
||
self.set_server_check_label("failure") | ||
|
||
# Привязка функций к кнопкам | ||
self.server_check_button.clicked.connect(self.check_server) | ||
self.login_button.clicked.connect(self.open_auth_window) | ||
self.register_button.clicked.connect(self.open_registration_window) | ||
|
||
# Начальное состояние кнопок | ||
self.login_button.setEnabled(False) | ||
self.register_button.setEnabled(False) | ||
|
||
def set_server_check_label(self, state: str): | ||
self.server_check_label.setPixmap(QPixmap(os.path.join(ROOT_PATH, 'GUI', 'images', 'status', f'{state}_64_64.png'))) | ||
|
||
def check_server(self): | ||
ip = self.ip_line.text() | ||
port = self.port_line.text() | ||
|
||
if not ip or not port: | ||
QMessageBox.warning(self, "Ошибка", "Все поля должны быть заполнены!") | ||
self.set_server_check_label("failure") | ||
self.set_buttons(False) | ||
return | ||
|
||
if not self.is_valid_ip(ip): | ||
QMessageBox.warning(self, "Ошибка", "IP неверно указан. Такого IP не может существовать.") | ||
self.set_server_check_label("failure") | ||
self.set_buttons(False) | ||
return | ||
|
||
if not self.is_valid_port(port): | ||
QMessageBox.warning(self, "Ошибка", "Порт неверно указан. Такого порта не может существовать.") | ||
self.set_server_check_label("failure") | ||
self.set_buttons(False) | ||
return | ||
|
||
cl_unit: ClientUnit = ClientUnit.get_instance() | ||
try: | ||
cl_unit.check_server(ip, int(port)) | ||
self.set_server_check_label("success") | ||
self.set_buttons(True) | ||
|
||
except Exception as e: | ||
QMessageBox.warning(self, "Ошибка", f"Невозможно подключиться к серверу: {str(e)}") | ||
self.set_server_check_label("failure") | ||
self.set_buttons(False) | ||
return | ||
|
||
def open_auth_window(self): | ||
auth_window = AuthDialog(self) | ||
auth_window.exec() | ||
|
||
def open_registration_window(self): | ||
registration_window = RegistrationDialog(self) | ||
registration_window.exec() | ||
|
||
def set_buttons(self, value: bool): | ||
self.login_button.setEnabled(value) | ||
self.register_button.setEnabled(value) | ||
|
||
def is_valid_ip(self, ip: str) -> bool: | ||
parts = ip.split('.') | ||
if len(parts) != 4: | ||
return False | ||
|
||
for part in parts: | ||
if not part.isdigit(): | ||
return False | ||
|
||
if not 0 <= int(part) <= 255: | ||
return False | ||
|
||
return True | ||
|
||
def is_valid_port(self, port: str) -> bool: | ||
if not port.isdigit(): | ||
return False | ||
|
||
port_num = int(port) | ||
return 1 <= port_num <= 65535 | ||
|
||
def run_download_server_content(self) -> None: | ||
download_dialog = ServerContentDownloadDialog(self) | ||
download_dialog.exec() | ||
|
||
def run_main_app(self) -> None: | ||
self.close() | ||
self.main_window: MainApplicationWindow = MainApplicationWindow.get_instance() | ||
self.main_window.show() | ||
|
||
class AuthDialog(QDialog): | ||
def __init__(self, parent=None): | ||
super().__init__(parent) | ||
loadUi(os.path.join(ROOT_PATH, 'GUI', 'windows', 'AuthDialog.ui'), self) | ||
|
||
self.username_line = self.findChild(QLineEdit, 'UsernameLine') | ||
self.password_line = self.findChild(QLineEdit, 'PasswordLine') | ||
self.login_button = self.findChild(QPushButton, 'LoginButton') | ||
|
||
self.login_button.clicked.connect(self.authenticate) | ||
|
||
def authenticate(self): | ||
username = self.username_line.text() | ||
password = self.password_line.text() | ||
|
||
if not username or not password: | ||
QMessageBox.warning(self, "Ошибка", "Все поля должны быть заполнены!") | ||
return | ||
|
||
cl_unit: ClientUnit = ClientUnit.get_instance() | ||
try: | ||
cl_unit.login(username, password) | ||
self.close_current_and_open_main_window() | ||
|
||
except Exception as e: | ||
QMessageBox.warning(self, "Ошибка", f"Невозможно выполнить аутентификацию: {str(e)}") | ||
|
||
def close_current_and_open_main_window(self): | ||
self.close() | ||
self.parent().run_download_server_content() | ||
|
||
class RegistrationDialog(QDialog): | ||
def __init__(self, parent=None): | ||
super().__init__(parent) | ||
loadUi(os.path.join(ROOT_PATH, 'GUI', 'windows', 'RegistrationDialog.ui'), self) | ||
|
||
self.username_line = self.findChild(QLineEdit, 'UsernameLine') | ||
self.password_line = self.findChild(QLineEdit, 'PasswordLine') | ||
self.password_line_2 = self.findChild(QLineEdit, 'PasswordLine_2') | ||
self.register_button = self.findChild(QPushButton, 'RegisterButton') | ||
|
||
self.register_button.clicked.connect(self.register) | ||
|
||
def register(self): | ||
username = self.username_line.text() | ||
password = self.password_line.text() | ||
password_2 = self.password_line_2.text() | ||
|
||
if not username or not password or not password_2: | ||
QMessageBox.warning(self, "Ошибка", "Все поля должны быть заполнены!") | ||
return | ||
|
||
if password != password_2: | ||
QMessageBox.warning(self, "Ошибка", "Пароли должны совпадать!") | ||
return | ||
|
||
cl_unit: ClientUnit = ClientUnit.get_instance() | ||
try: | ||
cl_unit.register(username, password) | ||
self.close_current_and_open_main_window() | ||
except Exception as err: | ||
QMessageBox.warning(self, "Ошибка", f"Логин который вы указали уже занят. Подробная ошибка: {err}") | ||
|
||
def close_current_and_open_main_window(self): | ||
self.close() | ||
self.parent().run_download_server_content() | ||
|
||
class DownloadThread(QThread): | ||
progress_updated = pyqtSignal(int, int) | ||
|
||
def __init__(self): | ||
super().__init__() | ||
|
||
def run(self): | ||
cl_unit: ClientUnit = ClientUnit.get_instance() | ||
cl_unit.download_server_content(self.report_progress) | ||
|
||
def report_progress(self, downloaded_size, total_size): | ||
self.progress_updated.emit(downloaded_size, total_size) | ||
|
||
class ServerContentDownloadDialog(QDialog): | ||
def __init__(self, parent=None): | ||
super().__init__(parent) | ||
loadUi(os.path.join(ROOT_PATH, 'GUI', 'windows', 'ServerContentDownloadDialog.ui'), self) | ||
|
||
self.progress_bar = self.findChild(QProgressBar, 'progressBar') | ||
|
||
self.download_thread = DownloadThread() | ||
self.download_thread.progress_updated.connect(self.update_progress) | ||
self.download_thread.start() | ||
|
||
def update_progress(self, downloaded_size, total_size): | ||
self.progress_bar.setMaximum(total_size) | ||
self.progress_bar.setValue(downloaded_size) | ||
|
||
if downloaded_size >= total_size: | ||
self.download_thread.quit() | ||
self.download_thread.wait() | ||
self.close() | ||
self.parent().run_main_app() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import os | ||
|
||
from PyQt6.QtGui import QPixmap | ||
from PyQt6.QtWidgets import (QDialog, QLabel, QLineEdit, QMainWindow, | ||
QMessageBox, QPushButton) | ||
from PyQt6.uic import loadUi | ||
from root_path import ROOT_PATH | ||
from systems.decorators import global_class | ||
from systems.network import ClientUnit | ||
|
||
|
||
@global_class | ||
class MainApplicationWindow(QMainWindow): | ||
def __init__(self): | ||
super().__init__() | ||
loadUi(os.path.join(ROOT_PATH, 'GUI', 'windows', 'MainWindow.ui'), self) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import os | ||
|
||
ROOT_PATH = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir)) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from systems.bin_system.bin_system import BinaryFileSystem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import atexit | ||
import os | ||
import pickle | ||
from typing import Any, Dict, Optional | ||
|
||
from root_path import ROOT_PATH | ||
|
||
|
||
class BinaryFileSystem: | ||
__slots__ = ['_data', '_file_path'] | ||
|
||
def __init__(self, file_path: Optional[str], file_name: str) -> None: | ||
"""Инициализирует объект BinaryFileSystem. | ||
На текущий момент данный класс используется как сохранение настроек | ||
Args: | ||
file_path (Optional[str]): Путь до файла с данными | ||
file_name (str): Имя файла | ||
""" | ||
if not file_path: | ||
file_path = "" | ||
|
||
full_file_path = os.path.join(ROOT_PATH, "data", full_file_path) | ||
|
||
if not os.path.exists(full_file_path): | ||
os.makedirs(full_file_path) | ||
|
||
self._file_path = os.path.join(full_file_path, f"{file_name}.bin") | ||
|
||
self._data = {} | ||
try: | ||
self._read_file() | ||
|
||
except Exception: | ||
self._write_file() | ||
|
||
atexit.register(self._write_file) | ||
|
||
def _write_file(self) -> None: | ||
"""Записывает данные в файл. | ||
""" | ||
with open(self._file_path, 'wb') as file: | ||
pickle.dump(self._data, file) | ||
|
||
def _read_file(self) -> Dict[str, Any]: | ||
"""Читает данные из файла | ||
Returns: | ||
Dict[str, Any]: Словарь данных | ||
""" | ||
with open(self._file_path, 'rb') as file: | ||
self._data = pickle.load(file) | ||
|
||
def __getitem__(self, key) -> Any: | ||
"""Возвращает значение по ключу. | ||
Args: | ||
key (str): Ключ для доступа к значению. | ||
Returns: | ||
Any: Значение, соответствующее ключу. | ||
""" | ||
return self._data.get(key, None) | ||
|
||
def __setitem__(self, key: str, value: Any) -> None: | ||
"""Устанавливает значение по ключу и сохраняет изменения в файл, если значение отличается от текущего. | ||
Args: | ||
key (str): Ключ для установки значения. | ||
value (Any): Устанавливаемое значение. | ||
""" | ||
if key in self._data and self._data.get(key, None) == value: | ||
return | ||
|
||
self._data[key] = value | ||
self._write_file() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
def global_class(cls): | ||
"""Декоратор для обеспечения того, что класс следует паттерну Singleton, то есть создается и используется только один экземпляр класса во всем приложении. | ||
Args: | ||
cls (type): Класс, к которому применяется декоратор. | ||
Returns: | ||
type: Класс с добавленным методом для получения единственного экземпляра. | ||
""" | ||
instances = {} | ||
|
||
def get_instance(*args, **kwargs): | ||
"""Возвращает единственный экземпляр класса, создавая его, если он еще не существует. | ||
Args: | ||
*args: Позиционные аргументы для передачи в конструктор класса. | ||
**kwargs: Именованные аргументы для передачи в конструктор класса. | ||
Returns: | ||
object: Единственный экземпляр класса. | ||
""" | ||
if cls not in instances: | ||
instances[cls] = cls(*args, **kwargs) | ||
|
||
return instances[cls] | ||
|
||
cls.get_instance = get_instance | ||
return cls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from systems.events_system.event_manager import EventManager | ||
from systems.events_system.register import register_ev | ||
|
||
__all__ = ['EventManager', 'register_ev'] |
Oops, something went wrong.