chgksuite_qt 0.0.3b0__py3-none-any.whl → 0.0.4b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chgksuite_qt/gui.py +149 -3
- chgksuite_qt/version.py +1 -1
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/METADATA +1 -1
- chgksuite_qt-0.0.4b0.dist-info/RECORD +15 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/WHEEL +1 -1
- chgksuite_qt-0.0.3b0.dist-info/RECORD +0 -15
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/._PKG-INFO +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/._SOURCES.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/._dependency_links.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/._entry_points.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/._requires.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/._top_level.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/entry_points.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/licenses/LICENSE.txt +0 -0
- {chgksuite_qt-0.0.3b0.dist-info → chgksuite_qt-0.0.4b0.dist-info}/top_level.txt +0 -0
chgksuite_qt/gui.py
CHANGED
|
@@ -5,13 +5,16 @@ from __future__ import unicode_literals
|
|
|
5
5
|
import argparse
|
|
6
6
|
import builtins
|
|
7
7
|
import io
|
|
8
|
+
import json
|
|
8
9
|
import os
|
|
10
|
+
import subprocess
|
|
9
11
|
import sys
|
|
10
12
|
import threading
|
|
13
|
+
import urllib.request
|
|
11
14
|
|
|
12
15
|
try:
|
|
13
16
|
from PyQt6 import QtWidgets, QtGui
|
|
14
|
-
from PyQt6.QtCore import QTimer,
|
|
17
|
+
from PyQt6.QtCore import QTimer, pyqtSignal, QObject
|
|
15
18
|
|
|
16
19
|
PYQT = True
|
|
17
20
|
except ImportError:
|
|
@@ -27,6 +30,63 @@ from chgksuite.common import (
|
|
|
27
30
|
from chgksuite.version import __version__
|
|
28
31
|
from chgksuite.cli import ArgparseBuilder, single_action
|
|
29
32
|
|
|
33
|
+
|
|
34
|
+
def get_pyapp_executable():
|
|
35
|
+
"""Return the pyapp executable path if running inside pyapp, else None."""
|
|
36
|
+
pyapp_env = os.environ.get("PYAPP", "")
|
|
37
|
+
# PYAPP_PASS_LOCATION sets PYAPP to the executable path instead of "1"
|
|
38
|
+
if pyapp_env and pyapp_env != "1" and os.path.isfile(pyapp_env):
|
|
39
|
+
return pyapp_env
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_installed_version(package_name):
|
|
44
|
+
"""Get installed version of a package."""
|
|
45
|
+
try:
|
|
46
|
+
from importlib.metadata import version
|
|
47
|
+
|
|
48
|
+
return version(package_name)
|
|
49
|
+
except Exception:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_pypi_version(package_name):
|
|
54
|
+
"""Get latest version of a package from PyPI."""
|
|
55
|
+
try:
|
|
56
|
+
url = f"https://pypi.org/pypi/{package_name}/json"
|
|
57
|
+
with urllib.request.urlopen(url, timeout=10) as response:
|
|
58
|
+
data = json.loads(response.read().decode())
|
|
59
|
+
return data["info"]["version"]
|
|
60
|
+
except Exception:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def check_for_updates():
|
|
65
|
+
"""Check PyPI for updates to chgksuite and chgksuite-qt. Returns (has_update, details_str, error)."""
|
|
66
|
+
packages = ["chgksuite", "chgksuite-qt"]
|
|
67
|
+
updates = []
|
|
68
|
+
|
|
69
|
+
for pkg in packages:
|
|
70
|
+
installed = get_installed_version(pkg)
|
|
71
|
+
latest = check_pypi_version(pkg)
|
|
72
|
+
if installed is None or latest is None:
|
|
73
|
+
continue
|
|
74
|
+
if installed != latest:
|
|
75
|
+
updates.append((pkg, installed, latest))
|
|
76
|
+
|
|
77
|
+
if updates:
|
|
78
|
+
details = "\n".join(f"{pkg}: {inst} → {lat}" for pkg, inst, lat in updates)
|
|
79
|
+
return True, details, None
|
|
80
|
+
|
|
81
|
+
# No updates - show current versions
|
|
82
|
+
current = ", ".join(
|
|
83
|
+
f"{pkg} {get_installed_version(pkg)}"
|
|
84
|
+
for pkg in packages
|
|
85
|
+
if get_installed_version(pkg)
|
|
86
|
+
)
|
|
87
|
+
return False, current, None
|
|
88
|
+
|
|
89
|
+
|
|
30
90
|
LANGS = ["by", "by_tar", "en", "kz_cyr", "ru", "sr", "ua", "uz", "uz_cyr"] + ["custom"]
|
|
31
91
|
|
|
32
92
|
debug = False
|
|
@@ -34,6 +94,7 @@ debug = False
|
|
|
34
94
|
|
|
35
95
|
class InputRequester(QObject):
|
|
36
96
|
"""Helper to request input from main thread via signal."""
|
|
97
|
+
|
|
37
98
|
input_requested = pyqtSignal(str)
|
|
38
99
|
|
|
39
100
|
def __init__(self, parent_window):
|
|
@@ -86,7 +147,7 @@ class RadioGroupVar:
|
|
|
86
147
|
for val, rb in self.radio_buttons:
|
|
87
148
|
if rb.isChecked():
|
|
88
149
|
return val
|
|
89
|
-
|
|
150
|
+
|
|
90
151
|
|
|
91
152
|
def init_layout(frame, layout, spacing=0):
|
|
92
153
|
layout.setSpacing(spacing)
|
|
@@ -246,6 +307,81 @@ class ParserWrapper(object):
|
|
|
246
307
|
self.advanced_frame.hide()
|
|
247
308
|
self.window.resize(self.window.minimumSizeHint())
|
|
248
309
|
|
|
310
|
+
def check_and_update(self):
|
|
311
|
+
"""Check for updates and run self-update if available."""
|
|
312
|
+
self.update_button.setEnabled(False)
|
|
313
|
+
self.update_button.setText("Проверка обновлений...")
|
|
314
|
+
|
|
315
|
+
# Check for updates in a thread to avoid blocking UI
|
|
316
|
+
def check_thread():
|
|
317
|
+
has_update, details, error = check_for_updates()
|
|
318
|
+
# Schedule UI update on main thread
|
|
319
|
+
QTimer.singleShot(
|
|
320
|
+
0, lambda: self._handle_update_check(has_update, details, error)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
threading.Thread(target=check_thread, daemon=True).start()
|
|
324
|
+
|
|
325
|
+
def _handle_update_check(self, has_update, details, error):
|
|
326
|
+
"""Handle update check result on main thread."""
|
|
327
|
+
self.update_button.setEnabled(True)
|
|
328
|
+
self.update_button.setText("Обновить chgksuite")
|
|
329
|
+
|
|
330
|
+
if has_update is None or (not has_update and not details):
|
|
331
|
+
QtWidgets.QMessageBox.warning(
|
|
332
|
+
self.window,
|
|
333
|
+
"Ошибка",
|
|
334
|
+
"Не удалось проверить обновления. Проверьте подключение к интернету.",
|
|
335
|
+
)
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
if not has_update:
|
|
339
|
+
QtWidgets.QMessageBox.information(
|
|
340
|
+
self.window,
|
|
341
|
+
"Обновления",
|
|
342
|
+
f"Уже установлена последняя версия.\n\n{details}",
|
|
343
|
+
)
|
|
344
|
+
return
|
|
345
|
+
|
|
346
|
+
# Update available - ask user
|
|
347
|
+
reply = QtWidgets.QMessageBox.question(
|
|
348
|
+
self.window,
|
|
349
|
+
"Доступно обновление",
|
|
350
|
+
f"Доступны обновления:\n{details}\n\n"
|
|
351
|
+
"Обновить сейчас? Приложение будет закрыто.",
|
|
352
|
+
QtWidgets.QMessageBox.StandardButton.Yes
|
|
353
|
+
| QtWidgets.QMessageBox.StandardButton.No,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
|
|
357
|
+
self._run_self_update()
|
|
358
|
+
|
|
359
|
+
def _run_self_update(self):
|
|
360
|
+
"""Run pyapp self-update command and close the application."""
|
|
361
|
+
try:
|
|
362
|
+
# Start the update process detached from current process
|
|
363
|
+
if sys.platform == "win32":
|
|
364
|
+
# On Windows, use CREATE_NEW_PROCESS_GROUP to detach
|
|
365
|
+
subprocess.Popen(
|
|
366
|
+
[self.pyapp_executable, "self", "update"],
|
|
367
|
+
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
|
|
368
|
+
| subprocess.DETACHED_PROCESS,
|
|
369
|
+
close_fds=True,
|
|
370
|
+
)
|
|
371
|
+
else:
|
|
372
|
+
# On Unix, use start_new_session to detach
|
|
373
|
+
subprocess.Popen(
|
|
374
|
+
[self.pyapp_executable, "self", "update"],
|
|
375
|
+
start_new_session=True,
|
|
376
|
+
close_fds=True,
|
|
377
|
+
)
|
|
378
|
+
# Close the application
|
|
379
|
+
self.app.quit()
|
|
380
|
+
except Exception as e:
|
|
381
|
+
QtWidgets.QMessageBox.critical(
|
|
382
|
+
self.window, "Ошибка", f"Не удалось запустить обновление: {e}"
|
|
383
|
+
)
|
|
384
|
+
|
|
249
385
|
def init_qt(self):
|
|
250
386
|
self.app = QtWidgets.QApplication(sys.argv)
|
|
251
387
|
self.window = QtWidgets.QWidget()
|
|
@@ -267,7 +403,9 @@ class ParserWrapper(object):
|
|
|
267
403
|
self.ok_button = QtWidgets.QPushButton("Запустить")
|
|
268
404
|
self.ok_button.clicked.connect(self.ok_button_press)
|
|
269
405
|
self.button_layout.addWidget(self.ok_button)
|
|
270
|
-
self.advanced_checkbox_var = QtWidgets.QCheckBox(
|
|
406
|
+
self.advanced_checkbox_var = QtWidgets.QCheckBox(
|
|
407
|
+
"Показать дополнительные настройки"
|
|
408
|
+
)
|
|
271
409
|
self.advanced_checkbox_var.stateChanged.connect(self.toggle_advanced_frame)
|
|
272
410
|
self.button_layout.addWidget(self.advanced_checkbox_var)
|
|
273
411
|
self.advanced_frame.hide()
|
|
@@ -279,6 +417,13 @@ class ParserWrapper(object):
|
|
|
279
417
|
self.output_text.setMinimumHeight(150)
|
|
280
418
|
self.window_layout.addWidget(self.output_text)
|
|
281
419
|
|
|
420
|
+
# Update button (only shown when running inside pyapp)
|
|
421
|
+
self.pyapp_executable = get_pyapp_executable()
|
|
422
|
+
if self.pyapp_executable:
|
|
423
|
+
self.update_button = QtWidgets.QPushButton("Обновить chgksuite")
|
|
424
|
+
self.update_button.clicked.connect(self.check_and_update)
|
|
425
|
+
self.window_layout.addWidget(self.update_button)
|
|
426
|
+
|
|
282
427
|
def add_argument(self, *args, **kwargs):
|
|
283
428
|
if kwargs.pop("advanced", False):
|
|
284
429
|
frame = self.advanced_frame
|
|
@@ -432,6 +577,7 @@ class SubparsersWrapper(object):
|
|
|
432
577
|
self.layout.addWidget(radio)
|
|
433
578
|
return pw
|
|
434
579
|
|
|
580
|
+
|
|
435
581
|
def app():
|
|
436
582
|
_, resourcedir = get_source_dirs()
|
|
437
583
|
ld = get_lastdir()
|
chgksuite_qt/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.4b0"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
chgksuite_qt/__main__.py,sha256=RujA7mxQMHCQBIRpkcCV7n96qsF4SOw8KnODbfaWP-A,143
|
|
2
|
+
chgksuite_qt/gui.py,sha256=aIfs0u2WvQMKU8UnWUPP3IIQvepBkPHnkobezgaR72g,21659
|
|
3
|
+
chgksuite_qt/version.py,sha256=_EEK3lBnUksNOov0bX8HkM84vSao0EC5q1qtTB6FipA,24
|
|
4
|
+
chgksuite_qt-0.0.4b0.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
5
|
+
chgksuite_qt-0.0.4b0.dist-info/._PKG-INFO,sha256=cGCSIA4k4LaOZZ1TzS2pJbfVjOKzlfckc6AvwWVUIm0,4096
|
|
6
|
+
chgksuite_qt-0.0.4b0.dist-info/._SOURCES.txt,sha256=cJD1Y7hSI__2iP5zgomjbYGfNemIJ2IdiQB1Q_CBhqA,4096
|
|
7
|
+
chgksuite_qt-0.0.4b0.dist-info/._dependency_links.txt,sha256=8zLto8jxNqMcOxDgJu-8xA2M8rWsZ5-gIO0sEYL--lY,4096
|
|
8
|
+
chgksuite_qt-0.0.4b0.dist-info/._entry_points.txt,sha256=M3W25-yp8kZFUMJ3emHEflLWvQlJkQogKkGBofXFvDY,4096
|
|
9
|
+
chgksuite_qt-0.0.4b0.dist-info/._requires.txt,sha256=d80mAqjZc811mEijlEPPOBB0aeTCqlPcHFTPsW2s3hk,4096
|
|
10
|
+
chgksuite_qt-0.0.4b0.dist-info/._top_level.txt,sha256=O3oq1KPhU8EtJKAWOb9TSyNSxrCI2JwT_t445fXIVq4,4096
|
|
11
|
+
chgksuite_qt-0.0.4b0.dist-info/METADATA,sha256=mlmHhMn3dFodVL4OXJcCpbx2Fys_NCRh08EId5ToNUo,573
|
|
12
|
+
chgksuite_qt-0.0.4b0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
+
chgksuite_qt-0.0.4b0.dist-info/entry_points.txt,sha256=-CJKDdxNqsUSAPWDPAqFX76__c5ZcDIkCZOERijAY_4,53
|
|
14
|
+
chgksuite_qt-0.0.4b0.dist-info/top_level.txt,sha256=macEIOXf4jYgtYr0E6pq_FKmDsvXXdzX46olPHFBpOQ,13
|
|
15
|
+
chgksuite_qt-0.0.4b0.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
chgksuite_qt/__main__.py,sha256=RujA7mxQMHCQBIRpkcCV7n96qsF4SOw8KnODbfaWP-A,143
|
|
2
|
-
chgksuite_qt/gui.py,sha256=I38T4vZ-VEJ3vpWPTfkVVdBzS-HDPZTeSvQWZgMrRaE,16380
|
|
3
|
-
chgksuite_qt/version.py,sha256=mBnMYfxg52DfwMea2Iskot56mAShybAVXEQQkyRqxKc,24
|
|
4
|
-
chgksuite_qt-0.0.3b0.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
5
|
-
chgksuite_qt-0.0.3b0.dist-info/._PKG-INFO,sha256=cGCSIA4k4LaOZZ1TzS2pJbfVjOKzlfckc6AvwWVUIm0,4096
|
|
6
|
-
chgksuite_qt-0.0.3b0.dist-info/._SOURCES.txt,sha256=cJD1Y7hSI__2iP5zgomjbYGfNemIJ2IdiQB1Q_CBhqA,4096
|
|
7
|
-
chgksuite_qt-0.0.3b0.dist-info/._dependency_links.txt,sha256=8zLto8jxNqMcOxDgJu-8xA2M8rWsZ5-gIO0sEYL--lY,4096
|
|
8
|
-
chgksuite_qt-0.0.3b0.dist-info/._entry_points.txt,sha256=M3W25-yp8kZFUMJ3emHEflLWvQlJkQogKkGBofXFvDY,4096
|
|
9
|
-
chgksuite_qt-0.0.3b0.dist-info/._requires.txt,sha256=d80mAqjZc811mEijlEPPOBB0aeTCqlPcHFTPsW2s3hk,4096
|
|
10
|
-
chgksuite_qt-0.0.3b0.dist-info/._top_level.txt,sha256=O3oq1KPhU8EtJKAWOb9TSyNSxrCI2JwT_t445fXIVq4,4096
|
|
11
|
-
chgksuite_qt-0.0.3b0.dist-info/METADATA,sha256=aAK4FRRGMIJAKZUVQ_vRXXRpCeyuSEymVpt-k5b61fk,573
|
|
12
|
-
chgksuite_qt-0.0.3b0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
-
chgksuite_qt-0.0.3b0.dist-info/entry_points.txt,sha256=-CJKDdxNqsUSAPWDPAqFX76__c5ZcDIkCZOERijAY_4,53
|
|
14
|
-
chgksuite_qt-0.0.3b0.dist-info/top_level.txt,sha256=macEIOXf4jYgtYr0E6pq_FKmDsvXXdzX46olPHFBpOQ,13
|
|
15
|
-
chgksuite_qt-0.0.3b0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|