forked from CastroFidel/winehelper
Merge branch 'minergenon-devel'
This commit is contained in:
7
GENERAL
Normal file
7
GENERAL
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Руководство пользователя
|
||||||
|
Подробное и актуальное руководство по использованию WineHelper смотрите на сайте: https://www.altlinux.org/Winehelper
|
||||||
|
|
||||||
|
# Совместимость ПО и сертификаты
|
||||||
|
С полным списком совместимого ПО и сертификатами можно ознакомиться по следующим ссылкам:
|
||||||
|
Для 10 платформы: https://www.basealt.ru/fileadmin/user_upload/compatibility/P10-view2.html
|
||||||
|
Для 11 платформы: https://www.basealt.ru/fileadmin/user_upload/compatibility/P11-view2.html
|
||||||
@@ -7,7 +7,7 @@ if [[ $(id -u) -eq 0 ]] ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
##### DEFAULT PATH #####
|
##### DEFAULT PATH #####
|
||||||
export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT THIRD_PARTY_FILE
|
export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT THIRD_PARTY_FILE WH_ICON_TRAY GENERAL
|
||||||
|
|
||||||
SCRIPT_NAME="$(basename "$0")"
|
SCRIPT_NAME="$(basename "$0")"
|
||||||
if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
|
if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
|
||||||
@@ -17,11 +17,12 @@ if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
|
|||||||
RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
|
RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
|
||||||
DATA_PATH="/usr/share/$SCRIPT_NAME"
|
DATA_PATH="/usr/share/$SCRIPT_NAME"
|
||||||
WH_ICON_PATH="/usr/share/icons/hicolor/scalable/apps/winehelper.svg"
|
WH_ICON_PATH="/usr/share/icons/hicolor/scalable/apps/winehelper.svg"
|
||||||
WH_ICON_TRAY=" /usr/share/icons/hicolor/symbolic/apps/winehelper-symbolic.svg"
|
WH_ICON_TRAY="/usr/share/icons/hicolor/symbolic/apps/winehelper-symbolic.svg"
|
||||||
CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG"
|
CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG"
|
||||||
LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE"
|
LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE"
|
||||||
AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT"
|
AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT"
|
||||||
THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD-PARTY"
|
THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD-PARTY"
|
||||||
|
GENERAL="/usr/share/doc/winehelper-$WH_VERSION/GENERAL"
|
||||||
else
|
else
|
||||||
# переменные для тестового запуска WineHelper из репозитория
|
# переменные для тестового запуска WineHelper из репозитория
|
||||||
USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
|
USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
|
||||||
@@ -33,6 +34,7 @@ else
|
|||||||
LICENSE_FILE="$DATA_PATH/LICENSE"
|
LICENSE_FILE="$DATA_PATH/LICENSE"
|
||||||
AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
|
AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
|
||||||
THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY"
|
THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY"
|
||||||
|
GENERAL="$DATA_PATH/GENERAL"
|
||||||
WH_DEVEL="1"
|
WH_DEVEL="1"
|
||||||
|
|
||||||
# минимальная проверка синтаксиса скриптов
|
# минимальная проверка синтаксиса скриптов
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ 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, QSystemTrayIcon, QMenu)
|
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox, QSystemTrayIcon, QMenu)
|
||||||
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve, pyqtSignal
|
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve, pyqtSignal, QRect
|
||||||
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor, QTextCharFormat
|
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor, QTextCharFormat, QColor, QPalette
|
||||||
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
||||||
|
|
||||||
|
|
||||||
@@ -26,9 +26,11 @@ class Var:
|
|||||||
DATA_PATH = os.environ.get("DATA_PATH")
|
DATA_PATH = os.environ.get("DATA_PATH")
|
||||||
CHANGELOG_FILE = os.environ.get("CHANGELOG_FILE")
|
CHANGELOG_FILE = os.environ.get("CHANGELOG_FILE")
|
||||||
WH_ICON_PATH = os.environ.get("WH_ICON_PATH")
|
WH_ICON_PATH = os.environ.get("WH_ICON_PATH")
|
||||||
|
WH_ICON_TRAY = os.environ.get("WH_ICON_TRAY")
|
||||||
LICENSE_FILE = os.environ.get("LICENSE_FILE")
|
LICENSE_FILE = os.environ.get("LICENSE_FILE")
|
||||||
LICENSE_AGREEMENT_FILE = os.environ.get("AGREEMENT")
|
LICENSE_AGREEMENT_FILE = os.environ.get("AGREEMENT")
|
||||||
THIRD_PARTY_FILE = os.environ.get("THIRD_PARTY_FILE")
|
THIRD_PARTY_FILE = os.environ.get("THIRD_PARTY_FILE")
|
||||||
|
GENERAL = os.environ.get("GENERAL")
|
||||||
|
|
||||||
class DependencyManager:
|
class DependencyManager:
|
||||||
"""Класс для управления проверкой и установкой системных зависимостей."""
|
"""Класс для управления проверкой и установкой системных зависимостей."""
|
||||||
@@ -1737,6 +1739,18 @@ class WineHelperGUI(QMainWindow):
|
|||||||
self.raise_()
|
self.raise_()
|
||||||
self.activateWindow()
|
self.activateWindow()
|
||||||
|
|
||||||
|
def create_colorized_icon(self, icon_path, color):
|
||||||
|
"""Загружает иконку (SVG) и применяет к ней указанный цвет."""
|
||||||
|
target_pixmap = QPixmap(icon_path)
|
||||||
|
if target_pixmap.isNull():
|
||||||
|
return QIcon()
|
||||||
|
|
||||||
|
painter = QPainter(target_pixmap)
|
||||||
|
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
|
||||||
|
painter.fillRect(target_pixmap.rect(), QColor(color))
|
||||||
|
painter.end()
|
||||||
|
return QIcon(target_pixmap)
|
||||||
|
|
||||||
def create_tray_icon(self):
|
def create_tray_icon(self):
|
||||||
"""Создает и настраивает иконку в системном трее."""
|
"""Создает и настраивает иконку в системном трее."""
|
||||||
if not QSystemTrayIcon.isSystemTrayAvailable():
|
if not QSystemTrayIcon.isSystemTrayAvailable():
|
||||||
@@ -1745,11 +1759,23 @@ class WineHelperGUI(QMainWindow):
|
|||||||
|
|
||||||
self.tray_icon = QSystemTrayIcon(self)
|
self.tray_icon = QSystemTrayIcon(self)
|
||||||
|
|
||||||
icon_path = Var.WH_ICON_PATH
|
# --- Определение цвета иконки в зависимости от темы ---
|
||||||
if icon_path and os.path.exists(icon_path):
|
if Var.WH_ICON_TRAY and os.path.exists(Var.WH_ICON_TRAY):
|
||||||
pixmap = QPixmap(icon_path)
|
# Получаем цвет текста окна из палитры приложения.
|
||||||
if not pixmap.isNull():
|
# Это хороший индикатор для определения контрастного цвета.
|
||||||
self.tray_icon.setIcon(QIcon(pixmap))
|
window_text_color = self.palette().color(QPalette.WindowText)
|
||||||
|
|
||||||
|
# Если цвет текста светлый (высокая яркость), значит, тема темная.
|
||||||
|
# И наоборот: если цвет текста темный, тема светлая.
|
||||||
|
# Яркость > 127 обычно указывает на светлый цвет.
|
||||||
|
is_dark_theme = window_text_color.lightness() > 127
|
||||||
|
|
||||||
|
if is_dark_theme:
|
||||||
|
# Для темных тем используем белую иконку
|
||||||
|
self.tray_icon.setIcon(self.create_colorized_icon(Var.WH_ICON_TRAY, Qt.white))
|
||||||
|
else:
|
||||||
|
# Для светлых тем используем черную иконку
|
||||||
|
self.tray_icon.setIcon(self.create_colorized_icon(Var.WH_ICON_TRAY, Qt.black))
|
||||||
|
|
||||||
# Создаем и сохраняем меню как атрибут класса, чтобы оно не удалялось
|
# Создаем и сохраняем меню как атрибут класса, чтобы оно не удалялось
|
||||||
self.tray_menu = QMenu(self)
|
self.tray_menu = QMenu(self)
|
||||||
@@ -1792,10 +1818,7 @@ class WineHelperGUI(QMainWindow):
|
|||||||
title = "Автоматическая установка"
|
title = "Автоматическая установка"
|
||||||
html_content = ("<h3>Автоматическая установка</h3>"
|
html_content = ("<h3>Автоматическая установка</h3>"
|
||||||
"<p>Скрипты из этого списка скачают, установят и настроят приложение за вас. Просто выберите программу и нажмите «Установить».</p>"
|
"<p>Скрипты из этого списка скачают, установят и настроят приложение за вас. Просто выберите программу и нажмите «Установить».</p>"
|
||||||
"<p>Для доступа к экспериментальным скриптам установки отметьте опцию <b>«Показать тестовые версии»</b> внизу списка.</p>"
|
"<p>Для доступа к экспериментальным скриптам установки отметьте опцию <b>«Показать тестовые версии»</b> внизу списка.</p>")
|
||||||
"<br><h3>Совместимость с дистрибутивами Альт</h3>"
|
|
||||||
"<p>С полным списком совместимого ПО и сертификатами (не только для WineHelper) можно ознакомиться по следующим ссылкам:<br>"
|
|
||||||
"<a href='https://www.basealt.ru/fileadmin/user_upload/compatibility/P10-view2.html'>Для 10 платформы</a> | <a href='https://www.basealt.ru/fileadmin/user_upload/compatibility/P11-view2.html'>Для 11 платформы</a></p>")
|
|
||||||
show_global = False
|
show_global = False
|
||||||
elif tab_name == "Ручная установка":
|
elif tab_name == "Ручная установка":
|
||||||
title = "Ручная установка"
|
title = "Ручная установка"
|
||||||
@@ -2201,6 +2224,7 @@ class WineHelperGUI(QMainWindow):
|
|||||||
self.install_tabs_data['auto'] = {
|
self.install_tabs_data['auto'] = {
|
||||||
'buttons': buttons, 'layout': layout, 'search_edit': search_edit, 'scroll_area': scroll_area
|
'buttons': buttons, 'layout': layout, 'search_edit': search_edit, 'scroll_area': scroll_area
|
||||||
}
|
}
|
||||||
|
self.install_tabs_data['auto']['test_buttons'] = []
|
||||||
|
|
||||||
# Добавляем чекбокс для тестовых версий
|
# Добавляем чекбокс для тестовых версий
|
||||||
test_checkbox = QCheckBox("Показать тестовые версии")
|
test_checkbox = QCheckBox("Показать тестовые версии")
|
||||||
@@ -2236,43 +2260,63 @@ class WineHelperGUI(QMainWindow):
|
|||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
script_folders = ["autoinstall"]
|
is_checked = data['test_checkbox'].isChecked()
|
||||||
if data['test_checkbox'].isChecked():
|
test_buttons = data.get('test_buttons', [])
|
||||||
script_folders.append("testinstall")
|
|
||||||
|
|
||||||
# Перед удалением кнопок останавливаем все связанные с ними таймеры анимации
|
# Если нужно показать тестовые версии и они еще не добавлены
|
||||||
for btn in data['buttons']:
|
if is_checked and not test_buttons:
|
||||||
if btn in self.icon_animators:
|
test_script_folder = "testinstall"
|
||||||
anim_data = self.icon_animators.pop(btn)
|
script_path = os.path.join(Var.DATA_PATH, test_script_folder)
|
||||||
if 'main_timer' in anim_data:
|
|
||||||
anim_data['main_timer'].stop()
|
|
||||||
if 'animation' in anim_data and anim_data['animation']:
|
|
||||||
anim_data['animation'].stop()
|
|
||||||
|
|
||||||
# Сбрасываем ссылку на активную кнопку, если она была удалена
|
|
||||||
if self.current_active_button in data['buttons']:
|
|
||||||
self.current_active_button = None
|
|
||||||
|
|
||||||
# Очищаем старые кнопки и layout
|
|
||||||
for btn in data['buttons']:
|
|
||||||
btn.parent().deleteLater()
|
|
||||||
data['buttons'].clear()
|
|
||||||
|
|
||||||
# Заполняем layout новыми кнопками
|
|
||||||
scripts = []
|
|
||||||
for folder in script_folders:
|
|
||||||
script_path = os.path.join(Var.DATA_PATH, folder)
|
|
||||||
if os.path.isdir(script_path):
|
if os.path.isdir(script_path):
|
||||||
try:
|
try:
|
||||||
folder_scripts = sorted(os.listdir(script_path))
|
folder_scripts = sorted(os.listdir(script_path))
|
||||||
self._populate_install_grid(data['layout'], folder_scripts, folder, data['buttons'])
|
# Запоминаем, какие кнопки являются тестовыми
|
||||||
scripts.extend(folder_scripts)
|
new_test_buttons = []
|
||||||
except OSError as e:
|
self._populate_install_grid(data['layout'], folder_scripts, test_script_folder, new_test_buttons)
|
||||||
print(f"Не удалось прочитать директорию {script_path}: {e}")
|
data['test_buttons'] = new_test_buttons
|
||||||
|
data['buttons'].extend(new_test_buttons)
|
||||||
|
self.autoinstall_scripts.extend(folder_scripts)
|
||||||
|
|
||||||
self.autoinstall_scripts = scripts
|
# Применяем фильтр и прокручиваем к первому новому элементу
|
||||||
# Применяем текущий фильтр поиска к обновленному списку
|
self.filter_buttons('auto')
|
||||||
self.filter_buttons('auto')
|
if new_test_buttons:
|
||||||
|
first_new_button = new_test_buttons[0]
|
||||||
|
frame = first_new_button.parent()
|
||||||
|
if isinstance(frame, QFrame):
|
||||||
|
# Даем время на отрисовку перед прокруткой
|
||||||
|
QTimer.singleShot(100, lambda: data['scroll_area'].ensureWidgetVisible(frame, 50, 50))
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
print(f"Не удалось прочитать директорию {test_script_folder}: {e}")
|
||||||
|
|
||||||
|
# Если нужно скрыть тестовые версии и они были добавлены
|
||||||
|
elif not is_checked and test_buttons:
|
||||||
|
# Останавливаем анимацию и удаляем виджеты тестовых кнопок
|
||||||
|
for btn in test_buttons:
|
||||||
|
if btn in self.icon_animators:
|
||||||
|
anim_data = self.icon_animators.pop(btn)
|
||||||
|
if 'main_timer' in anim_data:
|
||||||
|
anim_data['main_timer'].stop()
|
||||||
|
if 'animation' in anim_data and anim_data['animation']:
|
||||||
|
anim_data['animation'].stop()
|
||||||
|
|
||||||
|
# Удаляем кнопку из основного списка
|
||||||
|
if btn in data['buttons']:
|
||||||
|
data['buttons'].remove(btn)
|
||||||
|
|
||||||
|
# Удаляем фрейм кнопки из layout
|
||||||
|
frame = btn.parent()
|
||||||
|
if frame:
|
||||||
|
frame.deleteLater()
|
||||||
|
|
||||||
|
# Очищаем список тестовых кнопок
|
||||||
|
data['test_buttons'].clear()
|
||||||
|
# Обновляем список скриптов
|
||||||
|
self.autoinstall_scripts = [s for s in self.autoinstall_scripts if not os.path.exists(os.path.join(Var.DATA_PATH, "testinstall", s))]
|
||||||
|
|
||||||
|
# В любом случае применяем фильтр, чтобы скрыть/показать кнопки в соответствии с поиском
|
||||||
|
if data['test_checkbox'].isChecked():
|
||||||
|
self.filter_buttons('auto')
|
||||||
|
|
||||||
def create_installed_tab(self):
|
def create_installed_tab(self):
|
||||||
"""Создает вкладку для отображения установленных программ в виде кнопок"""
|
"""Создает вкладку для отображения установленных программ в виде кнопок"""
|
||||||
@@ -3234,17 +3278,44 @@ class WineHelperGUI(QMainWindow):
|
|||||||
help_subtabs = QTabWidget()
|
help_subtabs = QTabWidget()
|
||||||
help_layout.addWidget(help_subtabs)
|
help_layout.addWidget(help_subtabs)
|
||||||
|
|
||||||
# Подвкладка "Руководство"
|
# Подвкладка "Общее"
|
||||||
guide_tab = QWidget()
|
general_tab = QWidget()
|
||||||
guide_layout = QVBoxLayout(guide_tab)
|
general_layout = QVBoxLayout(general_tab)
|
||||||
guide_text = QTextBrowser()
|
general_text = QTextBrowser()
|
||||||
guide_text.setOpenExternalLinks(True)
|
general_text.setOpenExternalLinks(True)
|
||||||
guide_text.setHtml("""
|
|
||||||
<h2>Руководство пользователя</h2>
|
try:
|
||||||
<p>Подробное и актуальное руководство по использованию WineHelper смотрите на <a href="https://www.altlinux.org/Winehelper">https://www.altlinux.org/Winehelper</a></p>
|
if not Var.GENERAL or not os.path.exists(Var.GENERAL):
|
||||||
""")
|
raise FileNotFoundError
|
||||||
guide_layout.addWidget(guide_text)
|
|
||||||
help_subtabs.addTab(guide_tab, "Руководство")
|
with open(Var.GENERAL, 'r', encoding='utf-8') as f:
|
||||||
|
general_content = f.read()
|
||||||
|
|
||||||
|
html_content = ""
|
||||||
|
url_re = re.compile(r'(https?://[^\s]+)')
|
||||||
|
|
||||||
|
for line in general_content.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
html_content += "<br>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
line = html.escape(line)
|
||||||
|
line = url_re.sub(r'<a href="\1">\1</a>', line)
|
||||||
|
|
||||||
|
if line.startswith('# '):
|
||||||
|
html_content += f'<h2>{line[2:]}</h2>'
|
||||||
|
elif line.startswith('Для '):
|
||||||
|
html_content += f'<p style="margin-left: 10px;">• {line}</p>'
|
||||||
|
else:
|
||||||
|
html_content += f'<p>{line}</p>'
|
||||||
|
|
||||||
|
general_text.setHtml(html_content)
|
||||||
|
except (FileNotFoundError, TypeError):
|
||||||
|
general_text.setHtml(f'<h2>Ошибка</h2><p>Не удалось загрузить файл с общей информацией по пути:<br>{Var.GENERAL}</p>')
|
||||||
|
|
||||||
|
general_layout.addWidget(general_text)
|
||||||
|
help_subtabs.addTab(general_tab, "Общее")
|
||||||
|
|
||||||
# Подвкладка "Авторы"
|
# Подвкладка "Авторы"
|
||||||
authors_tab = QWidget()
|
authors_tab = QWidget()
|
||||||
@@ -4903,7 +4974,7 @@ def main():
|
|||||||
window.server = server
|
window.server = server
|
||||||
window.show()
|
window.show()
|
||||||
# Создаем иконку в системном трее после создания окна
|
# Создаем иконку в системном трее после создания окна
|
||||||
window.create_tray_icon()
|
# window.create_tray_icon() # Временно отключено
|
||||||
return app.exec_()
|
return app.exec_()
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
Reference in New Issue
Block a user