init: начало работы над приложением с использованием wxPython

This commit is contained in:
2026-08-05 02:25:56 +03:00
commit 58aaf93392
12 changed files with 495 additions and 0 deletions
View File
+4
View File
@@ -0,0 +1,4 @@
from mind_reader.app import main
if __name__ == "__main__":
main()
+39
View File
@@ -0,0 +1,39 @@
import sys
import wx
from mind_reader.ui.main_frame import MainFrame
def enable_high_dpi_support():
if sys.platform == "win32":
try:
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(2)
except Exception:
try:
import ctypes
ctypes.windll.user32.SetProcessDPIAware()
except Exception:
pass
class MindReaderApp(wx.App):
def OnInit(self) -> bool:
self.frame = MainFrame()
self.frame.Show(True)
self.SetTopWindow(self.frame)
return True
def main():
enable_high_dpi_support()
app = MindReaderApp(redirect=False)
app.MainLoop()
if __name__ == "__main__":
main()
View File
+7
View File
@@ -0,0 +1,7 @@
from mind_reader.ui.dialogs.client_dialog import ClientConnectDialog
from mind_reader.ui.dialogs.server_dialog import ServerStartDialog
__all__ = [
"ClientConnectDialog",
"ServerStartDialog",
]
@@ -0,0 +1,84 @@
import wx
class ClientConnectDialog(wx.Dialog):
def __init__(self, parent: wx.Window):
super().__init__(
parent=parent,
title="Подключение к серверу",
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
)
self._init_ui()
self.Fit()
calculated_size = self.GetSize()
self.SetMinSize(calculated_size)
self.CenterOnParent()
def _init_ui(self):
main_sizer = wx.BoxSizer(wx.VERTICAL)
grid_sizer = wx.FlexGridSizer(rows=2, cols=2, vgap=10, hgap=10)
grid_sizer.AddGrowableCol(1, 1)
label_name = wx.StaticText(self, label="Ваше имя:")
self.text_name = wx.TextCtrl(self, value="Игрок1")
grid_sizer.Add(label_name, 0, wx.ALIGN_CENTER_VERTICAL)
grid_sizer.Add(self.text_name, 1, wx.EXPAND)
label_ip = wx.StaticText(self, label="IP адрес сервера:")
self.text_ip = wx.TextCtrl(self, value="127.0.0.1")
grid_sizer.Add(label_ip, 0, wx.ALIGN_CENTER_VERTICAL)
grid_sizer.Add(self.text_ip, 1, wx.EXPAND)
main_sizer.Add(grid_sizer, 0, wx.EXPAND | wx.ALL, 15)
main_sizer.Add(
wx.StaticLine(self), 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10
)
button_sizer = wx.StdDialogButtonSizer()
self.button_connect = wx.Button(self, wx.ID_OK, label="Подключиться")
self.button_cancel = wx.Button(self, wx.ID_CANCEL, label="Отмена")
button_sizer.AddButton(self.button_connect)
button_sizer.AddButton(self.button_cancel)
button_sizer.Realize()
main_sizer.Add(button_sizer, 0, wx.EXPAND | wx.ALL, 10)
self.SetSizer(main_sizer)
self.button_connect.Bind(wx.EVT_BUTTON, self._on_connect)
def _on_connect(self, event):
name = self.text_name.GetValue().strip()
ip = self.text_ip.GetValue().strip()
if not name:
wx.MessageBox(
"Пожалуйста, введите ваше имя",
"Ошибка",
wx.OK | wx.ICON_WARNING,
self,
)
return
if not ip:
wx.MessageBox(
"Пожалуйста, введите IP адрес сервера",
"Ошибка",
wx.OK | wx.ICON_WARNING,
self,
)
return
event.Skip()
def get_data(self) -> tuple[str, str]:
return (
self.text_name.GetValue().strip(),
self.text_ip.GetValue().strip(),
)
@@ -0,0 +1,99 @@
import socket
import wx
def get_available_ip_addresses() -> list[str]:
ip_list = [
"127.0.0.1",
]
try:
hostname = socket.gethostname()
for ip in socket.gethostbyname_ex(hostname)[2]:
if ip not in ip_list:
ip_list.append(ip)
except Exception:
pass
return ip_list
class ServerStartDialog(wx.Dialog):
def __init__(self, parent: wx.Window):
super().__init__(
parent=parent,
title="Запуск сервера",
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
)
self._init_ui()
self.Fit()
calculated_size = self.GetSize()
self.SetMinSize(calculated_size)
self.CenterOnParent()
def _init_ui(self):
main_sizer = wx.BoxSizer(wx.VERTICAL)
grid_sizer = wx.FlexGridSizer(rows=2, cols=2, vgap=10, hgap=10)
grid_sizer.AddGrowableCol(1, 1)
label_name = wx.StaticText(self, label="Ваше имя:")
self.text_name = wx.TextCtrl(self, value="Игрок2")
grid_sizer.Add(label_name, 0, wx.ALIGN_CENTER_VERTICAL)
grid_sizer.Add(self.text_name, 1, wx.EXPAND)
label_ip = wx.StaticText(self, label="IP адрес для запуска:")
available_ips = get_available_ip_addresses()
self.combo_ip = wx.ComboBox(
self,
choices=available_ips,
style=wx.CB_READONLY,
)
if available_ips:
self.combo_ip.SetSelection(0)
grid_sizer.Add(label_ip, 0, wx.ALIGN_CENTER_VERTICAL)
grid_sizer.Add(self.combo_ip, 1, wx.EXPAND)
main_sizer.Add(grid_sizer, 0, wx.EXPAND | wx.ALL, 15)
main_sizer.Add(
wx.StaticLine(self), 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10
)
button_sizer = wx.StdDialogButtonSizer()
self.button_start = wx.Button(
self, wx.ID_OK, label="Ожидать подключение"
)
self.button_cancel = wx.Button(self, wx.ID_CANCEL, label="Отмена")
button_sizer.AddButton(self.button_start)
button_sizer.AddButton(self.button_cancel)
button_sizer.Realize()
main_sizer.Add(button_sizer, 0, wx.EXPAND | wx.ALL, 10)
self.SetSizer(main_sizer)
self.button_start.Bind(wx.EVT_BUTTON, self._on_start)
def _on_start(self, event):
name = self.text_name.GetValue().strip()
if not name:
wx.MessageBox(
"Пожалуйста, введите ваше имя",
"Ошибка",
wx.OK | wx.ICON_WARNING,
self,
)
return
event.Skip()
def get_data(self) -> tuple[str, str]:
return (
self.text_name.GetValue().strip(),
self.combo_ip.GetValue(),
)
+53
View File
@@ -0,0 +1,53 @@
import wx
from mind_reader.ui.dialogs import ClientConnectDialog, ServerStartDialog
from mind_reader.ui.menu_bar import AppMenuBar
class MainFrame(wx.Frame):
def __init__(self):
super().__init__(
parent=None,
id=wx.ID_ANY,
title="Mind Reader",
size=wx.Size(700, 500),
style=wx.DEFAULT_FRAME_STYLE,
)
self.Center()
self.menu_bar = AppMenuBar(self)
self.SetMenuBar(self.menu_bar)
self.CreateStatusBar(1)
self.SetStatusText("Подключение отсутствует")
self._bind_menu_events()
def _bind_menu_events(self):
self.Bind(
wx.EVT_MENU, self.on_client_connect, self.menu_bar.item_client
)
self.Bind(wx.EVT_MENU, self.on_server_start, self.menu_bar.item_server)
self.Bind(
wx.EVT_MENU, self.on_disconnect, self.menu_bar.item_disconnect
)
def on_client_connect(self, event):
d = ClientConnectDialog(self)
if d.ShowModal() == wx.ID_OK:
name, ip = d.get_data()
self.menu_bar.set_connected_state(True)
self.SetStatusText(f"Подключено к {ip} | Ваше имя {name}")
d.Destroy()
def on_server_start(self, event):
d = ServerStartDialog(self)
if d.ShowModal() == wx.ID_OK:
name, ip = d.get_data()
self.menu_bar.set_connected_state(True)
self.SetStatusText(f"Сервер запущен на {ip} | Ваше имя {name}")
d.Destroy()
def on_disconnect(self, event):
self.menu_bar.set_connected_state(False)
self.SetStatusText("Подключение отсутствует")
+85
View File
@@ -0,0 +1,85 @@
import wx
class AppMenuBar(wx.MenuBar):
def __init__(self, parent: wx.Frame):
super().__init__()
self.parent = parent
self._create_file_menu()
self._create_connection_menu()
self._create_help_menu()
def _create_file_menu(self):
self.file_menu = wx.Menu()
self.item_open = self.file_menu.Append(
wx.ID_OPEN,
"&Открыть...\tCtrl+O",
"Открыть файл с результатами сессий",
)
self.item_save = self.file_menu.Append(
wx.ID_SAVE,
"&Сохранить\tCtrl+S",
"Сохранить текущие результаты сессий",
)
self.item_save_as = self.file_menu.Append(
wx.ID_SAVEAS,
"Сохранить &как...\tCtrl+Shift+S",
"Сохранить результаты сессий в новый файл",
)
self.file_menu.AppendSeparator()
self.item_exit = self.file_menu.Append(
wx.ID_EXIT,
"&Выход\tAlt+F4",
"Завершить работу программы",
)
self.Append(self.file_menu, "&Файл")
def _create_connection_menu(self):
self.conn_menu = wx.Menu()
self.item_client = self.conn_menu.Append(
wx.ID_ANY,
"&Клиент...",
"Подключиться к серверу",
)
self.item_server = self.conn_menu.Append(
wx.ID_ANY,
"&Сервер...",
"Запустить сервер",
)
self.conn_menu.AppendSeparator()
self.item_disconnect = self.conn_menu.Append(
wx.ID_ANY,
"&Разорвать подключение",
"Завершить сетевое соединение",
)
self.item_disconnect.Enable(False)
self.Append(self.conn_menu, "&Подключение")
def _create_help_menu(self):
self.help_menu = wx.Menu()
self.item_about = self.help_menu.Append(
wx.ID_ABOUT,
"&О программе...\tF1",
"Информация о программе",
)
self.Append(self.help_menu, "&Справка")
def set_connected_state(self, is_connected: bool):
self.item_client.Enable(not is_connected)
self.item_server.Enable(not is_connected)
self.item_disconnect.Enable(is_connected)