qubes-installer-qubes-os/anaconda/pyanaconda/ui/gui/hubs/progress.py

299 lines
11 KiB
Python
Raw Normal View History

# Progress hub classes
#
# Copyright (C) 2011-2013 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
# Red Hat Author(s): Chris Lumens <clumens@redhat.com>
#
import gi
gi.require_version("GLib", "2.0")
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk
import itertools
import os
import sys
import glob
from pyanaconda.i18n import _, C_
from pyanaconda.localization import find_best_locale_match
from pyanaconda.product import productName
from pyanaconda.flags import flags
from pyanaconda import iutil
from pyanaconda.constants import THREAD_INSTALL, THREAD_CONFIGURATION, DEFAULT_LANG, IPMI_FINISHED
from pykickstart.constants import KS_SHUTDOWN, KS_REBOOT
from pyanaconda.ui.gui.hubs import Hub
from pyanaconda.ui.gui.utils import gtk_action_nowait, gtk_call_once
__all__ = ["ProgressHub"]
class ProgressHub(Hub):
"""
.. inheritance-diagram:: ProgressHub
:parts: 3
"""
builderObjects = ["progressWindow"]
mainWidgetName = "progressWindow"
uiFile = "hubs/progress.glade"
helpFile = "ProgressHub.xml"
def __init__(self, data, storage, payload, instclass):
Hub.__init__(self, data, storage, payload, instclass)
self._totalSteps = 0
self._currentStep = 0
self._configurationDone = False
self._rnotes_id = None
def _do_configuration(self, widget=None, reenable_ransom=True):
from pyanaconda.install import doConfiguration
from pyanaconda.threads import threadMgr, AnacondaThread
assert self._configurationDone == False
self._configurationDone = True
# Disable all personalization spokes
self.builder.get_object("progressWindow-scroll").set_sensitive(False)
if reenable_ransom:
self._start_ransom_notes()
self._restart_spinner()
GLib.timeout_add(250, self._update_progress, self._configuration_done)
threadMgr.add(AnacondaThread(name=THREAD_CONFIGURATION, target=doConfiguration,
args=(self.storage, self.payload, self.data, self.instclass)))
def _start_ransom_notes(self):
# Adding this as a timeout below means it'll get called after 60
# seconds, so we need to do the first call manually.
self._cycle_rnotes()
self._rnotes_id = GLib.timeout_add_seconds(60, self._cycle_rnotes)
def _update_progress(self, callback=None):
from pyanaconda.progress import progressQ
import queue
q = progressQ.q
# Grab all messages may have appeared since last time this method ran.
while True:
# Attempt to get a message out of the queue for how we should update
# the progress bar. If there's no message, don't error out.
try:
(code, args) = q.get(False)
except queue.Empty:
break
if code == progressQ.PROGRESS_CODE_INIT:
self._init_progress_bar(args[0])
elif code == progressQ.PROGRESS_CODE_STEP:
self._step_progress_bar()
elif code == progressQ.PROGRESS_CODE_MESSAGE:
self._update_progress_message(args[0])
elif code == progressQ.PROGRESS_CODE_COMPLETE:
q.task_done()
# we are done, stop the progress indication
gtk_call_once(self._progressBar.set_fraction, 1.0)
gtk_call_once(self._progressLabel.set_text, _("Complete!"))
gtk_call_once(self._spinner.stop)
gtk_call_once(self._spinner.hide)
if callback:
callback()
# There shouldn't be any more progress bar updates, so return False
# to indicate this method should be removed from the idle loop.
return False
elif code == progressQ.PROGRESS_CODE_QUIT:
sys.exit(args[0])
q.task_done()
return True
def _configuration_done(self):
# Configuration done, remove ransom notes timer
# and switch to the Reboot page
GLib.source_remove(self._rnotes_id)
self._progressNotebook.set_current_page(1)
self.window.set_may_continue(True)
iutil.ipmi_report(IPMI_FINISHED)
# kickstart install, continue automatically if reboot or shutdown selected
if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
self.window.emit("continue-clicked")
def _install_done(self):
# package installation done, check personalization spokes
# and start the configuration step if all is ready
if not self._inSpoke and self.continuePossible:
self._do_configuration(reenable_ransom=False)
else:
# some mandatory spokes are not ready
# switch to configure and finish page
GLib.source_remove(self._rnotes_id)
self._progressNotebook.set_current_page(0)
def _do_globs(self, path):
return glob.glob(path + "/*.png") + \
glob.glob(path + "/*.jpg") + \
glob.glob(path + "/*.svg")
def _get_rnotes(self):
# We first look for rnotes in paths containing the language, then in
# directories without the language component. You know, just in case.
paths = ["/tmp/updates/pixmaps/rnotes/",
"/tmp/product/pixmaps/rnotes/",
"/usr/share/anaconda/pixmaps/rnotes/"]
all_lang_pixmaps = []
for path in paths:
all_lang_pixmaps += self._do_globs(path + "/*")
pixmap_langs = [pixmap.split(os.path.sep)[-2] for pixmap in all_lang_pixmaps]
best_lang = find_best_locale_match(os.environ["LANG"], pixmap_langs)
if not best_lang:
# nothing found, try the default language
best_lang = find_best_locale_match(DEFAULT_LANG, pixmap_langs)
if not best_lang:
# nothing found even for the default language, try non-localized rnotes
non_localized = []
for path in paths:
non_localized += self._do_globs(path)
return non_localized
best_lang_pixmaps = []
for path in paths:
best_lang_pixmaps += self._do_globs(path + best_lang)
return best_lang_pixmaps
def _cycle_rnotes(self):
# Change the ransom notes image every minute by grabbing the next
# image's filename. Note that self._rnotesPages is an infinite list,
# so this will cycle through the images indefinitely.
try:
nxt = next(self._rnotesPages)
except StopIteration:
# there are no rnotes
pass
else:
self._progressNotebook.set_current_page(nxt)
return True
def initialize(self):
Hub.initialize(self)
if flags.livecdInstall:
continueText = self.builder.get_object("rebootLabel")
continueText.set_text(_("%s is now successfully installed on your system and ready "
"for you to use! When you are ready, reboot your system to start using it!"))
continueText.set_line_wrap(True)
self.window.get_continue_button().set_label(C_("GUI|Progress", "_Quit"))
self._progressBar = self.builder.get_object("progressBar")
self._progressLabel = self.builder.get_object("progressLabel")
self._progressNotebook = self.builder.get_object("progressNotebook")
self._spinner = self.builder.get_object("progressSpinner")
lbl = self.builder.get_object("configurationLabel")
lbl.set_text(_("%s is now successfully installed, but some configuration still needs to be done.\n"
"Finish it and then click the Finish configuration button please.") %
productName)
lbl = self.builder.get_object("rebootLabel")
lbl.set_text(_("%s is now successfully installed and ready for you to use!\n"
"Go ahead and reboot to start using it!") % productName)
rnotes = self._get_rnotes()
# Get the start of the pages we're about to add to the notebook
rnotes_start = self._progressNotebook.get_n_pages()
if rnotes:
# Add a new page in the notebook for each ransom note image.
for f in rnotes:
img = Gtk.Image.new_from_file(f)
img.show()
self._progressNotebook.append_page(img, None)
# An infinite list of the page numbers containing ransom notes images.
self._rnotesPages = itertools.cycle(range(rnotes_start,
self._progressNotebook.get_n_pages()))
else:
# Add a blank page to the notebook and we'll just cycle to that
# over and over again.
blank = Gtk.Box()
blank.show()
self._progressNotebook.append_page(blank, None)
self._rnotesPages = itertools.cycle([rnotes_start])
def refresh(self):
from pyanaconda.install import doInstall
from pyanaconda.threads import threadMgr, AnacondaThread
Hub.refresh(self)
self._start_ransom_notes()
GLib.timeout_add(250, self._update_progress, self._install_done)
threadMgr.add(AnacondaThread(name=THREAD_INSTALL, target=doInstall,
args=(self.storage, self.payload, self.data, self.instclass)))
def _updateContinueButton(self):
if self._configurationDone:
self.window.set_may_continue(self.continuePossible)
else:
self.builder.get_object("configureButton").set_sensitive(self.continuePossible)
def _init_progress_bar(self, steps):
self._totalSteps = steps
self._currentStep = 0
gtk_call_once(self._progressBar.set_fraction, 0.0)
def _step_progress_bar(self):
if not self._totalSteps:
return
self._currentStep += 1
gtk_call_once(self._progressBar.set_fraction, self._currentStep/self._totalSteps)
def _update_progress_message(self, message):
if not self._totalSteps:
return
gtk_call_once(self._progressLabel.set_text, message)
@gtk_action_nowait
def _restart_spinner(self):
self._spinner.show()
self._spinner.start()