forked from CastroFidel/winehelper
Compare commits
12 Commits
0f8f192634
...
0.5.4.8
Author | SHA1 | Date | |
---|---|---|---|
|
d5f337e6b4 | ||
|
904c9c9895 | ||
|
1d4ee1fd70 | ||
|
02a2256c8c | ||
|
cbcdba204e | ||
|
66c56f6ecf | ||
|
221b59eda7 | ||
|
adf5f78360 | ||
|
01f19cd94d | ||
|
117e497f94 | ||
|
3527846c6c | ||
|
553d427d66 |
15
winehelper
15
winehelper
@@ -126,6 +126,12 @@ WH_TESTINSTALL_DIR="$DATA_PATH/testinstall"
|
|||||||
WH_WINETRICKS="$DATA_PATH/winetricks_$WINETRICKS_VERSION"
|
WH_WINETRICKS="$DATA_PATH/winetricks_$WINETRICKS_VERSION"
|
||||||
|
|
||||||
WH_MENU_DIR="$HOME/.local/share/applications/WineHelper"
|
WH_MENU_DIR="$HOME/.local/share/applications/WineHelper"
|
||||||
|
|
||||||
|
# TODO: system menu directory
|
||||||
|
# /usr/share/desktop-directories/WineHelper.directory
|
||||||
|
# /etc/xdg/menus/applications-merged/WineHelper.menu
|
||||||
|
|
||||||
|
# user menu directory
|
||||||
WH_MENU_CATEGORY="$HOME/.local/share/desktop-directories/WineHelper.directory"
|
WH_MENU_CATEGORY="$HOME/.local/share/desktop-directories/WineHelper.directory"
|
||||||
WH_MENU_CONFIG="$HOME/.config/menus/applications-merged/WineHelper.menu"
|
WH_MENU_CONFIG="$HOME/.config/menus/applications-merged/WineHelper.menu"
|
||||||
|
|
||||||
@@ -1328,7 +1334,8 @@ use_winetricks () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kill_wine () {
|
kill_wine () {
|
||||||
wine_pids=$(ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' | awk -F/ '{print $3}')
|
wine_pids=$(ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' \
|
||||||
|
| grep "$USER_WORK_PATH" | awk -F/ '{print $3}')
|
||||||
|
|
||||||
for pw_kill_pids in ${wine_pids}; do
|
for pw_kill_pids in ${wine_pids}; do
|
||||||
if ps cax | grep "${pw_kill_pids}" ; then
|
if ps cax | grep "${pw_kill_pids}" ; then
|
||||||
@@ -1416,6 +1423,12 @@ wine_run_install () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_autoinstall () {
|
run_autoinstall () {
|
||||||
|
if [[ $WH_USE_GUI == "1" ]] \
|
||||||
|
&& [[ $(ps -o command= -p "$PPID" | awk '{print $2}') =~ "$DATA_PATH/winehelper_gui.py" ]]
|
||||||
|
then print_ok "Соглашения приняты из графического интерфейса."
|
||||||
|
else print_license_agreement
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $1 == "--clear-pfx" ]] ; then
|
if [[ $1 == "--clear-pfx" ]] ; then
|
||||||
export CLEAR_PREFIX="1"
|
export CLEAR_PREFIX="1"
|
||||||
shift
|
shift
|
||||||
|
@@ -10,11 +10,11 @@ import time
|
|||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, QTabBar,
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QTabBar,
|
||||||
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox,
|
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox,
|
||||||
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox)
|
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox, QSystemTrayIcon, QMenu)
|
||||||
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve
|
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve
|
||||||
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QDesktopServices
|
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor
|
||||||
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
||||||
|
|
||||||
|
|
||||||
@@ -1210,9 +1210,9 @@ class CreatePrefixDialog(QDialog):
|
|||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.parent_gui = parent # Store reference to main window
|
self.parent_gui = parent # Сохранить ссылку на главное окно
|
||||||
self.setWindowTitle("Создание нового префикса")
|
self.setWindowTitle("Создание нового префикса")
|
||||||
self.setMinimumSize(500, 250)
|
self.setMinimumSize(680, 250)
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
|
|
||||||
# Attributes to store results
|
# Attributes to store results
|
||||||
@@ -1226,9 +1226,22 @@ class CreatePrefixDialog(QDialog):
|
|||||||
form_layout = QFormLayout()
|
form_layout = QFormLayout()
|
||||||
form_layout.setSpacing(10)
|
form_layout.setSpacing(10)
|
||||||
|
|
||||||
|
# Создаем виджет для поля ввода и предупреждения
|
||||||
|
name_input_widget = QWidget()
|
||||||
|
name_input_layout = QVBoxLayout(name_input_widget)
|
||||||
|
name_input_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
name_input_layout.setSpacing(2)
|
||||||
|
|
||||||
self.prefix_name_edit = QLineEdit()
|
self.prefix_name_edit = QLineEdit()
|
||||||
self.prefix_name_edit.setPlaceholderText("Например: my_prefix")
|
self.prefix_name_edit.setPlaceholderText("Например: my_prefix")
|
||||||
form_layout.addRow("<b>Имя нового префикса:</b>", self.prefix_name_edit)
|
name_input_layout.addWidget(self.prefix_name_edit)
|
||||||
|
|
||||||
|
self.name_warning_label = QLabel("Имя может содержать только латинские буквы, цифры, тире и знаки подчеркивания.")
|
||||||
|
self.name_warning_label.setStyleSheet("color: red;")
|
||||||
|
self.name_warning_label.setVisible(False)
|
||||||
|
name_input_layout.addWidget(self.name_warning_label)
|
||||||
|
|
||||||
|
form_layout.addRow("<b>Имя нового префикса:</b>", name_input_widget)
|
||||||
|
|
||||||
arch_widget = QWidget()
|
arch_widget = QWidget()
|
||||||
arch_layout = QHBoxLayout(arch_widget)
|
arch_layout = QHBoxLayout(arch_widget)
|
||||||
@@ -1285,7 +1298,7 @@ class CreatePrefixDialog(QDialog):
|
|||||||
|
|
||||||
# Connect signals
|
# Connect signals
|
||||||
self.arch_win32_radio.toggled.connect(self.clear_wine_version_selection)
|
self.arch_win32_radio.toggled.connect(self.clear_wine_version_selection)
|
||||||
self.prefix_name_edit.textChanged.connect(self.update_create_button_state)
|
self.prefix_name_edit.textChanged.connect(self.validate_prefix_name)
|
||||||
self.wine_version_edit.textChanged.connect(self.update_create_button_state)
|
self.wine_version_edit.textChanged.connect(self.update_create_button_state)
|
||||||
|
|
||||||
def open_wine_version_dialog(self):
|
def open_wine_version_dialog(self):
|
||||||
@@ -1301,11 +1314,28 @@ class CreatePrefixDialog(QDialog):
|
|||||||
self.wine_version_edit.clear()
|
self.wine_version_edit.clear()
|
||||||
self.selected_wine_version_value = None
|
self.selected_wine_version_value = None
|
||||||
|
|
||||||
|
def validate_prefix_name(self, text):
|
||||||
|
"""Проверяет имя префикса в реальном времени и показывает/скрывает предупреждение."""
|
||||||
|
valid_pattern = r'^[a-zA-Z0-9_-]*$'
|
||||||
|
if re.match(valid_pattern, text):
|
||||||
|
self.name_warning_label.setVisible(False)
|
||||||
|
else:
|
||||||
|
# Удаляем недопустимые символы
|
||||||
|
cleaned_text = re.sub(r'[^a-zA-Z0-9_-]', '', text)
|
||||||
|
# Блокируем сигналы, чтобы избежать рекурсии при изменении текста
|
||||||
|
self.prefix_name_edit.blockSignals(True)
|
||||||
|
self.prefix_name_edit.setText(cleaned_text)
|
||||||
|
self.prefix_name_edit.blockSignals(False)
|
||||||
|
self.name_warning_label.setVisible(True)
|
||||||
|
|
||||||
|
self.update_create_button_state()
|
||||||
|
|
||||||
def update_create_button_state(self):
|
def update_create_button_state(self):
|
||||||
"""Включает или выключает кнопку 'Создать'."""
|
"""Включает или выключает кнопку 'Создать'."""
|
||||||
name_ok = bool(self.prefix_name_edit.text().strip())
|
name_ok = bool(self.prefix_name_edit.text().strip())
|
||||||
version_ok = bool(self.wine_version_edit.text().strip())
|
version_ok = bool(self.wine_version_edit.text().strip())
|
||||||
self.create_button.setEnabled(name_ok and version_ok)
|
# Кнопка активна, только если имя валидно и версия выбрана
|
||||||
|
self.create_button.setEnabled(name_ok and version_ok and not self.name_warning_label.isVisible())
|
||||||
|
|
||||||
def accept_creation(self):
|
def accept_creation(self):
|
||||||
"""Валидирует данные, сохраняет их и закрывает диалог с успехом."""
|
"""Валидирует данные, сохраняет их и закрывает диалог с успехом."""
|
||||||
@@ -1315,8 +1345,8 @@ class CreatePrefixDialog(QDialog):
|
|||||||
QMessageBox.warning(self, "Ошибка", "Имя префикса не может быть пустым.")
|
QMessageBox.warning(self, "Ошибка", "Имя префикса не может быть пустым.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not re.match(r'^[a-zA-Z0-9_.-]+$', prefix_name):
|
if not re.match(r'^[a-zA-Z0-9_-]+$', prefix_name):
|
||||||
QMessageBox.warning(self, "Ошибка", "Имя префикса может содержать только латинские буквы, цифры, точки, дефисы и подчеркивания.")
|
QMessageBox.warning(self, "Ошибка", "Имя префикса может содержать только латинские буквы, цифры, дефисы и знаки подчеркивания.")
|
||||||
return
|
return
|
||||||
|
|
||||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||||
@@ -1581,6 +1611,7 @@ class WineHelperGUI(QMainWindow):
|
|||||||
self.current_managed_prefix_name = None # Имя префикса, выбранного в выпадающем списке
|
self.current_managed_prefix_name = None # Имя префикса, выбранного в выпадающем списке
|
||||||
self.prefixes_before_install = set()
|
self.prefixes_before_install = set()
|
||||||
|
|
||||||
|
self.is_quitting = False # Флаг для корректного выхода из приложения
|
||||||
self.command_output_buffer = ""
|
self.command_output_buffer = ""
|
||||||
self.command_last_line_was_progress = False
|
self.command_last_line_was_progress = False
|
||||||
# Создаем главный виджет и layout
|
# Создаем главный виджет и layout
|
||||||
@@ -1647,6 +1678,50 @@ class WineHelperGUI(QMainWindow):
|
|||||||
self.raise_()
|
self.raise_()
|
||||||
self.activateWindow()
|
self.activateWindow()
|
||||||
|
|
||||||
|
def create_tray_icon(self):
|
||||||
|
"""Создает и настраивает иконку в системном трее."""
|
||||||
|
if not QSystemTrayIcon.isSystemTrayAvailable():
|
||||||
|
print("Системный трей не доступен.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.tray_icon = QSystemTrayIcon(self)
|
||||||
|
|
||||||
|
icon_path = Var.WH_ICON_PATH
|
||||||
|
if icon_path and os.path.exists(icon_path):
|
||||||
|
pixmap = QPixmap(icon_path)
|
||||||
|
if not pixmap.isNull():
|
||||||
|
self.tray_icon.setIcon(QIcon(pixmap))
|
||||||
|
|
||||||
|
# Создаем и сохраняем меню как атрибут класса, чтобы оно не удалялось
|
||||||
|
self.tray_menu = QMenu(self)
|
||||||
|
|
||||||
|
toggle_visibility_action = self.tray_menu.addAction("Показать/Скрыть")
|
||||||
|
toggle_visibility_action.triggered.connect(self.toggle_visibility)
|
||||||
|
self.tray_menu.addSeparator()
|
||||||
|
|
||||||
|
quit_action = self.tray_menu.addAction("Выход")
|
||||||
|
quit_action.triggered.connect(self.quit_application)
|
||||||
|
|
||||||
|
self.tray_icon.activated.connect(self.on_tray_icon_activated)
|
||||||
|
self.tray_icon.show()
|
||||||
|
|
||||||
|
def on_tray_icon_activated(self, reason):
|
||||||
|
"""Обрабатывает клики по иконке в трее."""
|
||||||
|
# Показываем меню при левом клике
|
||||||
|
if reason == QSystemTrayIcon.Trigger:
|
||||||
|
# Получаем позицию курсора и показываем меню
|
||||||
|
self.tray_menu.popup(QCursor.pos())
|
||||||
|
|
||||||
|
def toggle_visibility(self):
|
||||||
|
"""Переключает видимость главного окна."""
|
||||||
|
if self.isVisible() and self.isActiveWindow():
|
||||||
|
self.hide()
|
||||||
|
else:
|
||||||
|
# Сначала скрываем, чтобы "сбросить" состояние, затем активируем.
|
||||||
|
# Это помогает обойти проблемы с фокусом и переключением рабочих столов.
|
||||||
|
self.hide()
|
||||||
|
self.activate()
|
||||||
|
|
||||||
def add_tab(self, widget, title):
|
def add_tab(self, widget, title):
|
||||||
"""Добавляет вкладку в кастомный TabBar и страницу в StackedWidget."""
|
"""Добавляет вкладку в кастомный TabBar и страницу в StackedWidget."""
|
||||||
self.tab_bar.addTab(title)
|
self.tab_bar.addTab(title)
|
||||||
@@ -2088,7 +2163,7 @@ class WineHelperGUI(QMainWindow):
|
|||||||
|
|
||||||
# --- Контейнер для выбора и управления созданными префиксами ---
|
# --- Контейнер для выбора и управления созданными префиксами ---
|
||||||
self.management_container_groupbox = QGroupBox()
|
self.management_container_groupbox = QGroupBox()
|
||||||
self.management_container_groupbox.setVisible(False) # Скрыт, пока нет префиксов
|
self.management_container_groupbox.setVisible(True) # Всегда виден
|
||||||
container_layout = QVBoxLayout(self.management_container_groupbox)
|
container_layout = QVBoxLayout(self.management_container_groupbox)
|
||||||
|
|
||||||
selector_layout = QHBoxLayout()
|
selector_layout = QHBoxLayout()
|
||||||
@@ -2268,8 +2343,8 @@ class WineHelperGUI(QMainWindow):
|
|||||||
def _load_created_prefixes(self):
|
def _load_created_prefixes(self):
|
||||||
"""Загружает и обновляет список созданных префиксов в выпадающем списке."""
|
"""Загружает и обновляет список созданных префиксов в выпадающем списке."""
|
||||||
prefixes_root_path = os.path.join(Var.USER_WORK_PATH, "prefixes")
|
prefixes_root_path = os.path.join(Var.USER_WORK_PATH, "prefixes")
|
||||||
if not os.path.isdir(prefixes_root_path):
|
has_prefixes_dir = os.path.isdir(prefixes_root_path)
|
||||||
self.management_container_groupbox.setVisible(False)
|
if not has_prefixes_dir:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -2288,12 +2363,9 @@ class WineHelperGUI(QMainWindow):
|
|||||||
self.created_prefix_selector.blockSignals(False)
|
self.created_prefix_selector.blockSignals(False)
|
||||||
|
|
||||||
if not prefix_names:
|
if not prefix_names:
|
||||||
self.management_container_groupbox.setVisible(False)
|
|
||||||
self.on_created_prefix_selected(-1) # Убедимся, что панель управления сброшена
|
self.on_created_prefix_selected(-1) # Убедимся, что панель управления сброшена
|
||||||
return
|
return
|
||||||
|
|
||||||
self.management_container_groupbox.setVisible(True)
|
|
||||||
|
|
||||||
def on_created_prefix_selected(self, index):
|
def on_created_prefix_selected(self, index):
|
||||||
"""Обрабатывает выбор префикса из выпадающего списка."""
|
"""Обрабатывает выбор префикса из выпадающего списка."""
|
||||||
if index == -1:
|
if index == -1:
|
||||||
@@ -2969,14 +3041,15 @@ class WineHelperGUI(QMainWindow):
|
|||||||
authors_text = QTextEdit()
|
authors_text = QTextEdit()
|
||||||
authors_text.setReadOnly(True)
|
authors_text.setReadOnly(True)
|
||||||
authors_text.setHtml("""
|
authors_text.setHtml("""
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center; font-size: 10pt;">
|
||||||
<h2>Разработчики</h2>
|
<p><span style="font-size: 11pt;"><b>Разработчики</b></span><br>
|
||||||
Михаил Тергоев (fidel)<br>
|
Михаил Тергоев (fidel)<br>
|
||||||
Сергей Пальчех (minergenon)</p>
|
Сергей Пальчех (minergenon)</p>
|
||||||
<p><b>Помощники</b><br>
|
<p><span style="font-size: 11pt;"><b>Помощники</b></span><br>
|
||||||
Иван Мажукин (vanomj)</p>
|
Иван Мажукин (vanomj)</p>
|
||||||
<p><b>Идея и поддержка:</b><br>
|
<p><span style="font-size: 11pt;"><b>Идея и поддержка</b></span><br>
|
||||||
сообщество ALT Linux</p>
|
ООО "Базальт СПО"<br>
|
||||||
|
ALT Linux Team</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Отдельная благодарность всем, кто вносит свой вклад в развитие проекта,<br>
|
<p>Отдельная благодарность всем, кто вносит свой вклад в развитие проекта,<br>
|
||||||
тестирует и сообщает об ошибках!</p>
|
тестирует и сообщает об ошибках!</p>
|
||||||
@@ -3154,9 +3227,6 @@ class WineHelperGUI(QMainWindow):
|
|||||||
|
|
||||||
self.created_prefix_selector.setCurrentText(prefix_name)
|
self.created_prefix_selector.setCurrentText(prefix_name)
|
||||||
|
|
||||||
if not self.management_container_groupbox.isVisible():
|
|
||||||
self.management_container_groupbox.setVisible(True)
|
|
||||||
|
|
||||||
def update_installed_apps(self):
|
def update_installed_apps(self):
|
||||||
"""Обновляет список установленных приложений в виде кнопок"""
|
"""Обновляет список установленных приложений в виде кнопок"""
|
||||||
# Если активная кнопка находится в списке удаляемых, сбрасываем ее
|
# Если активная кнопка находится в списке удаляемых, сбрасываем ее
|
||||||
@@ -3741,8 +3811,14 @@ class WineHelperGUI(QMainWindow):
|
|||||||
QMessageBox.critical(self, "Ошибка",
|
QMessageBox.critical(self, "Ошибка",
|
||||||
f"Не удалось обработать команду запуска:\n{command_str}\n\nОшибка: {str(e)}")
|
f"Не удалось обработать команду запуска:\n{command_str}\n\nОшибка: {str(e)}")
|
||||||
|
|
||||||
|
def quit_application(self):
|
||||||
|
"""Инициирует процесс выхода из приложения."""
|
||||||
|
self.is_quitting = True
|
||||||
|
self.close() # Инициируем событие закрытия, которое будет обработано в closeEvent
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""Обрабатывает событие закрытия главного окна."""
|
"""Обрабатывает событие закрытия главного окна."""
|
||||||
|
# Теперь любое закрытие окна (крестик или выход из меню) инициирует выход
|
||||||
if self.running_apps:
|
if self.running_apps:
|
||||||
msg_box = QMessageBox(self)
|
msg_box = QMessageBox(self)
|
||||||
msg_box.setWindowTitle('Подтверждение выхода')
|
msg_box.setWindowTitle('Подтверждение выхода')
|
||||||
@@ -3758,16 +3834,27 @@ class WineHelperGUI(QMainWindow):
|
|||||||
msg_box.exec_()
|
msg_box.exec_()
|
||||||
|
|
||||||
if msg_box.clickedButton() == yes_button:
|
if msg_box.clickedButton() == yes_button:
|
||||||
|
# Отключаем обработчики сигналов от всех запущенных процессов,
|
||||||
|
# так как мы собираемся их принудительно завершить и выйти.
|
||||||
|
# Это предотвращает ошибку RuntimeError при закрытии.
|
||||||
|
for process in self.running_apps.values():
|
||||||
|
process.finished.disconnect()
|
||||||
|
|
||||||
# Используем встроенную команду killall для надежного завершения всех процессов wine
|
# Используем встроенную команду killall для надежного завершения всех процессов wine
|
||||||
print("Завершение всех запущенных приложений через 'winehelper killall'...")
|
print("Завершение всех запущенных приложений через 'winehelper killall'...")
|
||||||
kill_proc = QProcess()
|
# Используем subprocess.run, который дождется завершения команды
|
||||||
kill_proc.start(self.winehelper_path, ["killall"])
|
subprocess.run([self.winehelper_path, "killall"], check=False, capture_output=True)
|
||||||
kill_proc.waitForFinished(5000) # Даем до 5 секунд на завершение
|
|
||||||
|
# Принудительно дожидаемся завершения всех дочерних процессов
|
||||||
|
for process in self.running_apps.values():
|
||||||
|
process.waitForFinished(5000) # Ждем до 5 секунд
|
||||||
|
|
||||||
|
QApplication.instance().quit()
|
||||||
event.accept()
|
event.accept()
|
||||||
else:
|
else:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
else:
|
else:
|
||||||
super().closeEvent(event)
|
QApplication.instance().quit() # Если нет запущенных приложений, просто выходим
|
||||||
|
|
||||||
def uninstall_app(self):
|
def uninstall_app(self):
|
||||||
"""Удаляет выбранное установленное приложение и его префикс"""
|
"""Удаляет выбранное установленное приложение и его префикс"""
|
||||||
@@ -4514,6 +4601,8 @@ def main():
|
|||||||
# Сохраняем ссылку на сервер, чтобы он не был удален сборщиком мусора
|
# Сохраняем ссылку на сервер, чтобы он не был удален сборщиком мусора
|
||||||
window.server = server
|
window.server = server
|
||||||
window.show()
|
window.show()
|
||||||
|
# Создаем иконку в системном трее после создания окна
|
||||||
|
window.create_tray_icon()
|
||||||
return app.exec_()
|
return app.exec_()
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
Reference in New Issue
Block a user