3
3
# Copyright (C) 2009 Roderick B. Greening <roderick.greening@gmail.com>
4
4
# Copyright (C) 2014 Harald Sitter <apachelogger@kubuntu.org>
6
6
# Based in part on work by:
7
7
# David Edmundson <kde@davidedmundson.co.uk>
8
8
# Canonical Ltd. USB Creator Team
10
10
# This program is free software: you can redistribute it and/or modify
11
11
# it under the terms of the GNU General Public License version 3,
12
12
# as published by the Free Software Foundation.
19
19
# You should have received a copy of the GNU General Public License
20
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
# TODO: When pykf5 becomes available qmessagebox should be ported back to kmessagebox
25
from PyQt4.QtCore import *
26
from PyQt4.QtGui import *
28
from PyKDE4.kdeui import KIcon, KMessageBox, KGlobalSettings
29
from PyKDE4.kdecore import KProcess, KStandardDirs, KUrl, i18n
30
from PyKDE4.kio import KFileDialog
28
from PyQt5.QtCore import *
29
from PyQt5.QtGui import *
30
from PyQt5.QtWidgets import *
32
32
from usbcreator.frontends.kde.translate import translate
33
33
uic.properties.Properties._string = translate
51
51
class KdeFrontend(Frontend, QObject):
53
53
def startup_failure(cls, message):
54
KMessageBox.sorry(None, message, "", KMessageBox.Notify)
54
#KMessageBox.error(None, message)
55
QMessageBox.critical(None, '', message)
57
59
def DBusMainLoop(cls):
92
94
if self.__img is not None:
93
95
self.__backend.add_image(misc.text_type(self.__img))
95
downloadsDir = QDir(KGlobalSettings.self().downloadPath())
97
downloadsDir = QDir(QStandardPaths.standardLocations(QStandardPaths.DownloadLocation)[0])
97
99
isoFilter.append("*.iso")
98
100
for iso in downloadsDir.entryList(isoFilter, QDir.Files):
133
135
if 'USBCREATOR_LOCAL' in os.environ:
134
136
appdir = os.path.join(os.getcwd(), 'gui')
136
file = KStandardDirs.locate("appdata", self.__mainWindow_ui)
138
file = QStandardPaths.locate(QStandardPaths.DataLocation, self.__mainWindow_ui)
137
139
appdir = file[:file.rfind("/")]
138
140
uic.loadUi(misc.text_type(appdir + "/" + self.__mainWindow_ui), self.__mainWindow)
151
153
self.__mainWindow.ui_persist_frame.hide()
153
155
#hide sources if an argument was provided
154
if self.__img is not None:
156
if self.__img is not '':
155
157
self.__mainWindow.ui_source_list.hide()
156
158
self.__mainWindow.ui_add_source.hide()
157
159
self.__mainWindow.source_label.hide()
161
163
self.__mainWindow.ui_start_button.setEnabled(False)
163
165
#add some buttons
164
self.__mainWindow.ui_quit_button.setIcon(KIcon("application-exit"))
165
self.__mainWindow.ui_start_button.setIcon(KIcon("dialog-ok-apply"))
166
self.__mainWindow.ui_add_source.setIcon(KIcon("media-optical"))
167
self.__mainWindow.ui_format_dest.setIcon(KIcon("drive-removable-media-usb-pendrive"))
166
self.__mainWindow.ui_quit_button.setIcon(QIcon.fromTheme("application-exit"))
167
self.__mainWindow.ui_start_button.setIcon(QIcon.fromTheme("dialog-ok-apply"))
168
self.__mainWindow.ui_add_source.setIcon(QIcon.fromTheme("media-optical"))
169
self.__mainWindow.ui_format_dest.setIcon(QIcon.fromTheme("drive-removable-media-usb-pendrive"))
170
self.connect(self.__mainWindow.ui_add_source,SIGNAL('clicked()'),
171
self.add_file_source_dialog)
172
self.connect(self.__mainWindow.ui_persist_slider,SIGNAL('valueChanged(int)'),
172
self.__mainWindow.ui_add_source.clicked.connect(self.add_file_source_dialog)
173
self.__mainWindow.ui_persist_slider.valueChanged.connect(
173
174
lambda value: self.__mainWindow.ui_persist_label.setText(misc.format_mb_size(value)))
174
self.connect(self.__mainWindow.ui_quit_button,SIGNAL('clicked()'),
176
self.connect(self.__mainWindow.ui_start_button,SIGNAL('clicked()'),
178
self.connect(self.__mainWindow.ui_dest_list,SIGNAL(
179
'currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)'),
180
self.dest_selection_changed)
181
self.connect(self.__mainWindow.ui_source_list,SIGNAL(
182
'currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)'),
183
self.source_selection_changed)
184
self.connect(self.__mainWindow.ui_format_dest,SIGNAL('clicked()'),
185
self.format_dest_clicked)
175
self.__mainWindow.ui_quit_button.clicked.connect(self.quit)
176
self.__mainWindow.ui_start_button.clicked.connect(self.install)
177
self.__mainWindow.ui_dest_list.currentItemChanged.connect(self.dest_selection_changed)
178
self.__mainWindow.ui_source_list.currentItemChanged.connect(self.source_selection_changed)
179
self.__mainWindow.ui_format_dest.clicked.connect(self.format_dest_clicked)
181
# FIXME: we need a custom delegate and elide the iso column on the left rather than the right
182
# otherwise long paths will take up the entire space while in fact the image name is the useful bit of information 90% of the time
187
184
self.__mainWindow.ui_source_list.setSortingEnabled(True)
188
185
self.__mainWindow.ui_source_list.sortByColumn(0, Qt.AscendingOrder)
195
192
# Neither colum is resizable by the user, so additional tooltips are
196
193
# enabled for the widgetitems (see add_source).
197
self.__mainWindow.ui_source_list.header().setResizeMode(0, QHeaderView.Stretch)
198
self.__mainWindow.ui_source_list.header().setResizeMode(1, QHeaderView.Stretch)
199
self.__mainWindow.ui_source_list.header().setResizeMode(2, QHeaderView.ResizeToContents)
194
self.__mainWindow.ui_source_list.header().setSectionResizeMode(0, QHeaderView.Stretch)
195
self.__mainWindow.ui_source_list.header().setSectionResizeMode(1, QHeaderView.Stretch)
196
self.__mainWindow.ui_source_list.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
200
197
self.__mainWindow.ui_dest_list.setSortingEnabled(True)
201
198
self.__mainWindow.ui_dest_list.sortByColumn(0, Qt.AscendingOrder)
202
199
# For destinations only stretch the device column.
203
self.__mainWindow.ui_dest_list.header().setResizeMode(0, QHeaderView.Stretch)
204
self.__mainWindow.ui_dest_list.header().setResizeMode(1, QHeaderView.ResizeToContents)
205
self.__mainWindow.ui_dest_list.header().setResizeMode(2, QHeaderView.ResizeToContents)
206
self.__mainWindow.ui_dest_list.header().setResizeMode(3, QHeaderView.ResizeToContents)
207
self.progress_bar = QProgressDialog("",i18n('Cancel'),0,100,self.__mainWindow)
200
self.__mainWindow.ui_dest_list.header().setSectionResizeMode(0, QHeaderView.Stretch)
201
self.__mainWindow.ui_dest_list.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
202
self.__mainWindow.ui_dest_list.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
203
self.__mainWindow.ui_dest_list.header().setSectionResizeMode(3, QHeaderView.ResizeToContents)
204
self.progress_bar = QProgressDialog("",_('Cancel'),0,100,self.__mainWindow)
208
205
#set title of progress window (same as gtk frontend)
209
206
self.progress_bar.setWindowTitle(_('Installing'))
210
207
#prevent progress bar from emitting reset on reaching max value (and auto closing)
212
209
#force immediate showing, rather than waiting...
213
210
self.progress_bar.setMinimumDuration(0)
214
211
#must disconnect the canceled() SIGNAL, otherwise the progress bar is actually destroyed
215
self.disconnect(self.progress_bar,SIGNAL('canceled()'),self.progress_bar.cancel)
212
self.progress_bar.canceled.disconnect(self.progress_bar.cancel)
216
213
#now we connect our own signal to display a warning dialog instead
217
self.connect(self.progress_bar,SIGNAL('canceled()'),self.warning_dialog)
214
self.progress_bar.canceled.connect(self.warning_dialog)
220
217
self.__mainWindow.show()
235
232
self.progress_bar.hide()
237
234
message = _('Installation failed.')
238
KMessageBox.error(self.__mainWindow, message)
235
#KMessageBox.error(self.__mainWindow, message)
236
QMessageBox.critical(self.__mainWindow, '', message)
241
239
def add_timeout(self, interval, func, *args):
267
263
# the new_item lines should be auto triggered onChange to the
268
264
# TreeWidget when new_item is appended.
269
265
new_item.setText(0,target)
270
new_item.setIcon(0,KIcon("drive-removable-media-usb-pendrive"))
266
new_item.setIcon(0,QIcon.fromTheme("drive-removable-media-usb-pendrive"))
272
268
item = self.__mainWindow.ui_dest_list.currentItem()
312
308
# the new_item lines should be auto triggered onChange to the TreeWidget
313
309
# when new_item is appended.
314
310
new_item.setText(0,source)
315
new_item.setIcon(0,KIcon("media-optical"))
311
new_item.setIcon(0,QIcon.fromTheme("media-optical"))
317
313
item = self.__mainWindow.ui_source_list.currentItem()
384
380
for i in range(0,self.__mainWindow.ui_dest_list.topLevelItemCount()):
385
381
item = self.__mainWindow.ui_dest_list.topLevelItem(i)
386
382
if misc.text_type(item.data(0,Qt.UserRole)) == udi:
387
self.__mainWindow.ui_dest_list.emit(
388
SIGNAL('itemChanged(item,0)'))
383
# FIXME: pyqt5 entirely bypasses qt's signals and apparently fails
384
# to do so correctly so the following yields an error
385
# even though it should work just fine (i.e. the signal exists
387
self.__mainWindow.ui_dest_list.itemChanged.emit(item, 0)
390
389
# Update persistence maximum value.
391
390
self.__mainWindow.ui_persist_frame.setEnabled(False)
449
448
def add_file_source_dialog(self):
450
449
self.__recently_added_image = None
452
filter = '*.iso|' + _('CD Images') + '\n*.img|' + _('Disk Images')
454
# FIXME: should set the default path KUrl to users home dir...
455
# This is all screwy as its run as root under kdesudo... Home = root and not user.. blarg!
456
# Need to convert to plain string for backend to work
457
filename = misc.text_type(KFileDialog.getOpenFileName(KUrl(KGlobalSettings.self().downloadPath()),
451
# This here filter is for kfiledialog, no clue if it will ever make a return
452
#filter = '*.iso|' + _('CD Images') + '\n*.img|' + _('Disk Images')
453
filter = _('CD Images') + '(*.iso)' + ';;' + _('Disk Images') + '(*.img)'
455
downloadPath = QStandardPaths.standardLocations(QStandardPaths.DownloadLocation)[0]
456
openFileName = QFileDialog.getOpenFileName(self.__mainWindow,
461
openFileName = openFileName[0]
462
filename = misc.text_type(openFileName)
527
531
caption = _('Retry?')
529
res = KMessageBox.warningYesNo(self.__mainWindow,message,caption)
533
#res = KMessageBox.warningYesNo(self.__mainWindow,message,caption)
534
res = QMessageBox.warning(self.__mainWindow, caption, message,
535
QMessageBox.Yes, QMessageBox.No)
531
return res == KMessageBox.Yes
537
#return res == KMessageBox.Yes
538
return res == QMessageBox.Yes
533
540
def notify(self,title):
534
KMessageBox.sorry(self.__mainWindow,title)
541
#KMessageBox.sorry(self.__mainWindow,title)
542
QMessageBox.warning(self.__mainWindow, '', title)
536
544
def warning_dialog(self):
537
545
'''A warning dialog to show when progress dialog cancel is pressed'''
542
550
#hide the progress bar - install will still continue in bg
543
551
self.progress_bar.hide()
545
res = KMessageBox.warningYesNo(self.__mainWindow,text,caption)
553
#res = KMessageBox.warningYesNo(self.__mainWindow,text,caption)
554
res = QMessageBox.warning(self.__mainWindow, caption, text,
555
QMessageBox.Yes, QMessageBox.No)
547
if res == KMessageBox.Yes:
557
#if res == KMessageBox.Yes:
558
if res == QMessageBox.Yes:
550
561
#user chose not to quit, so re-show progress bar
560
571
self.__mainWindow.ui_format_dest.setEnabled(True)
561
572
# TODO sort through error types (message.get_dbus_name()) in backend,
562
573
# individual functions in frontend for each error type.
563
KMessageBox.error(self.__mainWindow, str(message))
574
#KMessageBox.error(self.__mainWindow, str(message))
575
QMessageBox.critical(self.__mainWindow, '', str(message))
565
577
def format_dest_clicked(self):
566
578
item = self.__mainWindow.ui_dest_list.currentItem()
571
583
text = _('Are you sure you want to erase the entire disk?')
573
res = KMessageBox.warningYesNo(self.__mainWindow,text)
585
#res = KMessageBox.warningYesNo(self.__mainWindow,text)
586
res = QMessageBox.warning(self.__mainWindow, '', text,
587
QMessageBox.Yes, QMessageBox.No)
575
if res == KMessageBox.Yes:
589
#if res == KMessageBox.Yes:
590
if res == QMessageBox.Yes:
576
591
self.__mainWindow.setCursor(Qt.BusyCursor)
577
592
# Disable simultaneous format attempts.. let current finish first
578
593
self.__mainWindow.ui_format_dest.setEnabled(False)