From d3ed6d1b104d2e99224d69809b141e8a978f6812 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:04:08 +0000 Subject: [PATCH 1/2] Initial plan From 0c8f8d936898bc29ad6df8e4484debec4df0a5ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:07:50 +0000 Subject: [PATCH 2/2] Implement README TODO tasks: progress bar, compression level, drag and drop Co-authored-by: leotada <1084584+leotada@users.noreply.github.com> --- README.md | 6 +++--- main.py | 33 +++++++++++++++++++++++++++++++-- zipcontroller.py | 34 +++++++++++++++++++++++++++------- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8972d93..3b294f5 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ TODO -* Progress bar -* More compression options -* Drag and drop support +* ~~Progress bar~~ +* ~~More compression options~~ +* ~~Drag and drop support~~ 7Zip DOC - file:///usr/share/doc/p7zip/DOC/MANUAL/cmdline/switches/method.htm diff --git a/main.py b/main.py index 5e50c79..0ba984b 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,9 @@ import multiprocessing +import urllib.parse import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk +from gi.repository import Gdk, GLib, Gtk from zipcontroller import ZipController @@ -46,11 +47,24 @@ def __init__(self): self.button_threads.set_value(multiprocessing.cpu_count()) box1.add(self.button_threads) + label_level = Gtk.Label("Level") + box1.add(label_level) + + self.button_level = Gtk.SpinButton() + self.button_level.set_numeric(True) + self.button_level.set_range(0, 9) + self.button_level.set_increments(1, 1) + self.button_level.set_value(5) + box1.add(self.button_level) + self.store = Gtk.ListStore(str) tree = Gtk.TreeView(self.store) renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Arquivos", renderer, text=0) tree.append_column(column) + tree.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) + tree.drag_dest_add_uri_targets() + tree.connect("drag-data-received", self.on_drag_data_received) box0.pack_start(tree, True, True, 0) box_bottom_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) @@ -64,6 +78,10 @@ def __init__(self): button_clear.connect("clicked", self.clean) box_bottom_buttons.add(button_clear) + self.progress_bar = Gtk.ProgressBar() + self.progress_bar.set_show_text(True) + box0.add(self.progress_bar) + def clean(self, widget): self.file_list.clear() self.store.clear() @@ -127,7 +145,18 @@ def compress(self, widget): output = self.entry_name.get_text() if len(self.file_list) > 0 and output != '': multithread = self.button_threads.get_value_as_int() - ZipController().compress(self.file_list, output, multithread) + level = self.button_level.get_value_as_int() + self.progress_bar.set_fraction(0) + ZipController().compress(self.file_list, output, multithread, level, + progress_callback=lambda f: GLib.idle_add(self.progress_bar.set_fraction, f)) + + def on_drag_data_received(self, widget, drag_context, x, y, data, info, time): + for uri in data.get_uris(): + path = urllib.parse.urlparse(uri).path + path = urllib.parse.unquote(path) + if path not in self.file_list: + self.file_list.append(path) + self.store.append([path]) if __name__ == '__main__': app_window = Py7zip() diff --git a/zipcontroller.py b/zipcontroller.py index cac6e94..8305d7b 100644 --- a/zipcontroller.py +++ b/zipcontroller.py @@ -1,19 +1,39 @@ +import re import subprocess import threading -from typing import List +from typing import Callable, List, Optional class ZipController: - def compress(self, file_list: List[str], output: str, threads: int, level: int = 5): + def compress(self, file_list: List[str], output: str, threads: int, level: int = 5, + progress_callback: Optional[Callable[[float], None]] = None): call = ["7za", "a", "-r", "-t7z", "-m0=lzma2", f"-mx={level}", f"-mmt={threads}", output] for file in file_list: call.append(file) - self.start_thread(call) + self.start_thread(call, progress_callback) - def do_subprocess(self, call): - subprocess.check_call(call) + def do_subprocess(self, call, progress_callback=None): + process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + buf = b'' + while True: + chunk = process.stdout.read(4096) + if not chunk: + break + for byte in chunk: + c = bytes([byte]) + if c in (b'\r', b'\n'): + if progress_callback and buf: + match = re.search(rb'(\d+)%', buf) + if match: + progress_callback(int(match.group(1)) / 100.0) + buf = b'' + else: + buf += c + process.wait() + if process.returncode != 0: + raise subprocess.CalledProcessError(process.returncode, call) - def start_thread(self, call): - t = threading.Thread(target=self.do_subprocess, kwargs={'call': call}) + def start_thread(self, call, progress_callback=None): + t = threading.Thread(target=self.do_subprocess, kwargs={'call': call, 'progress_callback': progress_callback}) t.start()