From 7c64ac7703fc0a3b035a1be20729df9d74c8a111 Mon Sep 17 00:00:00 2001 From: Zaharia Constantin <1303303+soulraven@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:20:32 +0300 Subject: [PATCH] Bump the version to 0.1.8 with some brake changes on progress bar implementation --- CHANGELOG.md | 7 +- README.md | 79 ++++++++++++--------- demo.py | 50 +++++++++----- docs/AUTHORS.md | 5 +- docs/img/basic_dialog.png | Bin 0 -> 10113 bytes docs/img/calendar_dialog.png | Bin 0 -> 20140 bytes docs/img/progressbar.png | Bin 0 -> 4284 bytes pyproject.toml | 2 +- src/sgzenity/SGProgresBar.py | 130 ++++++++++++++++++++++++----------- src/sgzenity/__init__.py | 2 +- src/sgzenity/loading.glade | 52 ++++++++++++++ src/sgzenity/sgszenity.py | 25 +------ src/sgzenity/simpleDialog.py | 1 + src/sgzenity/thread.py | 31 ++++++--- 14 files changed, 258 insertions(+), 126 deletions(-) create mode 100644 docs/img/basic_dialog.png create mode 100644 docs/img/calendar_dialog.png create mode 100644 docs/img/progressbar.png create mode 100644 src/sgzenity/loading.glade diff --git a/CHANGELOG.md b/CHANGELOG.md index 130e19f..dd5a993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ ### @TODO: - improve the code -- change the interfaces and reimplement the callback method + + +## Version 0.1.8 +- BRAKE: the mode how the ProgressBar is reimplemented will brake the old code. Use the example from README.md +- reword on the progress bar and threads. For now seams to work OK +- small fixes for documentation and added some images ## Version 0.1.7 - refactored all the code diff --git a/README.md b/README.md index f45e099..2f01afa 100644 --- a/README.md +++ b/README.md @@ -36,23 +36,6 @@ $ python setup.py install Simple dialog: -```python -from src.sgzenity import calendar - -result = calendar(title="Awesome Calendar", text="Your birthday ?") -print(result) -``` -This code show a calendar dialog : - -![dialog_01](docs/img/screen_01.png) - -And display the result : - -```bash -$ python test.py -$ (year=2017, month=6, day=4) -``` - ## API ### Simple message @@ -97,6 +80,9 @@ warning(title='', text='', width=330, height=120, timeout=None) >* **timeout** (*int*) – close the window after n seconds ### Question + +![basic_dialog_01](docs/img/basic_dialog.png) + ```python question(title='', text='', width=330, height=120, timeout=None) ``` @@ -114,6 +100,9 @@ question(title='', text='', width=330, height=120, timeout=None) >_Return type_: bool ### Progress Bar + +![basic_dialog_01](docs/img/progressbar.png) + ```python progress_bar(title, text, pulse_mode, callback) ``` @@ -128,21 +117,40 @@ progress_bar(title, text, pulse_mode, callback) ### Demo + ```python -def callback_progress_bar(fraction=None): - global counter - counter += 0.01 - if counter <= _max: - return counter - return True - -def demo_progress_bar(): - progress = progress_bar( - "DEMO TITLE", "DEMO TEXT", False, callback_progress_bar, 350, 30, 10 - ) - progress.run_progressbar() - -demo_progress_bar() +import time +from sgzenity.thread import WorkerThread +from sgzenity import ProgressBar + +class WorkingThread(WorkerThread): + def payload(self): + loading = self.data + steps = 10 + for s in range(steps): + if self.stop: + break + loading.heartbeat() + print('Pulse {}.'.format(s)) + time.sleep(1) + if self.stop: + print('Working thread canceled.') + else: + print('Working thread ended.') + loading.close() + + +def sg_progress_bar(): + loading = ProgressBar("DEMO TITLE", "DEMO TEXT", pulse_mode=True) + + workthread = WorkingThread(loading) + loading.show(workthread) + workthread.start() + + Gtk.main() + + +sg_progress_bar() ``` ### Entry @@ -244,6 +252,15 @@ calendar(text='', day=None, month=None, title='', width=330, height=120, timeout > >_Return type_: tuple +![calendar_dialog_01](docs/img/calendar_dialog.png) + +And display the result : + +```bash +$ python demo.py +$ (year=2017, month=6, day=4) +``` + ### Color selection ```python diff --git a/demo.py b/demo.py index f847665..eb28fe9 100644 --- a/demo.py +++ b/demo.py @@ -1,37 +1,51 @@ #! /usr/bin/env python3 # -*- coding:utf-8 -*- -import random + import time import gi gi.require_version("Gtk", "3.0") -from gi.repository import GLib, Gtk +from gi.repository import GLib, GObject, Gtk + +from src.sgzenity.SGProgresBar import ProgressBar +from src.sgzenity.sgszenity import calendar, question +from src.sgzenity.thread import WorkerThread -from src.sgzenity.sgszenity import progress_bar, question -counter = 0 -_max = 1 +class WorkingThread(WorkerThread): + def payload(self): + loading = self.data + steps = 10 + for s in range(steps): + if self.stop: + break + loading.heartbeat() + print('Pulse {}.'.format(s)) + time.sleep(1) + if self.stop: + print('Working thread canceled.') + else: + print('Working thread ended.') + loading.close() -def callback_progress_bar(fraction=None): - global counter - counter += 0.01 - if counter <= _max: - return counter - return True +def sg_progress_bar(): + loading = ProgressBar("DEMO TITLE", "DEMO TEXT", pulse_mode=True) -def demo_progress_bar(): - progress = progress_bar( - "DEMO TITLE", "DEMO TEXT", False, callback_progress_bar, 350, 30, 10 - ) - progress.run_progressbar() + workthread = WorkingThread(loading) + loading.show(workthread) + workthread.start() + Gtk.main() -demo_progress_bar() + +sg_progress_bar() _error = question( "something went wrong", text="Some big text in small space", height=150, width=400 ) -# print(_error) + +_calendar = calendar("DEMO CALENDAR") +print(_calendar) diff --git a/docs/AUTHORS.md b/docs/AUTHORS.md index 7ed4bf2..afa5ddf 100644 --- a/docs/AUTHORS.md +++ b/docs/AUTHORS.md @@ -1,2 +1,5 @@ -Authors\n=======\n\nA huge thanks to all of our contributors:\n +Authors +======= + +A huge thanks to all of our contributors: - Zaharia Constantin diff --git a/docs/img/basic_dialog.png b/docs/img/basic_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..d9d6d79dadcdd947742704bbfc641aad91fa897c GIT binary patch literal 10113 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 Hng{E)fRkH*^OL|Q&X3F-TpS$oOE-vuKO)IW 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 literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index 6eea080..844e647 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "sgzenity" -version = "0.1.7" +version = "0.1.8" description = "sgzentry is a library for python which was inspired by Zenity. When you write scripts, you can use sgzentry to create simple dialogs that interact graphically with the user." homepage="https://github.com/SoftGeekRO/sgzenity" repository="https://github.com/SoftGeekRO/sgzenity.git" diff --git a/src/sgzenity/SGProgresBar.py b/src/sgzenity/SGProgresBar.py index 04d27cb..01a95a8 100644 --- a/src/sgzenity/SGProgresBar.py +++ b/src/sgzenity/SGProgresBar.py @@ -7,77 +7,129 @@ from gi.repository import GLib, GObject, Gtk -from .base import Base from .thread import WorkerThread +DEFAULT_WIDTH = 300 +DEFAULT_HEIGHT = 60 +BORDER_WIDTH = 10 -class SGProgressBar(Base): - def __init__(self, title, text, pulse_mode=False, callback=None, *args, **kwargs): +class ProgressBar(Gtk.Window): + + def __init__( + self, + title, + text=None, + pulse_mode=True, + width=DEFAULT_WIDTH, + height=DEFAULT_HEIGHT, + border_width=BORDER_WIDTH, + parent=None, + **kwargs, + ): super().__init__(**kwargs) self.title = title self.text = text - self.show_text = True if self.text else False self.pulse_mode = pulse_mode - self.callback = callback - - self.dialog = Gtk.ProgressBar() - self.vbox.pack_start(self.dialog, True, True, 0) + self.border_width = border_width + self.width = width + self.height = height + self.pulses = 10 + self._count = 0 + self.workthread = None - self.timeout_id = GLib.timeout_add(50, self.update_progress, None) + # Create the GUI + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + self.add(vbox) - self.init_progressbar() + self.progressbar = Gtk.ProgressBar() + vbox.pack_start(self.progressbar, True, True, 0) - # Start the background thread. - self.worker = WorkerThread(self) - self.worker.start() + self.timeout_id = GLib.timeout_add(50, self.on_timeout, None) - def run_progressbar(self): - GObject.threads_init() - self.show_all() - Gtk.main() + self.connect("destroy", self.cancel) - def refresh_in_thread(self): - GObject.idle_add(self.update_progress) + self.init() - def init_progressbar(self): - # global config for progress bar - self.set_border_width(10) + def init(self): + self.set_border_width(self.border_width) self.set_resizable(False) self.set_default_size(self.width, self.height) if self.pulse_mode: - self.dialog.pulse() + self.progressbar.pulse() else: - self.dialog.set_fraction(0.0) + self.progressbar.set_fraction(0.0) if self.title: self.set_title(self.title) - if self.show_text: - self.dialog.set_text(self.text) - self.dialog.set_show_text(self.show_text) + if self.text: + self.progressbar.set_text(self.text) + self.progressbar.set_show_text(True if self.text else False) - self.connect("destroy", self._destroy) + self.connect("destroy", self.cancel) - def update_progress(self, progress=None): - """ + def show(self, workthread): + """Show loading window. + This needs to be called frm Gtk main thread. + + Show the loading dialog just before starting the workthread. + :param workthread: :return: """ + if self.workthread is not None: + print( + 'There is a workthread active. Please call close() or cancel() before starting a new loading event.' + ) + return False - callback = self.callback() if callable(self.callback) else None - if callback >= 1: - time.sleep(0.1) - self._destroy() + if workthread is not None: + if not isinstance(workthread, WorkerThread): + raise Exception('The thread needs to be a subclass of WorkingThread.') + self.workthread = workthread + self.show_all() + + return False + + def on_timeout(self, progress=None): if self.pulse_mode: - self.dialog.pulse() + self.progressbar.pulse() else: - self.dialog.set_fraction(callback) - return False + self.progressbar.set_fraction(self._count) + return self.pulse_mode + + def heartbeat(self, text=None): - def _destroy(self, widget=None): - self.worker.done = True + if self.pulse_mode: + return False + + self._count += 0.1 + + if text is None: + text = '{0:0.1f}%'.format(self._count * 100) + + GLib.idle_add(self.progressbar.set_fraction, self._count) + GLib.idle_add(self.progressbar.set_text, text) + + def close(self): + """Close the loading window. + + This should be called when the workthread has finished it's work. + This can be called outside the Gtk main thread. + """ + self.workthread = None Gtk.main_quit() + + def cancel(self, widget=None): + """Close the loading window. + + This should be called when the workthread has finished it's work. + This can be called outside the Gtk main thread. + """ + if self.workthread is not None: + self.workthread.cancel() + self.close() diff --git a/src/sgzenity/__init__.py b/src/sgzenity/__init__.py index 4458d26..1f00bcc 100644 --- a/src/sgzenity/__init__.py +++ b/src/sgzenity/__init__.py @@ -1,4 +1,5 @@ from .sgszenity import ( + ProgressBar, calendar, color_selection, entry, @@ -6,7 +7,6 @@ file_selection, message, password, - progress_bar, question, scale, sglist, diff --git a/src/sgzenity/loading.glade b/src/sgzenity/loading.glade new file mode 100644 index 0000000..6bfa9bd --- /dev/null +++ b/src/sgzenity/loading.glade @@ -0,0 +1,52 @@ + + + + + False + 10 + popup + Please wait... + True + center-always + True + dialog + True + True + True + False + False + + + True + False + 5 + + + True + False + <span size="x-large">Processing. Please wait...</span> + True + + + True + True + 0 + + + + + 50 + True + False + True + + + False + True + 1 + + + + + + diff --git a/src/sgzenity/sgszenity.py b/src/sgzenity/sgszenity.py index 52c72ce..a94e44e 100644 --- a/src/sgzenity/sgszenity.py +++ b/src/sgzenity/sgszenity.py @@ -1,39 +1,18 @@ #! /usr/bin/env python3 # -*- coding:utf-8 -*- -from .base import DEFAULT_HEIGHT, DEFAULT_WIDTH, ZLIST_HEIGHT, Gtk +from .base import DEFAULT_HEIGHT, DEFAULT_WIDTH, ZLIST_HEIGHT, GObject, Gtk from .SGCalendar import SGCalendar from .SGColorSelection import SGColorSelection from .SGEntryMessage import SGEntryMessage from .SGEntryPassword import SGEntryPassword from .SGFileSection import SGFileSelection from .SGList import SGList -from .SGProgresBar import SGProgressBar +from .SGProgresBar import ProgressBar from .SGScale import SGScale from .simpleDialog import SGSimpleDialog -def progress_bar( - title, - text, - pulse_mode, - callback=None, - width=DEFAULT_WIDTH, - height=DEFAULT_HEIGHT, - timeout=None, -): - progress = SGProgressBar( - title, - text, - pulse_mode, - callback, - width=width, - height=height, - timeout=timeout, - ) - return progress - - def _simple_dialog(dialog_type, text, title, width, height, timeout): dialog = SGSimpleDialog(dialog_type, text, title, width, height, timeout) dialog.run() diff --git a/src/sgzenity/simpleDialog.py b/src/sgzenity/simpleDialog.py index 90f2e4e..c95adac 100644 --- a/src/sgzenity/simpleDialog.py +++ b/src/sgzenity/simpleDialog.py @@ -26,6 +26,7 @@ def __init__(self, dialog_type, text, *args, **kwargs): message_format=None, ) self.vbox.pack_start(self.dialog, True, True, 0) + self.init_dialog() def init_dialog(self): diff --git a/src/sgzenity/thread.py b/src/sgzenity/thread.py index 76b0b56..416e169 100644 --- a/src/sgzenity/thread.py +++ b/src/sgzenity/thread.py @@ -5,18 +5,27 @@ class WorkerThread(threading.Thread): - def __init__(self, delegate): + def __init__(self, data=None): threading.Thread.__init__(self) - self.delegate = delegate - self.done = False + self.data = data + self.stop = False def run(self): - while True: - if self.done: - print('Background thread shutting down cleanly') - break - if hasattr(self.delegate, "refresh_in_thread"): - self.delegate.refresh_in_thread() + # while True: + # if self.done: + # print('Background thread shutting down cleanly') + # break + # self.delegate() + # # Sleep for a little bit ... + # time.sleep(random.uniform(0.01, 0.1)) + self.payload() - # Sleep for a little bit ... - time.sleep(random.uniform(0.01, 0.1)) + def cancel(self): + """Cancel the execution task + + :return: + """ + self.stop = True + + def payload(self): + raise Exception('Please subclass and implement WorkingThread.payload()')