249 lines
9.7 KiB
Python
249 lines
9.7 KiB
Python
# DistUpgradeFetcherKDE.py
|
|
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
|
|
#
|
|
# Copyright (c) 2008 Canonical Ltd
|
|
# Copyright (c) 2014-2018 Harald Sitter <sitter@kde.org>
|
|
#
|
|
# Author: Jonathan Riddell <jriddell@ubuntu.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation; either version 2 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
try:
|
|
# 14.04 has a broken pyqt5, so don't even try to import it and require
|
|
# pyqt4.
|
|
# In 14.04 various signals in pyqt5 can not be connected because it thinks
|
|
# the signal does not exist or has an incompatible signature. Since this
|
|
# potentially renders the GUI entirely broken and pyqt5 was not actively
|
|
# used back then it is fair to simply require qt4 on trusty systems.
|
|
from .utils import get_dist
|
|
if get_dist() == 'trusty':
|
|
raise ImportError
|
|
|
|
from PyQt5 import uic
|
|
from PyQt5.QtCore import QTranslator, PYQT_VERSION, \
|
|
QLocale
|
|
from PyQt5.QtGui import QIcon
|
|
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox, \
|
|
QApplication
|
|
except ImportError:
|
|
from PyKDE4.kdeui import KIcon, KMessageBox, KStandardGuiItem
|
|
from PyQt4.QtGui import QDialog, QDialogButtonBox, QApplication, QIcon
|
|
from PyQt4.QtCore import PYQT_VERSION
|
|
from PyQt4 import uic
|
|
|
|
import apt_pkg
|
|
|
|
from DistUpgrade.DistUpgradeFetcherCore import DistUpgradeFetcherCore
|
|
from gettext import gettext as _
|
|
from urllib.request import urlopen
|
|
from urllib.error import HTTPError
|
|
import os
|
|
|
|
import apt
|
|
|
|
from .QUrlOpener import QUrlOpener
|
|
|
|
|
|
# TODO: uifile resolution is an utter mess and should be revised globally for
|
|
# both the fetcher and the upgrader GUI.
|
|
|
|
# TODO: make this a singleton
|
|
# We have no globally constructed QApplication available so we need to
|
|
# make sure that one is created when needed. Since from a module POV
|
|
# this can be happening in any order of the two classes this function takes
|
|
# care of it for the classes, the classes only hold a ref to the qapp returned
|
|
# to prevent it from getting GC'd, so in essence this is a singleton scoped to
|
|
# the longest lifetime of an instance from the Qt GUI. Since the lifetime is
|
|
# pretty much equal to the process' one we might as well singleton up.
|
|
def _ensureQApplication():
|
|
if not QApplication.instance():
|
|
# Force environment to make sure Qt uses suitable theming and UX.
|
|
os.environ["QT_PLATFORM_PLUGIN"] = "kde"
|
|
# For above settings to apply automatically we need to indicate that we
|
|
# are inside a full KDE session.
|
|
os.environ["KDE_FULL_SESSION"] = "TRUE"
|
|
# We also need to indicate version as otherwise KDElibs3 compatibility
|
|
# might kick in such as in QIconLoader.cpp:QString fallbackTheme.
|
|
os.environ["KDE_SESSION_VERSION"] = "5"
|
|
# Pretty much all of the above but for Qt5
|
|
os.environ["QT_QPA_PLATFORMTHEME"] = "kde"
|
|
|
|
app = QApplication(["ubuntu-release-upgrader"])
|
|
|
|
# Try to load default Qt translations so we don't have to worry about
|
|
# QStandardButton translations.
|
|
# FIXME: make sure we dep on l10n
|
|
translator = QTranslator(app)
|
|
if isinstance(PYQT_VERSION, int):
|
|
translator.load(QLocale.system(), 'qt', '_',
|
|
'/usr/share/qt5/translations')
|
|
else:
|
|
translator.load(QLocale.system(), 'qt', '_',
|
|
'/usr/share/qt4/translations')
|
|
app.installTranslator(translator)
|
|
return app
|
|
return QApplication.instance()
|
|
|
|
|
|
# Qt 5 vs. KDELibs4 compat functions
|
|
def _warning(text):
|
|
if isinstance(PYQT_VERSION, int):
|
|
QMessageBox.warning(None, "", text)
|
|
else:
|
|
KMessageBox.sorry(None, text, "")
|
|
|
|
|
|
def _icon(name):
|
|
if isinstance(PYQT_VERSION, int):
|
|
return QIcon.fromTheme(name)
|
|
else:
|
|
return KIcon(name)
|
|
|
|
|
|
class DistUpgradeFetcherKDE(DistUpgradeFetcherCore):
|
|
|
|
def __init__(self, new_dist, progress, parent, datadir):
|
|
DistUpgradeFetcherCore.__init__(self, new_dist, progress)
|
|
|
|
self.app = _ensureQApplication()
|
|
self.app.setWindowIcon(_icon("system-software-update"))
|
|
|
|
self.datadir = datadir
|
|
|
|
QUrlOpener().setupUrlHandles()
|
|
|
|
QApplication.processEvents()
|
|
|
|
def error(self, summary, message):
|
|
if isinstance(PYQT_VERSION, int):
|
|
QMessageBox.critical(None, summary, message)
|
|
else:
|
|
KMessageBox.sorry(None, message, summary)
|
|
|
|
def runDistUpgrader(self):
|
|
# now run it with sudo
|
|
if os.getuid() != 0:
|
|
os.execv("/usr/bin/pkexec",
|
|
["pkexec",
|
|
self.script + " --frontend=DistUpgradeViewKDE"])
|
|
else:
|
|
os.execv(self.script,
|
|
[self.script, "--frontend=DistUpgradeViewKDE"] +
|
|
self.run_options)
|
|
|
|
def showReleaseNotes(self):
|
|
# FIXME: care about i18n! (append -$lang or something)
|
|
# TODO: ^ what is this supposed to mean?
|
|
self.dialog = QDialog()
|
|
uic.loadUi(self.datadir + "/dialog_release_notes.ui", self.dialog)
|
|
upgradeButton = self.dialog.buttonBox.button(QDialogButtonBox.Ok)
|
|
upgradeButton.setText(_("&Upgrade"))
|
|
upgradeButton.setIcon(_icon("dialog-ok"))
|
|
cancelButton = self.dialog.buttonBox.button(QDialogButtonBox.Cancel)
|
|
cancelButton.setText(_("&Cancel"))
|
|
cancelButton.setIcon(_icon("dialog-cancel"))
|
|
self.dialog.setWindowTitle(_("Release Notes"))
|
|
self.dialog.show()
|
|
if self.new_dist.releaseNotesHtmlUri is not None:
|
|
uri = self._expandUri(self.new_dist.releaseNotesHtmlUri)
|
|
# download/display the release notes
|
|
# TODO: add some progress reporting here
|
|
result = None
|
|
try:
|
|
release_notes = urlopen(uri)
|
|
notes = release_notes.read().decode("UTF-8", "replace")
|
|
self.dialog.scrolled_notes.setText(notes)
|
|
result = self.dialog.exec_()
|
|
except HTTPError:
|
|
primary = "<span weight=\"bold\" size=\"larger\">%s</span>" % \
|
|
_("Could not find the release notes")
|
|
secondary = _("The server may be overloaded. ")
|
|
_warning(primary + "<br />" + secondary)
|
|
except IOError:
|
|
primary = "<span weight=\"bold\" size=\"larger\">%s</span>" % \
|
|
_("Could not download the release notes")
|
|
secondary = _("Please check your internet connection.")
|
|
_warning(primary + "<br />" + secondary)
|
|
# user clicked cancel
|
|
if result == QDialog.Accepted:
|
|
return True
|
|
return False
|
|
|
|
|
|
class KDEAcquireProgressAdapter(apt.progress.base.AcquireProgress):
|
|
def __init__(self, parent, datadir, label):
|
|
self.app = _ensureQApplication()
|
|
self.dialog = QDialog()
|
|
|
|
uiFile = os.path.join(datadir, "fetch-progress.ui")
|
|
uic.loadUi(uiFile, self.dialog)
|
|
self.dialog.setWindowTitle(_("Upgrade"))
|
|
self.dialog.installingLabel.setText(label)
|
|
self.dialog.buttonBox.rejected.connect(self.abort)
|
|
|
|
# This variable is used as return value for AcquireProgress pulses.
|
|
# Setting it to False will abort the Acquire and consequently the
|
|
# entire fetcher.
|
|
self._continue = True
|
|
|
|
QApplication.processEvents()
|
|
|
|
def abort(self):
|
|
self._continue = False
|
|
|
|
def start(self):
|
|
self.dialog.installingLabel.setText(
|
|
_("Downloading additional package files..."))
|
|
self.dialog.installationProgress.setValue(0)
|
|
self.dialog.show()
|
|
|
|
def stop(self):
|
|
self.dialog.hide()
|
|
|
|
def pulse(self, owner):
|
|
apt.progress.base.AcquireProgress.pulse(self, owner)
|
|
self.dialog.installationProgress.setValue(int(
|
|
(self.current_bytes + self.current_items) /
|
|
float(self.total_bytes + self.total_items) * 100))
|
|
current_item = self.current_items + 1
|
|
if current_item > self.total_items:
|
|
current_item = self.total_items
|
|
label_text = _("Downloading additional package files...")
|
|
if self.current_cps > 0:
|
|
label_text += _("File %s of %s at %sB/s") % (
|
|
self.current_items, self.total_items,
|
|
apt_pkg.size_to_str(self.current_cps))
|
|
else:
|
|
label_text += _("File %s of %s") % (
|
|
self.current_items, self.total_items)
|
|
self.dialog.installingLabel.setText(label_text)
|
|
QApplication.processEvents()
|
|
return self._continue
|
|
|
|
def mediaChange(self, medium, drive):
|
|
msg = _("Please insert '%s' into the drive '%s'") % (medium, drive)
|
|
if isinstance(PYQT_VERSION, int):
|
|
change = QMessageBox.question(None, _("Media Change"), msg,
|
|
QMessageBox.Ok, QMessageBox.Cancel)
|
|
if change == QMessageBox.Ok:
|
|
return True
|
|
else:
|
|
change = KMessageBox.questionYesNo(None, _("Media Change"),
|
|
_("Media Change") + "<br>" +
|
|
msg, KStandardGuiItem.ok(),
|
|
KStandardGuiItem.cancel())
|
|
if change == KMessageBox.Yes:
|
|
return True
|
|
return False
|