Skip to content

Instantly share code, notes, and snippets.

@carlos-jenkins
Last active April 5, 2020 15:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save carlos-jenkins/5358445 to your computer and use it in GitHub Desktop.
Save carlos-jenkins/5358445 to your computer and use it in GitHub Desktop.
Simple example to use Gtk.ProgressBar with Python 2.7 and PyGObject.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkWindow" id="wait">
<property name="can_focus">False</property>
<property name="border_width">10</property>
<property name="type">popup</property>
<property name="title" translatable="yes">Please wait...</property>
<property name="modal">True</property>
<property name="window_position">center-always</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="urgency_hint">True</property>
<property name="decorated">False</property>
<property name="deletable">False</property>
<child>
<object class="GtkVBox" id="body">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span size="x-large"&gt;Processing. Please wait...&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="progress">
<property name="height_request">50</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="show_text">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHButtonBox" id="actions">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="cancel">
<property name="label">gtk-cancel</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="cancel" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
# -*- coding:utf-8 -*-
from __future__ import division
import threading
from gi.repository import Gtk, GObject
class WorkingThread(threading.Thread):
"""
Working thread subclass.
"""
def __init__(self, data=None):
"""
Object constructor.
"""
super(WorkingThread, self).__init__()
self.stop = False
self.data = data
def cancel(self):
"""
Request for a cancelation of the executing task.
"""
self.stop = True
def run(self):
"""
Override threading.Thread dummy run().
"""
self.payload()
def payload(self):
"""
This function do the heavy work.
Please override on subclasses.
This function can use self.stop to know if a cancel was requested, also
it can use self.data for any data it needs. self.data is set in the
constructor when creating the thread.
"""
raise Exception('Please subclass and implement WorkingThread.payload()')
class LoadingWindow(object):
"""
Show and handle a loading window.
"""
def __init__(self, parent=None, label=None):
"""
The object constructor.
"""
# Create the GUI
builder = Gtk.Builder()
builder.add_from_file('loading.glade')
# Get the main objects
self.wait = builder.get_object('wait')
self.label = builder.get_object('label')
self.progress = builder.get_object('progress')
# Configure object
self.pulses = 1
self._count = 0
self.workthread = None
if parent is not None:
self.wait.set_transient_for(parent)
if label is not None:
self.label.set_markup(label)
# Connect signals
builder.connect_signals(self)
def show(self, pulses, workthread):
"""
Show loading window.
This needs to be called from Gtk main thread. Show the loading dialog
just before starting the workthread.
"""
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
if workthread is not None:
if not isinstance(workthread, WorkingThread):
raise Exception(
'The thread needs to be a subclass of WorkingThread.'
)
self.workthread = workthread
self.pulses = max(pulses, 1)
self._count = 0
self.progress.set_fraction(0.0)
self.progress.set_text('')
self.wait.show()
return False
def pulse(self, text=None):
"""
Pulse one step forward the progress bar.
This can be called outside the Gtk main thread.
"""
self._count += 1
fraction = min(1.0, self._count / self.pulses)
if text is None:
text = '{0:0.1f}%'.format(fraction*100)
GObject.idle_add(self.progress.set_fraction, fraction)
GObject.idle_add(self.progress.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
GObject.idle_add(self.wait.hide)
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()
# -*- coding:utf-8 -*-
import time
from loading import WorkingThread, LoadingWindow
from gi.repository import Gtk, GObject
class MyWorkingThread(WorkingThread):
def payload(self):
loading = self.data
for s in range(steps):
if self.stop:
break
loading.pulse()
print('Pulse {}.'.format(s))
time.sleep(0.1)
if self.stop:
print('Working thread canceled.')
else:
print('Working thread ended.')
loading.close()
if __name__ == '__main__':
GObject.threads_init()
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.set_title('Loading Test')
window.connect('delete-event', lambda x,y: Gtk.main_quit())
window.set_default_size(200, 100)
window.set_position(Gtk.WindowPosition.CENTER)
loading = LoadingWindow(window)
steps = 100
def _launch_work(widget):
workthread = MyWorkingThread(loading)
loading.show(steps, workthread)
workthread.start()
button = Gtk.Button('Click me to start working.')
button.connect('clicked', _launch_work)
window.add(button)
window.show_all()
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment