progress: Some internal API cleanup
* Simplify start() and end() function signature * Drop use of 'basename' and standardize on 'text' * Add vmmMeter.is_started() * Add vmmMeter.set_text() * Fix asyncjob UI to show text in the progress bar Signed-off-by: Cole Robinson <crobinso@redhat.com>
This commit is contained in:
parent
167d2f2f8e
commit
6659889319
|
@ -1,38 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.36.0 -->
|
||||
<!-- Generated with glade 3.38.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<object class="GtkWindow" id="vmm-progress">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">12</property>
|
||||
<property name="title" translatable="yes">Operation in progress</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">300</property>
|
||||
<property name="default_height">200</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="urgency_hint">True</property>
|
||||
<property name="window-position">center-on-parent</property>
|
||||
<property name="default-width">300</property>
|
||||
<property name="default-height">200</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<property name="skip-taskbar-hint">True</property>
|
||||
<property name="urgency-hint">True</property>
|
||||
<property name="deletable">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox13">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox50">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image99">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="margin_start">3</property>
|
||||
<property name="margin_end">3</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="margin-start">3</property>
|
||||
<property name="margin-end">3</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
<property name="stock">gtk-dialog-info</property>
|
||||
<property name="icon_size">6</property>
|
||||
</object>
|
||||
|
@ -45,12 +45,12 @@
|
|||
<child>
|
||||
<object class="GtkLabel" id="pbar-text">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="label" translatable="yes">Please wait a few moments...</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="max_width_chars">50</property>
|
||||
<property name="max-width-chars">50</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -67,14 +67,14 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="warning-box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="warning-icon">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">3</property>
|
||||
<property name="margin_end">3</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">3</property>
|
||||
<property name="margin-end">3</property>
|
||||
<property name="stock">gtk-dialog-warning</property>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -85,12 +85,12 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="warning-text">
|
||||
<property name="width_request">400</property>
|
||||
<property name="width-request">400</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">some warning</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="max_width_chars">40</property>
|
||||
<property name="max-width-chars">40</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -108,13 +108,13 @@
|
|||
<child>
|
||||
<object class="GtkLabel" id="pbar-stage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="margin_start">3</property>
|
||||
<property name="margin_end">3</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="margin_bottom">3</property>
|
||||
<property name="margin-start">3</property>
|
||||
<property name="margin-end">3</property>
|
||||
<property name="margin-top">3</property>
|
||||
<property name="margin-bottom">3</property>
|
||||
<property name="label" translatable="yes">Processing...</property>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -126,8 +126,9 @@
|
|||
<child>
|
||||
<object class="GtkProgressBar" id="pbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="pulse_step">0.10000000149</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="pulse-step">0.10000000149</property>
|
||||
<property name="show-text">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -138,21 +139,21 @@
|
|||
<child>
|
||||
<object class="GtkBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel-async-job">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_async_job_cancel_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
@ -160,21 +161,21 @@
|
|||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="details">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="resize_toplevel">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="resize-toplevel">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="details-box">
|
||||
<property name="width_request">380</property>
|
||||
<property name="height_request">200</property>
|
||||
<property name="width-request">380</property>
|
||||
<property name="height-request">200</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
@ -183,10 +184,10 @@
|
|||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">_Details</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="track-visited-links">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -198,8 +199,5 @@
|
|||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -17,47 +17,61 @@ import virtinst.progress
|
|||
from .baseclass import vmmGObjectUI
|
||||
|
||||
|
||||
class vmmMeter(virtinst.progress.BaseMeter):
|
||||
def __init__(self, cb_pulse, cb_fraction, cb_done):
|
||||
class _vmmMeter(virtinst.progress.BaseMeter):
|
||||
def __init__(self, pbar_pulse, pbar_fraction, pbar_done):
|
||||
virtinst.progress.BaseMeter.__init__(self)
|
||||
self.started = False
|
||||
|
||||
self._vmm_pulse = cb_pulse
|
||||
self._vmm_fraction = cb_fraction
|
||||
self._vmm_done = cb_done
|
||||
self._pbar_pulse = pbar_pulse
|
||||
self._pbar_fraction = pbar_fraction
|
||||
self._pbar_done = pbar_done
|
||||
self._started = False
|
||||
|
||||
|
||||
def _do_start(self, now=None):
|
||||
text = self.text or self.basename
|
||||
if self.size is None:
|
||||
out = " %5sB" % (0)
|
||||
self._vmm_pulse(out, text)
|
||||
else:
|
||||
out = "%3i%% %5sB" % (0, 0)
|
||||
self._vmm_fraction(0, out, text)
|
||||
self.started = True
|
||||
#################
|
||||
# Internal APIs #
|
||||
#################
|
||||
|
||||
def _do_update(self, amount_read, now=None):
|
||||
text = self.text or self.basename
|
||||
def _write(self, amount_read):
|
||||
fread = virtinst.progress.format_number(amount_read)
|
||||
if self.size is None: # pragma: no cover
|
||||
if self.size is None:
|
||||
out = " %5sB" % (fread)
|
||||
self._vmm_pulse(out, text)
|
||||
self._pbar_pulse(out, self.text)
|
||||
else:
|
||||
frac = self.re.fraction_read()
|
||||
out = "%3i%% %5sB" % (frac * 100, fread)
|
||||
self._vmm_fraction(frac, out, text)
|
||||
self._pbar_fraction(frac, out, self.text)
|
||||
|
||||
def _do_end(self, amount_read, now=None):
|
||||
text = self.text or self.basename
|
||||
fread = virtinst.progress.format_number(amount_read)
|
||||
if self.size is None:
|
||||
out = " %5sB" % (fread)
|
||||
self._vmm_pulse(out, text)
|
||||
else:
|
||||
out = "%3i%% %5sB" % (100, fread)
|
||||
self._vmm_done(out, text)
|
||||
self.started = False
|
||||
|
||||
#############################################
|
||||
# Public APIs specific to virt-manager code #
|
||||
#############################################
|
||||
|
||||
def change_meter_text(self, text):
|
||||
self.text = text
|
||||
self._write(0)
|
||||
|
||||
def is_started(self):
|
||||
return bool(self._started)
|
||||
|
||||
|
||||
###################
|
||||
# Meter overrides #
|
||||
###################
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
self._started = True
|
||||
super().start(*args, **kwargs)
|
||||
self._write(0)
|
||||
|
||||
def update(self, amount_read): # pylint: disable=arguments-differ
|
||||
super().update(amount_read)
|
||||
self._write(amount_read)
|
||||
|
||||
def end(self):
|
||||
self._started = False
|
||||
super().end()
|
||||
self._write(0)
|
||||
self._pbar_done()
|
||||
|
||||
|
||||
def cb_wrapper(callback, asyncjob, *args, **kwargs):
|
||||
|
@ -219,9 +233,9 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||
|
||||
def get_meter(self):
|
||||
if not self._meter:
|
||||
self._meter = vmmMeter(self._pbar_pulse,
|
||||
self._pbar_fraction,
|
||||
self._pbar_done)
|
||||
self._meter = _vmmMeter(self._pbar_pulse,
|
||||
self._pbar_fraction,
|
||||
self._pbar_done)
|
||||
return self._meter
|
||||
|
||||
def set_error(self, error, details):
|
||||
|
@ -308,13 +322,8 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||
self.widget("pbar").set_fraction(frac)
|
||||
|
||||
@idle_wrapper
|
||||
def _pbar_done(self, progress, stage=None):
|
||||
def _pbar_done(self):
|
||||
self._is_pulsing = False
|
||||
if not self.builder:
|
||||
return # pragma: no cover
|
||||
self._set_stage_text(stage or _("Completed"))
|
||||
self.widget("pbar").set_text(progress)
|
||||
self.widget("pbar").set_fraction(1)
|
||||
|
||||
@idle_wrapper
|
||||
def details_enable(self):
|
||||
|
|
|
@ -2079,9 +2079,9 @@ class vmmCreateVM(vmmGObjectUI):
|
|||
import logging
|
||||
import virtBootstrap
|
||||
|
||||
meter.start(text=_("Bootstraping container"), size=100)
|
||||
meter.start(_("Bootstraping container"), 100)
|
||||
def progress_update_cb(prog):
|
||||
meter.text = _(prog['status'])
|
||||
meter.change_meter_text(_(prog['status']))
|
||||
meter.update(prog['value'])
|
||||
|
||||
asyncjob.details_enable()
|
||||
|
|
|
@ -224,12 +224,12 @@ class _vmmDeleteBase(vmmGObjectUI):
|
|||
for path in paths:
|
||||
try:
|
||||
log.debug("Deleting path: %s", path)
|
||||
meter.start(text=_("Deleting path '%s'") % path)
|
||||
meter.start(_("Deleting path '%s'") % path, None)
|
||||
self._async_delete_path(conn, path, meter)
|
||||
except Exception as e:
|
||||
storage_errors.append((str(e),
|
||||
"".join(traceback.format_exc())))
|
||||
meter.end(0)
|
||||
meter.end()
|
||||
return storage_errors
|
||||
|
||||
def _async_delete_path(self, conn, path, ignore):
|
||||
|
|
|
@ -47,9 +47,8 @@ def start_job_progress_thread(vm, meter, progtext):
|
|||
if not data_total:
|
||||
continue # pragma: no cover
|
||||
|
||||
if not meter.started:
|
||||
meter.start(size=data_total,
|
||||
text=progtext)
|
||||
if not meter.is_started():
|
||||
meter.start(progtext, data_total)
|
||||
|
||||
progress = data_total - data_remaining
|
||||
meter.update(progress)
|
||||
|
|
|
@ -411,8 +411,8 @@ class _StorageBase(object):
|
|||
def will_create_storage(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def create(self, progresscb):
|
||||
ignore = progresscb # pragma: no cover
|
||||
def create(self, meter):
|
||||
ignore = meter # pragma: no cover
|
||||
raise xmlutil.DevError(
|
||||
"%s can't create storage" % self.__class__.__name__)
|
||||
|
||||
|
@ -435,7 +435,7 @@ class _StorageCreator(_StorageBase):
|
|||
# Public API #
|
||||
##############
|
||||
|
||||
def create(self, progresscb):
|
||||
def create(self, meter):
|
||||
raise NotImplementedError
|
||||
def validate(self):
|
||||
raise NotImplementedError
|
||||
|
@ -495,8 +495,8 @@ class ManagedStorageCreator(_StorageCreator):
|
|||
self._pool = vol_install.pool
|
||||
self._vol_install = vol_install
|
||||
|
||||
def create(self, progresscb):
|
||||
return self._vol_install.install(meter=progresscb)
|
||||
def create(self, meter):
|
||||
return self._vol_install.install(meter=meter)
|
||||
def is_size_conflict(self):
|
||||
return self._vol_install.is_size_conflict()
|
||||
def validate(self):
|
||||
|
@ -561,16 +561,15 @@ class CloneStorageCreator(_StorageCreator):
|
|||
if msg:
|
||||
log.warning(msg) # pragma: no cover
|
||||
|
||||
def create(self, progresscb):
|
||||
def create(self, meter):
|
||||
text = (_("Cloning %(srcfile)s") %
|
||||
{'srcfile': os.path.basename(self._input_path)})
|
||||
|
||||
size_bytes = int(self.get_size() * 1024 * 1024 * 1024)
|
||||
progresscb.start(filename=self._output_path, size=size_bytes,
|
||||
text=text)
|
||||
meter.start(text, size_bytes)
|
||||
|
||||
# Plain file clone
|
||||
self._clone_local(progresscb, size_bytes)
|
||||
self._clone_local(meter, size_bytes)
|
||||
|
||||
def _clone_local(self, meter, size_bytes):
|
||||
if self._input_path == "/dev/null": # pragma: no cover
|
||||
|
@ -618,7 +617,7 @@ class CloneStorageCreator(_StorageCreator):
|
|||
l = os.read(src_fd, clone_block_size)
|
||||
s = len(l)
|
||||
if s == 0:
|
||||
meter.end(size_bytes)
|
||||
meter.end()
|
||||
break
|
||||
# check sequence of zeros
|
||||
if sparse and zeros == l:
|
||||
|
@ -626,7 +625,7 @@ class CloneStorageCreator(_StorageCreator):
|
|||
else:
|
||||
b = os.write(dst_fd, l)
|
||||
if s != b: # pragma: no cover
|
||||
meter.end(i)
|
||||
meter.end()
|
||||
break
|
||||
i += s
|
||||
if i < size_bytes:
|
||||
|
|
|
@ -104,7 +104,7 @@ class Installer(object):
|
|||
name = os.path.basename(path)
|
||||
|
||||
try:
|
||||
meter.start(size=None, text=_("Removing disk '%s'") % name)
|
||||
meter.start(_("Removing disk '%s'") % name, None)
|
||||
|
||||
if disk.get_vol_object():
|
||||
disk.get_vol_object().delete()
|
||||
|
@ -113,7 +113,7 @@ class Installer(object):
|
|||
# it's here in case future assumptions change
|
||||
os.unlink(path)
|
||||
|
||||
meter.end(0)
|
||||
meter.end()
|
||||
except Exception as e: # pragma: no cover
|
||||
log.debug("Failed to remove disk '%s'",
|
||||
name, exc_info=True)
|
||||
|
@ -634,7 +634,7 @@ class Installer(object):
|
|||
"""
|
||||
meter_label = _("Creating domain...")
|
||||
meter = progress.ensure_meter(meter)
|
||||
meter.start(size=None, text=meter_label)
|
||||
meter.start(meter_label, None)
|
||||
needs_boot = doboot or self.has_install_phase()
|
||||
|
||||
if guest.type == "vz" and not self._is_reinstall:
|
||||
|
@ -655,6 +655,7 @@ class Installer(object):
|
|||
domain.XMLDesc(0))
|
||||
except Exception as e: # pragma: no cover
|
||||
log.debug("Error fetching XML from libvirt object: %s", e)
|
||||
meter.end()
|
||||
return domain
|
||||
|
||||
def _flag_autostart(self, domain):
|
||||
|
|
|
@ -104,11 +104,10 @@ class _URLFetcher(object):
|
|||
|
||||
log.debug("Fetching URI: %s", url)
|
||||
self.meter.start(
|
||||
text=_("Retrieving file %s...") % os.path.basename(filename),
|
||||
size=size)
|
||||
_("Retrieving file %s...") % os.path.basename(filename), size)
|
||||
|
||||
total = self._write(urlobj, fileobj)
|
||||
self.meter.end(total)
|
||||
self._write(urlobj, fileobj)
|
||||
self.meter.end()
|
||||
|
||||
def _write(self, urlobj, fileobj):
|
||||
"""
|
||||
|
|
|
@ -105,8 +105,7 @@ def _upload_file(conn, meter, destpool, src):
|
|||
|
||||
# Start transfer
|
||||
total = 0
|
||||
meter.start(size=size,
|
||||
text=_("Transferring %s") % os.path.basename(src))
|
||||
meter.start(_("Transferring %s") % os.path.basename(src), size)
|
||||
while True:
|
||||
blocksize = 1024 * 1024 # 1 MiB
|
||||
data = fileobj.read(blocksize)
|
||||
|
@ -119,7 +118,7 @@ def _upload_file(conn, meter, destpool, src):
|
|||
|
||||
# Cleanup
|
||||
stream.finish()
|
||||
meter.end(size)
|
||||
meter.end()
|
||||
except Exception: # pragma: no cover
|
||||
vol.delete(0)
|
||||
raise
|
||||
|
|
|
@ -95,7 +95,6 @@ class BaseMeter:
|
|||
def __init__(self):
|
||||
self.update_period = 0.3 # seconds
|
||||
|
||||
self.filename = None
|
||||
self.url = None
|
||||
self.basename = None
|
||||
self.text = None
|
||||
|
@ -106,19 +105,17 @@ class BaseMeter:
|
|||
self.last_update_time = None
|
||||
self.re = RateEstimator()
|
||||
|
||||
def start(self, filename=None, url=None, basename=None,
|
||||
size=None, now=None, text=None):
|
||||
self.filename = filename
|
||||
self.url = url
|
||||
self.basename = basename
|
||||
def set_text(self, text):
|
||||
self.text = text
|
||||
|
||||
def start(self, text, size):
|
||||
self.text = text
|
||||
|
||||
self.size = size
|
||||
if size is not None:
|
||||
self.fsize = format_number(size) + 'B'
|
||||
|
||||
if now is None:
|
||||
now = time.time()
|
||||
now = time.time()
|
||||
self.start_time = now
|
||||
self.re.start(size, now)
|
||||
self.last_amount_read = 0
|
||||
|
@ -143,13 +140,8 @@ class BaseMeter:
|
|||
def _do_update(self, amount_read, now=None):
|
||||
pass
|
||||
|
||||
def end(self, amount_read, now=None):
|
||||
if now is None:
|
||||
now = time.time()
|
||||
self.re.update(amount_read, now)
|
||||
self.last_amount_read = amount_read
|
||||
self.last_update_time = now
|
||||
self._do_end(amount_read, now)
|
||||
def end(self):
|
||||
self._do_end(self.last_amount_read, self.last_update_time)
|
||||
|
||||
def _do_end(self, amount_read, now=None):
|
||||
pass
|
||||
|
|
|
@ -684,8 +684,7 @@ class StorageVolume(_StorageObject):
|
|||
|
||||
try:
|
||||
t.start()
|
||||
meter.start(size=self.capacity,
|
||||
text=_("Allocating '%s'") % self.name)
|
||||
meter.start(_("Allocating '%s'") % self.name, self.capacity)
|
||||
|
||||
if self.conn.is_really_test():
|
||||
# Test suite doesn't support any flags, so reset them
|
||||
|
@ -698,7 +697,7 @@ class StorageVolume(_StorageObject):
|
|||
log.debug("Using vol create flags=%s", createflags)
|
||||
vol = self.pool.createXML(xml, createflags)
|
||||
|
||||
meter.end(self.capacity)
|
||||
meter.end()
|
||||
log.debug("Storage volume '%s' install complete.", self.name)
|
||||
return vol
|
||||
except Exception as e:
|
||||
|
|
Loading…
Reference in New Issue