chgksuite_tk 0.0.2b0__tar.gz → 0.0.3b0__tar.gz
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_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/PKG-INFO +1 -1
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk/gui.py +143 -3
- chgksuite_tk-0.0.3b0/chgksuite_tk/version.py +1 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/PKG-INFO +1 -1
- chgksuite_tk-0.0.2b0/chgksuite_tk/version.py +0 -1
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/LICENSE +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk/__main__.py +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/._PKG-INFO +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/._SOURCES.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/._dependency_links.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/._entry_points.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/._requires.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/._top_level.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/SOURCES.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/dependency_links.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/entry_points.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/requires.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/chgksuite_tk.egg-info/top_level.txt +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/pyproject.toml +0 -0
- {chgksuite_tk-0.0.2b0 → chgksuite_tk-0.0.3b0}/setup.cfg +0 -0
|
@@ -4,11 +4,14 @@ from __future__ import unicode_literals
|
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
6
|
import os
|
|
7
|
+
import subprocess
|
|
7
8
|
import sys
|
|
9
|
+
import urllib.request
|
|
8
10
|
|
|
9
11
|
try:
|
|
10
12
|
import tkinter as tk
|
|
11
13
|
import tkinter.filedialog as filedialog
|
|
14
|
+
import tkinter.messagebox as messagebox
|
|
12
15
|
import tkinter.simpledialog as simpledialog
|
|
13
16
|
|
|
14
17
|
TKINTER = True
|
|
@@ -31,6 +34,62 @@ from chgksuite.version import __version__
|
|
|
31
34
|
from chgksuite.cli import ArgparseBuilder, single_action
|
|
32
35
|
|
|
33
36
|
|
|
37
|
+
def get_pyapp_executable():
|
|
38
|
+
"""Return the pyapp executable path if running inside pyapp, else None."""
|
|
39
|
+
pyapp_env = os.environ.get("PYAPP", "")
|
|
40
|
+
# PYAPP_PASS_LOCATION sets PYAPP to the executable path instead of "1"
|
|
41
|
+
if pyapp_env and pyapp_env != "1" and os.path.isfile(pyapp_env):
|
|
42
|
+
return pyapp_env
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_installed_version(package_name):
|
|
47
|
+
"""Get installed version of a package."""
|
|
48
|
+
try:
|
|
49
|
+
from importlib.metadata import version
|
|
50
|
+
|
|
51
|
+
return version(package_name)
|
|
52
|
+
except Exception:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def check_pypi_version(package_name):
|
|
57
|
+
"""Get latest version of a package from PyPI."""
|
|
58
|
+
try:
|
|
59
|
+
url = f"https://pypi.org/pypi/{package_name}/json"
|
|
60
|
+
with urllib.request.urlopen(url, timeout=10) as response:
|
|
61
|
+
data = json.loads(response.read().decode())
|
|
62
|
+
return data["info"]["version"]
|
|
63
|
+
except Exception:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def check_for_updates():
|
|
68
|
+
"""Check PyPI for updates to chgksuite and chgksuite-tk. Returns (has_update, details_str, error)."""
|
|
69
|
+
packages = ["chgksuite", "chgksuite-tk"]
|
|
70
|
+
updates = []
|
|
71
|
+
|
|
72
|
+
for pkg in packages:
|
|
73
|
+
installed = get_installed_version(pkg)
|
|
74
|
+
latest = check_pypi_version(pkg)
|
|
75
|
+
if installed is None or latest is None:
|
|
76
|
+
continue
|
|
77
|
+
if installed != latest:
|
|
78
|
+
updates.append((pkg, installed, latest))
|
|
79
|
+
|
|
80
|
+
if updates:
|
|
81
|
+
details = "\n".join(f"{pkg}: {inst} → {lat}" for pkg, inst, lat in updates)
|
|
82
|
+
return True, details, None
|
|
83
|
+
|
|
84
|
+
# No updates - show current versions
|
|
85
|
+
current = ", ".join(
|
|
86
|
+
f"{pkg} {get_installed_version(pkg)}"
|
|
87
|
+
for pkg in packages
|
|
88
|
+
if get_installed_version(pkg)
|
|
89
|
+
)
|
|
90
|
+
return False, current, None
|
|
91
|
+
|
|
92
|
+
|
|
34
93
|
class InputRequester:
|
|
35
94
|
"""Helper to request input from main thread via Tk dialog."""
|
|
36
95
|
|
|
@@ -40,7 +99,9 @@ class InputRequester:
|
|
|
40
99
|
self.event = threading.Event()
|
|
41
100
|
|
|
42
101
|
def _show_dialog(self, prompt):
|
|
43
|
-
self.response = simpledialog.askstring(
|
|
102
|
+
self.response = simpledialog.askstring(
|
|
103
|
+
"Input Required", prompt, parent=self.tk_root
|
|
104
|
+
)
|
|
44
105
|
if self.response is None:
|
|
45
106
|
self.response = ""
|
|
46
107
|
self.event.set()
|
|
@@ -211,6 +272,70 @@ class ParserWrapper(object):
|
|
|
211
272
|
else:
|
|
212
273
|
self.advanced_frame.pack_forget()
|
|
213
274
|
|
|
275
|
+
def check_and_update(self):
|
|
276
|
+
"""Check for updates and run self-update if available."""
|
|
277
|
+
self.update_button.config(state="disabled", text="Проверка обновлений...")
|
|
278
|
+
|
|
279
|
+
def check_thread():
|
|
280
|
+
has_update, details, error = check_for_updates()
|
|
281
|
+
# Schedule UI update on main thread
|
|
282
|
+
self.tk.after(
|
|
283
|
+
0, lambda: self._handle_update_check(has_update, details, error)
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
threading.Thread(target=check_thread, daemon=True).start()
|
|
287
|
+
|
|
288
|
+
def _handle_update_check(self, has_update, details, error):
|
|
289
|
+
"""Handle update check result on main thread."""
|
|
290
|
+
self.update_button.config(state="normal", text="Обновить chgksuite")
|
|
291
|
+
|
|
292
|
+
if has_update is None or (not has_update and not details):
|
|
293
|
+
messagebox.showwarning(
|
|
294
|
+
"Ошибка",
|
|
295
|
+
"Не удалось проверить обновления. Проверьте подключение к интернету.",
|
|
296
|
+
)
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
if not has_update:
|
|
300
|
+
messagebox.showinfo(
|
|
301
|
+
"Обновления", f"Уже установлена последняя версия.\n\n{details}"
|
|
302
|
+
)
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
# Update available - ask user
|
|
306
|
+
reply = messagebox.askyesno(
|
|
307
|
+
"Доступно обновление",
|
|
308
|
+
f"Доступны обновления:\n{details}\n\n"
|
|
309
|
+
"Обновить сейчас? Приложение будет закрыто.",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
if reply:
|
|
313
|
+
self._run_self_update()
|
|
314
|
+
|
|
315
|
+
def _run_self_update(self):
|
|
316
|
+
"""Run pyapp self-update command and close the application."""
|
|
317
|
+
try:
|
|
318
|
+
# Start the update process detached from current process
|
|
319
|
+
if sys.platform == "win32":
|
|
320
|
+
# On Windows, use CREATE_NEW_PROCESS_GROUP to detach
|
|
321
|
+
subprocess.Popen(
|
|
322
|
+
[self.pyapp_executable, "self", "update"],
|
|
323
|
+
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
|
|
324
|
+
| subprocess.DETACHED_PROCESS,
|
|
325
|
+
close_fds=True,
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
# On Unix, use start_new_session to detach
|
|
329
|
+
subprocess.Popen(
|
|
330
|
+
[self.pyapp_executable, "self", "update"],
|
|
331
|
+
start_new_session=True,
|
|
332
|
+
close_fds=True,
|
|
333
|
+
)
|
|
334
|
+
# Close the application
|
|
335
|
+
self.tk.quit()
|
|
336
|
+
except Exception as e:
|
|
337
|
+
messagebox.showerror("Ошибка", f"Не удалось запустить обновление: {e}")
|
|
338
|
+
|
|
214
339
|
def init_tk(self):
|
|
215
340
|
self.tk = tk.Tk()
|
|
216
341
|
self.tk.title("chgksuite v{}".format(__version__))
|
|
@@ -247,13 +372,28 @@ class ParserWrapper(object):
|
|
|
247
372
|
# Output text widget
|
|
248
373
|
self.output_frame = tk.Frame(self.mainframe)
|
|
249
374
|
self.output_frame.pack(side="top", fill="both", expand=True, pady=10)
|
|
250
|
-
self.output_text = tk.Text(
|
|
251
|
-
|
|
375
|
+
self.output_text = tk.Text(
|
|
376
|
+
self.output_frame, height=10, width=70, font=("Courier", 10)
|
|
377
|
+
)
|
|
378
|
+
self.output_scrollbar = tk.Scrollbar(
|
|
379
|
+
self.output_frame, command=self.output_text.yview
|
|
380
|
+
)
|
|
252
381
|
self.output_text.configure(yscrollcommand=self.output_scrollbar.set)
|
|
253
382
|
self.output_text.pack(side="left", fill="both", expand=True)
|
|
254
383
|
self.output_scrollbar.pack(side="right", fill="y")
|
|
255
384
|
self.output_text.config(state="disabled")
|
|
256
385
|
|
|
386
|
+
# Update button (only shown when running inside pyapp)
|
|
387
|
+
self.pyapp_executable = get_pyapp_executable()
|
|
388
|
+
if self.pyapp_executable:
|
|
389
|
+
self.update_button = tk.Button(
|
|
390
|
+
self.mainframe,
|
|
391
|
+
text="Обновить chgksuite",
|
|
392
|
+
command=self.check_and_update,
|
|
393
|
+
width=20,
|
|
394
|
+
)
|
|
395
|
+
self.update_button.pack(side="top", pady=5)
|
|
396
|
+
|
|
257
397
|
def add_argument(self, *args, **kwargs):
|
|
258
398
|
if kwargs.pop("advanced", False):
|
|
259
399
|
frame = self.advanced_frame
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.3b0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.2b0"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|