1
# DistUpgradeViewKDE.py
3
# Copyright (c) 2007 Canonical Ltd
5
# Author: Jonathan Riddell <jriddell@ubuntu.com>
7
# This program is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU General Public License as
9
# published by the Free Software Foundation; either version 2 of the
10
# License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
19
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22
from __future__ import absolute_import, print_function
24
from PyQt4.QtCore import QUrl, Qt, SIGNAL, QTimer
25
from PyQt4.QtGui import (
26
QDesktopServices, QDialog, QPixmap, QTreeWidgetItem, QMessageBox,
27
QApplication, QTextEdit, QTextOption, QTextCursor, QPushButton,
28
QWidget, QIcon, QHBoxLayout, QLabel
46
from .DistUpgradeApport import run_apport, apport_crash
48
from .DistUpgradeView import DistUpgradeView, FuzzyTimeToStr, InstallProgress, AcquireProgress
52
from .DistUpgradeGettext import gettext as gett
55
return unicode(gett(str), 'UTF-8')
58
if isinstance(str, unicode):
60
return unicode(str, 'UTF-8')
62
def loadUi(file, parent):
63
if os.path.exists(file):
64
uic.loadUi(file, parent)
67
print("error, can't find file: " + file)
69
class DumbTerminal(QTextEdit):
70
""" A very dumb terminal """
71
def __init__(self, installProgress, parent_frame):
72
" really dumb terminal with simple editing support "
73
QTextEdit.__init__(self, "", parent_frame)
74
self.installProgress = installProgress
75
self.setFontFamily("Monospace")
76
self.setFontPointSize(8)
77
self.setWordWrapMode(QTextOption.NoWrap)
78
self.setUndoRedoEnabled(False)
79
self.setOverwriteMode(True)
81
#self.connect(self, SIGNAL("cursorPositionChanged()"),
82
# self.onCursorPositionChanged)
86
(self.child_pid, self.installProgress.master_fd) = pty.fork()
87
if self.child_pid == 0:
88
os.environ["TERM"] = "dumb"
91
def update_interface(self):
92
(rlist, wlist, xlist) = select.select([self.installProgress.master_fd],[],[], 0)
94
line = os.read(self.installProgress.master_fd, 255)
95
self.insertWithTermCodes(utf8(line))
96
QApplication.processEvents()
98
def insertWithTermCodes(self, text):
99
""" support basic terminal codes """
102
# \b - backspace - this seems to comes as "^H" now ??!
104
self.insertPlainText(display_text)
105
self.textCursor().deletePreviousChar()
107
# \r - is filtered out
110
# \a - bell - ignore for now
115
self.insertPlainText(display_text)
117
def keyPressEvent(self, ev):
118
""" send (ascii) key events to the pty """
120
if not hasattr(self.installProgress, "master_fd"):
122
# special handling for backspace
123
if ev.key() == Qt.Key_Backspace:
124
#print("sent backspace")
125
os.write(self.installProgress.master_fd, chr(8))
127
# do nothing for events like "shift"
130
# now sent the key event to the termianl as utf-8
131
os.write(self.installProgress.master_fd, ev.text().toUtf8())
133
def onCursorPositionChanged(self):
134
""" helper that ensures that the cursor is always at the end """
137
# block signals so that we do not run into a recursion
139
self.moveCursor(QTextCursor.End)
142
class KDECdromProgressAdapter(apt.progress.base.CdromProgress):
143
""" Report the cdrom add progress """
144
def __init__(self, parent):
145
self.status = parent.window_main.label_status
146
self.progressbar = parent.window_main.progressbar_cache
149
def update(self, text, step):
150
""" update is called regularly so that the gui can be redrawn """
152
self.status.setText(text)
153
self.progressbar.setValue(step/float(self.totalSteps))
154
QApplication.processEvents()
156
def ask_cdrom_name(self):
159
def change_cdrom(self):
162
class KDEOpProgress(apt.progress.base.OpProgress):
163
""" methods on the progress bar """
164
def __init__(self, progressbar, progressbar_label):
165
self.progressbar = progressbar
166
self.progressbar_label = progressbar_label
167
#self.progressbar.set_pulse_step(0.01)
168
#self.progressbar.pulse()
170
def update(self, percent):
172
# self.progressbar.set_fraction(1)
174
# self.progressbar.pulse()
175
#self.progressbar.set_fraction(percent/100.0)
176
self.progressbar.setValue(percent)
177
QApplication.processEvents()
180
self.progressbar_label.setText("")
182
class KDEAcquireProgressAdapter(AcquireProgress):
183
""" methods for updating the progress bar while fetching packages """
184
# FIXME: we really should have some sort of "we are at step"
186
# FIXME2: we need to thing about mediaCheck here too
187
def __init__(self, parent):
188
AcquireProgress.__init__(self)
189
# if this is set to false the download will cancel
190
self.status = parent.window_main.label_status
191
self.progress = parent.window_main.progressbar_cache
194
def media_change(self, medium, drive):
195
msg = _("Please insert '%s' into the drive '%s'") % (medium,drive)
196
change = QMessageBox.question(self.parent.window_main, _("Media Change"), msg, QMessageBox.Ok, QMessageBox.Cancel)
197
if change == QMessageBox.Ok:
202
AcquireProgress.start(self)
203
#self.progress.show()
204
self.progress.setValue(0)
208
self.parent.window_main.progress_text.setText(" ")
209
self.status.setText(_("Fetching is complete"))
211
def pulse(self, owner):
212
""" we don't have a mainloop in this application, we just call processEvents here and elsewhere"""
213
# FIXME: move the status_str and progress_str into python-apt
214
# (python-apt need i18n first for this)
215
AcquireProgress.pulse(self, owner)
216
self.progress.setValue(self.percent)
217
current_item = self.current_items + 1
218
if current_item > self.total_items:
219
current_item = self.total_items
221
if self.current_cps > 0:
222
self.status.setText(_("Fetching file %li of %li at %sB/s") % (current_item, self.total_items, apt_pkg.size_to_str(self.current_cps)))
223
self.parent.window_main.progress_text.setText("<i>" + _("About %s remaining") % unicode(FuzzyTimeToStr(self.eta), 'utf-8') + "</i>")
225
self.status.setText(_("Fetching file %li of %li") % (current_item, self.total_items))
226
self.parent.window_main.progress_text.setText(" ")
228
QApplication.processEvents()
231
class KDEInstallProgressAdapter(InstallProgress):
232
"""methods for updating the progress bar while installing packages"""
233
# timeout with no status change when the terminal is expanded
235
TIMEOUT_TERMINAL_ACTIVITY = 240
237
def __init__(self,parent):
238
InstallProgress.__init__(self)
240
self.label_status = parent.window_main.label_status
241
self.progress = parent.window_main.progressbar_cache
242
self.progress_text = parent.window_main.progress_text
245
self._terminal_log = open("/var/log/dist-upgrade/term.log","w")
246
except Exception as e:
247
# if something goes wrong (permission denied etc), use stdout
248
logging.error("Can not open terminal log: '%s'" % e)
249
self._terminal_log = sys.stdout
250
# some options for dpkg to make it die less easily
251
apt_pkg.config.set("DPkg::StopOnError","False")
253
def start_update(self):
254
InstallProgress.start_update(self)
255
self.finished = False
256
# FIXME: add support for the timeout
257
# of the terminal (to display something useful then)
258
# -> longer term, move this code into python-apt
259
self.label_status.setText(_("Applying changes"))
260
self.progress.setValue(0)
261
self.progress_text.setText(" ")
262
# do a bit of time-keeping
263
self.start_time = 0.0
265
self.last_activity = 0.0
266
self.parent.window_main.showTerminalButton.setEnabled(True)
268
def error(self, pkg, errormsg):
269
InstallProgress.error(self, pkg, errormsg)
270
logging.error("got an error from dpkg for pkg: '%s': '%s'" % (pkg, errormsg))
271
# we do not report followup errors from earlier failures
272
if gettext.dgettext('dpkg', "dependency problems - leaving unconfigured") in errormsg:
274
summary = _("Could not install '%s'") % pkg
275
msg = _("The upgrade will continue but the '%s' package may not "
276
"be in a working state. Please consider submitting a "
277
"bug report about it.") % pkg
278
msg = "<big><b>%s</b></big><br />%s" % (summary, msg)
280
dialogue = QDialog(self.parent.window_main)
281
loadUi("dialog_error.ui", dialogue)
282
self.parent.translate_widget_children(dialogue)
283
dialogue.label_error.setText(utf8(msg))
285
dialogue.textview_error.setText(utf8(errormsg))
286
dialogue.textview_error.show()
288
dialogue.textview_error.hide()
289
dialogue.connect(dialogue.button_bugreport, SIGNAL("clicked()"), self.parent.reportBug)
292
def conffile(self, current, new):
293
"""ask question in case conffile has been changed by user"""
294
logging.debug("got a conffile-prompt from dpkg for file: '%s'" % current)
296
prim = _("Replace the customized configuration file\n'%s'?") % current
297
sec = _("You will lose any changes you have made to this "
298
"configuration file if you choose to replace it with "
300
markup = "<span weight=\"bold\" size=\"larger\">%s </span> \n\n%s" % (prim, sec)
301
self.confDialogue = QDialog(self.parent.window_main)
302
loadUi("dialog_conffile.ui", self.confDialogue)
303
self.confDialogue.label_conffile.setText(markup)
304
self.confDialogue.textview_conffile.hide()
305
#FIXME, below to be tested
306
#self.confDialogue.resize(self.confDialogue.minimumSizeHint())
307
self.confDialogue.connect(self.confDialogue.show_difference_button, SIGNAL("clicked()"), self.showConffile)
309
# workaround silly dpkg
310
if not os.path.exists(current):
311
current = current+".dpkg-dist"
314
if os.path.exists("/usr/bin/diff"):
315
cmd = ["/usr/bin/diff", "-u", current, new]
316
diff = utf8(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0])
317
self.confDialogue.textview_conffile.setText(diff)
319
self.confDialogue.textview_conffile.setText(_("The 'diff' command was not found"))
320
result = self.confDialogue.exec_()
321
self.time_ui += time.time() - start
322
# if replace, send this to the terminal
323
if result == QDialog.Accepted:
324
os.write(self.master_fd, "y\n")
326
os.write(self.master_fd, "n\n")
328
def showConffile(self):
329
if self.confDialogue.textview_conffile.isVisible():
330
self.confDialogue.textview_conffile.hide()
331
self.confDialogue.show_difference_button.setText(_("Show Difference >>>"))
333
self.confDialogue.textview_conffile.show()
334
self.confDialogue.show_difference_button.setText(_("<<< Hide Difference"))
338
(self.child_pid, self.master_fd) = pty.fork()
339
if self.child_pid == 0:
340
os.environ["TERM"] = "dumb"
341
if (not os.environ.has_key("DEBIAN_FRONTEND") or
342
os.environ["DEBIAN_FRONTEND"] == "kde"):
343
os.environ["DEBIAN_FRONTEND"] = "noninteractive"
344
os.environ["APT_LISTCHANGES_FRONTEND"] = "none"
345
logging.debug(" fork pid is: %s" % self.child_pid)
346
return self.child_pid
348
def status_change(self, pkg, percent, status):
349
"""update progress bar and label"""
350
# start the timer when the first package changes its status
351
if self.start_time == 0.0:
352
#print("setting start time to %s" % self.start_time)
353
self.start_time = time.time()
354
self.progress.setValue(self.percent)
355
self.label_status.setText(unicode(status.strip(), 'UTF-8'))
356
# start showing when we gathered some data
358
self.last_activity = time.time()
359
self.activity_timeout_reported = False
360
delta = self.last_activity - self.start_time
361
# time wasted in conffile questions (or other ui activity)
362
delta -= self.time_ui
363
time_per_percent = (float(delta)/percent)
364
eta = (100.0 - self.percent) * time_per_percent
365
# only show if we have some sensible data (60sec < eta < 2days)
366
if eta > 61.0 and eta < (60*60*24*2):
367
self.progress_text.setText(_("About %s remaining") % FuzzyTimeToStr(eta))
369
self.progress_text.setText(" ")
371
def finish_update(self):
372
self.label_status.setText("")
374
def update_interface(self):
376
no mainloop in this application, just call processEvents lots here
377
it's also important to sleep for a minimum amount of time
379
# log the output of dpkg (on the master_fd) to the terminal log
382
(rlist, wlist, xlist) = select.select([self.master_fd],[],[], 0)
384
line = os.read(self.master_fd, 255)
385
self._terminal_log.write(line)
386
self.parent.terminal_text.insertWithTermCodes(utf8(line))
389
except Exception as e:
391
logging.debug("error reading from self.master_fd '%s'" % e)
396
InstallProgress.update_interface(self)
397
except ValueError as e:
398
logging.error("got ValueError from InstallProgress.update_interface. Line was '%s' (%s)" % (self.read, e))
399
# reset self.read so that it can continue reading and does not loop
401
# check about terminal activity
402
if self.last_activity > 0 and \
403
(self.last_activity + self.TIMEOUT_TERMINAL_ACTIVITY) < time.time():
404
if not self.activity_timeout_reported:
405
#FIXME bug 95465, I can't recreate this, so here's a hacky fix
407
logging.warning("no activity on terminal for %s seconds (%s)" % (self.TIMEOUT_TERMINAL_ACTIVITY, self.label_status.text()))
408
except UnicodeEncodeError:
409
logging.warning("no activity on terminal for %s seconds" % (self.TIMEOUT_TERMINAL_ACTIVITY))
410
self.activity_timeout_reported = True
411
self.parent.window_main.konsole_frame.show()
412
QApplication.processEvents()
415
def wait_child(self):
417
self.update_interface()
418
(pid, res) = os.waitpid(self.child_pid,os.WNOHANG)
419
if pid == self.child_pid:
421
# we need the full status here (not just WEXITSTATUS)
424
# inherit from the class created in window_main.ui
425
# to add the handler for closing the window
426
class UpgraderMainWindow(QWidget):
429
QWidget.__init__(self)
430
#uic.loadUi("window_main.ui", self)
431
loadUi("window_main.ui", self)
433
def setParent(self, parentRef):
434
self.parent = parentRef
436
def closeEvent(self, event):
437
close = self.parent.on_window_main_delete_event()
443
class DistUpgradeViewKDE(DistUpgradeView):
444
"""KDE frontend of the distUpgrade tool"""
445
def __init__(self, datadir=None, logdir=None):
446
DistUpgradeView.__init__(self)
447
# silence the PyQt4 logger
448
logger = logging.getLogger("PyQt4")
449
logger.setLevel(logging.INFO)
451
localedir=os.path.join(os.getcwd(),"mo")
453
localedir="/usr/share/locale/update-manager"
455
# FIXME: i18n must be somewhere relative do this dir
457
gettext.bindtextdomain("update-manager", localedir)
458
gettext.textdomain("update-manager")
459
except Exception as e:
460
logging.warning("Error setting locales (%s)" % e)
462
#about = KAboutData("adept_manager","Upgrader","0.1","Dist Upgrade Tool for Kubuntu",KAboutData.License_GPL,"(c) 2007 Canonical Ltd",
463
#"http://wiki.kubuntu.org/KubuntuUpdateManager", "jriddell@ubuntu.com")
464
#about.addAuthor("Jonathan Riddell", None,"jriddell@ubuntu.com")
465
#about.addAuthor("Michael Vogt", None,"michael.vogt@ubuntu.com")
466
#KCmdLineArgs.init(["./dist-upgrade.py"],about)
468
# we test for DISPLAY here, QApplication does not throw a
469
# exception when run without DISPLAY but dies instead
470
if not "DISPLAY" in os.environ:
471
raise Exception("No DISPLAY in os.environ found")
472
self.app = QApplication(["update-manager"])
474
if os.path.exists("/usr/share/icons/oxygen/48x48/apps/system-software-update.png"):
475
messageIcon = QPixmap("/usr/share/icons/oxygen/48x48/apps/system-software-update.png")
477
messageIcon = QPixmap("/usr/share/icons/hicolor/48x48/apps/adept_manager.png")
478
self.app.setWindowIcon(QIcon(messageIcon))
480
self.window_main = UpgraderMainWindow()
481
self.window_main.setParent(self)
482
self.window_main.show()
484
self.prev_step = 0 # keep a record of the latest step
486
self._opCacheProgress = KDEOpProgress(self.window_main.progressbar_cache, self.window_main.progress_text)
487
self._acquireProgress = KDEAcquireProgressAdapter(self)
488
self._cdromProgress = KDECdromProgressAdapter(self)
490
self._installProgress = KDEInstallProgressAdapter(self)
492
# reasonable fault handler
493
sys.excepthook = self._handleException
495
self.window_main.showTerminalButton.setEnabled(False)
496
self.app.connect(self.window_main.showTerminalButton, SIGNAL("clicked()"), self.showTerminal)
498
#kdesu requires us to copy the xauthority file before it removes it when Adept is killed
499
fd, copyXauth = tempfile.mkstemp("", "adept")
500
if 'XAUTHORITY' in os.environ and os.environ['XAUTHORITY'] != copyXauth:
501
shutil.copy(os.environ['XAUTHORITY'], copyXauth)
502
os.environ["XAUTHORITY"] = copyXauth
504
# Note that with kdesudo this needs --nonewdcop
505
## create a new DCOP-Client:
506
#client = DCOPClient()
507
## connect the client to the local DCOP-server:
510
#for qcstring_app in client.registeredApplications():
511
# app = str(qcstring_app)
512
# if app.startswith("adept"):
513
# adept = DCOPApp(qcstring_app, client)
514
# adeptInterface = adept.object("MainApplication-Interface")
515
# adeptInterface.quit()
517
# This works just as well
518
subprocess.call(["killall", "adept_manager"])
519
subprocess.call(["killall", "adept_updater"])
522
gettext.bindtextdomain("update-manager",localedir)
523
gettext.textdomain("update-manager")
524
self.translate_widget_children()
525
self.window_main.label_title.setText(self.window_main.label_title.text().replace("Ubuntu", "Kubuntu"))
527
# setup terminal text in hidden by default spot
528
self.window_main.konsole_frame.hide()
529
self.konsole_frame_layout = QHBoxLayout(self.window_main.konsole_frame)
530
self.window_main.konsole_frame.setMinimumSize(600, 400)
531
self.terminal_text = DumbTerminal(self._installProgress, self.window_main.konsole_frame)
532
self.konsole_frame_layout.addWidget(self.terminal_text)
533
self.terminal_text.show()
535
# for some reason we need to start the main loop to get everything displayed
536
# this app mostly works with processEvents but run main loop briefly to keep it happily displaying all widgets
537
QTimer.singleShot(10, self.exitMainLoop)
540
def exitMainLoop(self):
541
print("exitMainLoop")
544
def translate_widget_children(self, parentWidget=None):
545
if parentWidget == None:
546
parentWidget = self.window_main
547
if isinstance(parentWidget, QDialog) or isinstance(parentWidget, QWidget):
548
if str(parentWidget.windowTitle()) == "Error":
549
parentWidget.setWindowTitle( gettext.dgettext("kdelibs", "Error"))
551
parentWidget.setWindowTitle(_( str(parentWidget.windowTitle()) ))
553
if parentWidget.children() != None:
554
for widget in parentWidget.children():
555
self.translate_widget(widget)
556
self.translate_widget_children(widget)
558
def translate_widget(self, widget):
559
if isinstance(widget, QLabel) or isinstance(widget, QPushButton):
560
if str(widget.text()) == "&Cancel":
561
widget.setText(unicode(gettext.dgettext("kdelibs", "&Cancel"), 'UTF-8'))
562
elif str(widget.text()) == "&Close":
563
widget.setText(unicode(gettext.dgettext("kdelibs", "&Close"), 'UTF-8'))
564
elif str(widget.text()) != "":
565
widget.setText( _(str(widget.text())).replace("_", "&") )
567
def _handleException(self, exctype, excvalue, exctb):
570
if (issubclass(exctype, KeyboardInterrupt) or
571
issubclass(exctype, SystemExit)):
574
# we handle the exception here, hand it to apport and run the
575
# apport gui manually after it because we kill u-m during the upgrade
576
# to prevent it from popping up for reboot notifications or FF restart
577
# notifications or somesuch
578
lines = traceback.format_exception(exctype, excvalue, exctb)
579
logging.error("not handled exception in KDE frontend:\n%s" % "\n".join(lines))
580
# we can't be sure that apport will run in the middle of a upgrade
581
# so we still show a error message here
582
apport_crash(exctype, excvalue, exctb)
584
tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
585
dialog = QDialog(self.window_main)
586
loadUi("dialog_error.ui", dialog)
587
self.translate_widget_children(self.dialog)
589
#dialog.connect(dialog.beastie_url, SIGNAL("leftClickedURL(const QString&)"), self.openURL)
590
dialog.crash_detail.setText(tbtext)
594
def openURL(self, url):
595
"""start konqueror"""
596
#need to run this else kdesu can't run Konqueror
597
#subprocess.call(['su', 'ubuntu', 'xhost', '+localhost'])
598
QDesktopServices.openUrl(QUrl(url))
601
"""start konqueror"""
602
#need to run this else kdesu can't run Konqueror
603
#subprocess.call(['su', 'ubuntu', 'xhost', '+localhost'])
604
QDesktopServices.openUrl(QUrl("https://launchpad.net/ubuntu/+source/update-manager/+filebug"))
606
def showTerminal(self):
607
if self.window_main.konsole_frame.isVisible():
608
self.window_main.konsole_frame.hide()
609
self.window_main.showTerminalButton.setText(_("Show Terminal >>>"))
611
self.window_main.konsole_frame.show()
612
self.window_main.showTerminalButton.setText(_("<<< Hide Terminal"))
613
self.window_main.resize(self.window_main.sizeHint())
615
def getAcquireProgress(self):
616
return self._acquireProgress
618
def getInstallProgress(self, cache):
619
self._installProgress._cache = cache
620
return self._installProgress
622
def getOpCacheProgress(self):
623
return self._opCacheProgress
625
def getCdromProgress(self):
626
return self._cdromProgress
628
def update_status(self, msg):
629
self.window_main.label_status.setText(utf8(msg))
631
def hideStep(self, step):
632
image = getattr(self.window_main,"image_step%i" % step)
633
label = getattr(self.window_main,"label_step%i" % step)
638
step = self.prev_step
640
image = getattr(self.window_main,"image_step%i" % step)
641
if os.path.exists("/usr/share/icons/oxygen/16x16/actions/dialog-cancel.png"):
642
cancelIcon = QPixmap("/usr/share/icons/oxygen/16x16/actions/dialog-cancel.png")
643
elif os.path.exists("/usr/lib/kde4/share/icons/oxygen/16x16/actions/dialog-cancel.png"):
644
cancelIcon = QPixmap("/usr/lib/kde4/share/icons/oxygen/16x16/actions/dialog-cancel.png")
646
cancelIcon = QPixmap("/usr/share/icons/crystalsvg/16x16/actions/cancel.png")
647
image.setPixmap(cancelIcon)
650
def setStep(self, step):
651
if os.path.exists("/usr/share/icons/oxygen/16x16/actions/dialog-ok.png"):
652
okIcon = QPixmap("/usr/share/icons/oxygen/16x16/actions/dialog-ok.png")
653
elif os.path.exists("/usr/lib/kde4/share/icons/oxygen/16x16/actions/dialog-ok.png"):
654
okIcon = QPixmap("/usr/lib/kde4/share/icons/oxygen/16x16/actions/dialog-ok.png")
656
okIcon = QPixmap("/usr/share/icons/crystalsvg/16x16/actions/ok.png")
658
if os.path.exists("/usr/share/icons/oxygen/16x16/actions/arrow-right.png"):
659
arrowIcon = QPixmap("/usr/share/icons/oxygen/16x16/actions/arrow-right.png")
660
elif os.path.exists("/usr/lib/kde4/share/icons/oxygen/16x16/actions/arrow-right.png"):
661
arrowIcon = QPixmap("/usr/lib/kde4/share/icons/oxygen/16x16/actions/arrow-right.png")
663
arrowIcon = QPixmap("/usr/share/icons/crystalsvg/16x16/actions/1rightarrow.png")
666
image = getattr(self.window_main,"image_step%i" % self.prev_step)
667
label = getattr(self.window_main,"label_step%i" % self.prev_step)
668
image.setPixmap(okIcon)
671
self.prev_step = step
672
# show the an arrow for the current step and make the label bold
673
image = getattr(self.window_main,"image_step%i" % step)
674
label = getattr(self.window_main,"label_step%i" % step)
675
image.setPixmap(arrowIcon)
677
label.setText("<b>" + label.text() + "</b>")
679
def information(self, summary, msg, extended_msg=None):
680
msg = "<big><b>%s</b></big><br />%s" % (summary,msg)
682
dialogue = QDialog(self.window_main)
683
loadUi("dialog_error.ui", dialogue)
684
self.translate_widget_children(dialogue)
685
dialogue.label_error.setText(utf8(msg))
686
if extended_msg != None:
687
dialogue.textview_error.setText(utf8(extended_msg))
688
dialogue.textview_error.show()
690
dialogue.textview_error.hide()
691
dialogue.button_bugreport.hide()
692
dialogue.setWindowTitle(_("Information"))
694
if os.path.exists("/usr/share/icons/oxygen/48x48/status/dialog-information.png"):
695
messageIcon = QPixmap("/usr/share/icons/oxygen/48x48/status/dialog-information.png")
696
elif os.path.exists("/usr/lib/kde4/share/icons/oxygen/48x48/status/dialog-information.png"):
697
messageIcon = QPixmap("/usr/lib/kde4/share/icons/oxygen/48x48/status/dialog-information.png")
699
messageIcon = QPixmap("/usr/share/icons/crystalsvg/32x32/actions/messagebox_info.png")
700
dialogue.image.setPixmap(messageIcon)
703
def error(self, summary, msg, extended_msg=None):
704
msg="<big><b>%s</b></big><br />%s" % (summary, msg)
706
dialogue = QDialog(self.window_main)
707
loadUi("dialog_error.ui", dialogue)
708
self.translate_widget_children(dialogue)
709
dialogue.label_error.setText(utf8(msg))
710
if extended_msg != None:
711
dialogue.textview_error.setText(utf8(extended_msg))
712
dialogue.textview_error.show()
714
dialogue.textview_error.hide()
715
dialogue.button_close.show()
716
self.app.connect(dialogue.button_bugreport, SIGNAL("clicked()"), self.reportBug)
718
if os.path.exists("/usr/share/icons/oxygen/48x48/status/dialog-error.png"):
719
messageIcon = QPixmap("/usr/share/icons/oxygen/48x48/status/dialog-error.png")
720
elif os.path.exists("/usr/lib/kde4/share/icons/oxygen/48x48/status/dialog-error.png"):
721
messageIcon = QPixmap("/usr/lib/kde4/share/icons/oxygen/48x48/status/dialog-error.png")
723
messageIcon = QPixmap("/usr/share/icons/crystalsvg/32x32/actions/messagebox_critical.png")
724
dialogue.image.setPixmap(messageIcon)
729
def confirmChanges(self, summary, changes, demotions, downloadSize,
730
actions=None, removal_bold=True):
731
"""show the changes dialogue"""
732
# FIXME: add a whitelist here for packages that we expect to be
733
# removed (how to calc this automatically?)
734
DistUpgradeView.confirmChanges(self, summary, changes, demotions,
736
msg = unicode(self.confirmChangesMessage, 'UTF-8')
737
self.changesDialogue = QDialog(self.window_main)
738
loadUi("dialog_changes.ui", self.changesDialogue)
740
self.changesDialogue.treeview_details.hide()
741
self.changesDialogue.connect(self.changesDialogue.show_details_button, SIGNAL("clicked()"), self.showChangesDialogueDetails)
742
self.translate_widget_children(self.changesDialogue)
743
self.changesDialogue.show_details_button.setText(_("Details") + " >>>")
744
self.changesDialogue.resize(self.changesDialogue.sizeHint())
746
if os.path.exists("/usr/share/icons/oxygen/48x48/status/dialog-warning.png"):
747
warningIcon = QPixmap("/usr/share/icons/oxygen/48x48/status/dialog-warning.png")
748
elif os.path.exists("/usr/lib/kde4/share/icons/oxygen/48x48/status/dialog-warning.png"):
749
warningIcon = QPixmap("/usr/lib/kde4/share/icons/oxygen/48x48/status/dialog-warning.png")
751
warningIcon = QPixmap("/usr/share/icons/crystalsvg/32x32/actions/messagebox_warning.png")
753
self.changesDialogue.question_pixmap.setPixmap(warningIcon)
756
cancel = actions[0].replace("_", "")
757
self.changesDialogue.button_cancel_changes.setText(cancel)
758
confirm = actions[1].replace("_", "")
759
self.changesDialogue.button_confirm_changes.setText(confirm)
761
summaryText = unicode("<big><b>%s</b></big>" % summary, 'UTF-8')
762
self.changesDialogue.label_summary.setText(summaryText)
763
self.changesDialogue.label_changes.setText(msg)
764
# fill in the details
765
self.changesDialogue.treeview_details.clear()
766
self.changesDialogue.treeview_details.setHeaderLabels(["Packages"])
767
self.changesDialogue.treeview_details.header().hide()
768
for demoted in self.demotions:
769
self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("No longer supported %s") % demoted.name]) )
770
for rm in self.toRemove:
771
self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Remove %s") % rm.name]) )
772
for rm in self.toRemoveAuto:
773
self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Remove (was auto installed) %s") % rm.name]) )
774
for inst in self.toInstall:
775
self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Install %s") % inst.name]) )
776
for up in self.toUpgrade:
777
self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Upgrade %s") % up.name]) )
779
#FIXME resize label, stop it being shrinkable
780
res = self.changesDialogue.exec_()
781
if res == QDialog.Accepted:
785
def showChangesDialogueDetails(self):
786
if self.changesDialogue.treeview_details.isVisible():
787
self.changesDialogue.treeview_details.hide()
788
self.changesDialogue.show_details_button.setText(_("Details") + " >>>")
790
self.changesDialogue.treeview_details.show()
791
self.changesDialogue.show_details_button.setText("<<< " + _("Details"))
792
self.changesDialogue.resize(self.changesDialogue.sizeHint())
794
def askYesNoQuestion(self, summary, msg, default='No'):
795
answer = QMessageBox.question(self.window_main, unicode(summary, 'UTF-8'), unicode("<font>") + unicode(msg, 'UTF-8'), QMessageBox.Yes|QMessageBox.No, QMessageBox.No)
796
if answer == QMessageBox.Yes:
800
def confirmRestart(self):
801
messageBox = QMessageBox(QMessageBox.Question, _("Restart required"), _("<b><big>Restart the system to complete the upgrade</big></b>"), QMessageBox.NoButton, self.window_main)
802
yesButton = messageBox.addButton(QMessageBox.Yes)
803
noButton = messageBox.addButton(QMessageBox.No)
804
yesButton.setText(_("_Restart Now").replace("_", "&"))
805
noButton.setText(gettext.dgettext("kdelibs", "&Close"))
806
answer = messageBox.exec_()
807
if answer == QMessageBox.Yes:
811
def processEvents(self):
812
QApplication.processEvents()
814
def pulseProgress(self, finished=False):
815
# FIXME: currently we do nothing here because this is
816
# run in a different python thread and QT explodes if the UI is
817
# touched from a non QThread
820
def on_window_main_delete_event(self):
821
#FIXME make this user friendly
822
text = _("""<b><big>Cancel the running upgrade?</big></b>
824
The system could be in an unusable state if you cancel the upgrade. You are strongly advised to resume the upgrade.""")
825
text = text.replace("\n", "<br />")
826
cancel = QMessageBox.warning(self.window_main, _("Cancel Upgrade?"), text, QMessageBox.Yes, QMessageBox.No)
827
if cancel == QMessageBox.Yes:
831
if __name__ == "__main__":
833
view = DistUpgradeViewKDE()
834
view.askYesNoQuestion("input box test","bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar ")
836
if sys.argv[1] == "--test-term":
837
pid = view.terminal_text.fork()
839
subprocess.call(["bash"])
842
view.terminal_text.update_interface()
843
QApplication.processEvents()
846
if sys.argv[1] == "--show-in-terminal":
847
for c in open(sys.argv[2]).read():
848
view.terminal_text.insertWithTermCodes( c )
850
QApplication.processEvents()
853
QApplication.processEvents()
856
for pkg in sys.argv[1:]:
857
if cache[pkg].is_installed and not cache[pkg].isUpgradable:
858
cache[pkg].mark_delete(purge=True)
860
cache[pkg].mark_install()
861
cache.commit(view._acquireProgress,view._installProgress)
863
# keep the window open
865
QApplication.processEvents()