ca061d3607
svn: r13720
209 lines
6.5 KiB
Python
209 lines
6.5 KiB
Python
"""
|
|
This module provides a progess dialog for displaying the status of
|
|
long running operations.
|
|
"""
|
|
|
|
import gtk
|
|
|
|
class _GtkProgressBar(gtk.VBox):
|
|
"""This widget displays the progress bar and labels for a progress
|
|
indicator. It provides an interface to updating the progress bar.
|
|
"""
|
|
|
|
def __init__(self, long_op_status):
|
|
""":param long_op_status: the status of the operation.
|
|
:type long_op_status: L{gen.utils.LongOpStatus}
|
|
"""
|
|
gtk.VBox.__init__(self)
|
|
|
|
msg = long_op_status.get_msg()
|
|
self._old_val = -1
|
|
self._lbl = gtk.Label(msg)
|
|
self._lbl.set_use_markup(True)
|
|
#self.set_border_width(24)
|
|
|
|
self._pbar = gtk.ProgressBar()
|
|
self._hbox = gtk.HBox()
|
|
|
|
# Only display the cancel button is the operation
|
|
# can be canceled.
|
|
if long_op_status.can_cancel():
|
|
self._cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
|
|
self._cancel.connect("clicked",
|
|
lambda x: long_op_status.cancel())
|
|
self._cancel.show()
|
|
self._hbox.pack_end(self._cancel)
|
|
|
|
self._hbox.pack_start(self._pbar)
|
|
|
|
self.pack_start(self._lbl, expand=False, fill=False)
|
|
self.pack_start(self._hbox, expand=False, fill=False)
|
|
|
|
|
|
self._pbar_max = (long_op_status.get_total_steps()/
|
|
long_op_status.get_interval())
|
|
self._pbar_index = 0.0
|
|
self._pbar.set_fraction(((100/float(long_op_status.get_total_steps())*
|
|
float(long_op_status.get_interval())))/
|
|
100.0)
|
|
|
|
if msg != '':
|
|
self._lbl.show()
|
|
self._pbar.show()
|
|
self._hbox.show()
|
|
|
|
def step(self):
|
|
"""Move the progress bar on a step.
|
|
"""
|
|
self._pbar_index = self._pbar_index + 1.0
|
|
|
|
if self._pbar_index > self._pbar_max:
|
|
self._pbar_index = self._pbar_max
|
|
|
|
try:
|
|
val = int(100*self._pbar_index/self._pbar_max)
|
|
except ZeroDivisionError:
|
|
val = 0
|
|
|
|
if val != self._old_val:
|
|
self._pbar.set_text("%d%%" % val)
|
|
self._pbar.set_fraction(val/100.0)
|
|
self._pbar.old_val = val
|
|
|
|
class GtkProgressDialog(gtk.Dialog):
|
|
"""A gtk window to display the status of a long running
|
|
process."""
|
|
|
|
def __init__(self, window_params, title):
|
|
""":param title: The title to display on the top of the window.
|
|
:type title: string
|
|
"""
|
|
gtk.Dialog.__init__(self, *window_params)
|
|
self.connect('delete_event', self._warn)
|
|
self.set_has_separator(False)
|
|
self.set_title(title)
|
|
#self.set_resize_mode(gtk.RESIZE_IMMEDIATE)
|
|
#self.show()
|
|
|
|
self._progress_bars = []
|
|
|
|
def add(self, long_op_status):
|
|
"""Add a new status object to the progress dialog.
|
|
|
|
:param long_op_status: the status object.
|
|
:type long_op_status: L{gen.utils.LongOpStatus}
|
|
:returns: a key that can be used as the L{pbar_idx}
|
|
to the other methods.
|
|
:rtype: int
|
|
"""
|
|
pbar = _GtkProgressBar(long_op_status)
|
|
|
|
self.vbox.pack_start(pbar, expand=False, fill=False)
|
|
|
|
pbar.show()
|
|
# this seems to cause an infinite loop:
|
|
#self.resize_children()
|
|
|
|
self._progress_bars.append(pbar)
|
|
# This is a bad idea; could cause deletes while adding:
|
|
#self._process_events()
|
|
return len(self._progress_bars)-1
|
|
|
|
def remove(self, pbar_idx):
|
|
"""Remove the specified status object from the progress dialog.
|
|
|
|
:param pbar_idx: the index as returned from L{add}
|
|
:type pbar_idx: int
|
|
"""
|
|
if pbar_idx is not None:
|
|
pbar = self._progress_bars[pbar_idx]
|
|
self.vbox.remove(pbar)
|
|
del self._progress_bars[pbar_idx]
|
|
|
|
def step(self, pbar_idx):
|
|
"""Click the progress bar over to the next value. Be paranoid
|
|
and insure that it doesn't go over 100%.
|
|
|
|
:param pbar_idx: the index as returned from L{add}
|
|
:type pbar_idx: int
|
|
"""
|
|
if pbar_idx < len(self._progress_bars):
|
|
self._progress_bars[pbar_idx].step()
|
|
self._process_events()
|
|
|
|
def _process_events(self):
|
|
while gtk.events_pending():
|
|
gtk.main_iteration()
|
|
|
|
def show(self):
|
|
"""Show the dialog and process any events.
|
|
"""
|
|
gtk.Dialog.show(self)
|
|
self._process_events()
|
|
|
|
def hide(self):
|
|
"""Hide the dialog and process any events.
|
|
"""
|
|
gtk.Dialog.hide(self)
|
|
self._process_events()
|
|
|
|
def _warn(self, x, y):
|
|
return True
|
|
|
|
def close(self):
|
|
self.destroy()
|
|
|
|
if __name__ == '__main__':
|
|
import time
|
|
from gen.utils import LongOpStatus, ProgressMonitor
|
|
|
|
def test(a, b):
|
|
d = ProgressMonitor(GtkProgressDialog)
|
|
|
|
s = LongOpStatus("Doing very long operation", 100, 10, can_cancel=True)
|
|
|
|
d.add_op(s)
|
|
|
|
for i in xrange(0, 99):
|
|
if s.should_cancel():
|
|
break
|
|
time.sleep(0.1)
|
|
if i == 30:
|
|
t = LongOpStatus("doing a shorter one", 100, 10,
|
|
can_cancel=True)
|
|
d.add_op(t)
|
|
for j in xrange(0, 99):
|
|
if s.should_cancel():
|
|
t.cancel()
|
|
break
|
|
if t.should_cancel():
|
|
break
|
|
time.sleep(0.1)
|
|
t.heartbeat()
|
|
if not t.was_cancelled():
|
|
t.end()
|
|
if i == 60:
|
|
t = LongOpStatus("doing another shorter one", 100, 10)
|
|
d.add_op(t)
|
|
for j in xrange(0, 99):
|
|
if s.should_cancel():
|
|
t.cancel()
|
|
break
|
|
time.sleep(0.1)
|
|
t.heartbeat()
|
|
t.end()
|
|
s.heartbeat()
|
|
if not s.was_cancelled():
|
|
s.end()
|
|
|
|
w = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
|
w.connect('destroy', gtk.main_quit)
|
|
button = gtk.Button("Test")
|
|
button.connect("clicked", test, None)
|
|
w.add(button)
|
|
button.show()
|
|
w.show()
|
|
gtk.main()
|
|
print 'done'
|
|
|