From e01c01aec7b5fb8a69a9da775d3d29d83fa9326b Mon Sep 17 00:00:00 2001 From: John Schember Date: Fri, 5 Jun 2009 07:29:25 -0400 Subject: [PATCH] GUI eject button working for Linux. --- src/calibre/devices/interface.py | 12 +- src/calibre/devices/prs500/driver.py | 3 + src/calibre/devices/usbms/device.py | 36 ++++ src/calibre/gui2/device.py | 6 +- src/calibre/gui2/images/eject.svg | 281 +++++++++++++++++++++++++++ src/calibre/gui2/main.py | 20 +- src/calibre/gui2/main.ui | 3 + src/calibre/gui2/widgets.py | 38 +++- 8 files changed, 386 insertions(+), 13 deletions(-) create mode 100644 src/calibre/gui2/images/eject.svg diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index ff9d71f370..f4ade8d02e 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -67,11 +67,19 @@ class DevicePlugin(Plugin): For example: For devices that present themselves as USB Mass storage devices, this method would be responsible for mounting the device or if the device has been automounted, for finding out where it has been - mounted. The driver for the PRS505 has a implementation of this function - that should serve as a good example for USB Mass storage devices. + mounted. The base class within USBMS device.py has a implementation of + this function that should serve as a good example for USB Mass storage + devices. ''' raise NotImplementedError() + def eject(self): + ''' + Un-mount / eject the device from the OS. This does not check if there + are pending GUI jobs that need to communicate with the device. + ''' + raise NotImplementedError() + def set_progress_reporter(self, report_progress): ''' @param report_progress: Function that is called with a % progress diff --git a/src/calibre/devices/prs500/driver.py b/src/calibre/devices/prs500/driver.py index c6cd120283..cb7cd715fd 100644 --- a/src/calibre/devices/prs500/driver.py +++ b/src/calibre/devices/prs500/driver.py @@ -279,6 +279,9 @@ class PRS500(DeviceConfig, DevicePlugin): if res.code != 0: raise ProtocolError("Could not set time on device") + def eject(self): + pass + def close(self): """ Release device interface """ try: diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index a48d71edd2..7ad93b1d6e 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -491,3 +491,39 @@ class Device(DeviceConfig, DevicePlugin): time.sleep(3) self.open_osx() + def eject_windows(self): + pass + + def eject_osx(self): + pass + + def eject_linux(self): + drives = self.find_device_nodes() + for drive in drives: + if drive: + cmd = ['pumount'] + try: + p = subprocess.Popen(cmd + [drive]) + except: + pass + while p.poll() is None: + time.sleep(0.1) + + def eject(self): + if islinux: + try: + self.eject_linux() + except: + pass + if iswindows: + try: + self.eject_windows() + except: + pass + if isosx: + try: + self.eject_osx() + except: + pass + self._main_prefix = self._card_a_prefix = self._card_b_prefix = None + diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 4c13effe15..1205aabf66 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -123,6 +123,10 @@ class DeviceManager(Thread): self.connected_slot(False) device[1] ^= True + def umount_device(self): + self.device.eject() + self.device = None + def next(self): if not self.jobs.empty(): try: @@ -852,5 +856,3 @@ class DeviceGUI(object): getattr(f, 'close', lambda : True)() if memory and memory[1]: self.library_view.model().delete_books_by_id(memory[1]) - - diff --git a/src/calibre/gui2/images/eject.svg b/src/calibre/gui2/images/eject.svg new file mode 100644 index 0000000000..6cffae670b --- /dev/null +++ b/src/calibre/gui2/images/eject.svg @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index b2648409f2..e82c613da3 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -176,10 +176,23 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): SIGNAL('activated(QSystemTrayIcon::ActivationReason)'), self.system_tray_icon_activated) self.tool_bar.contextMenuEvent = self.no_op + + ####################### Start spare job server ######################## + QTimer.singleShot(1000, self.add_spare_server) + + ####################### Setup device detection ######################## + self.device_manager = DeviceManager(Dispatcher(self.device_detected), + self.job_manager) + self.device_manager.start() + + ####################### Location View ######################## QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'), self.location_selected) + QObject.connect(self.location_view, + SIGNAL('umount_device()'), + self.device_manager.umount_device) ####################### Vanity ######################## self.vanity_template = _('

For help visit + true + Qt::ScrollBarAlwaysOff diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index e918fa060e..d6b9d7d8ed 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -9,7 +9,8 @@ from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \ QSyntaxHighlighter, QCursor, QColor, QWidget, \ QPixmap, QMovie, QPalette, QTimer, QDialog, \ QAbstractListModel, QVariant, Qt, SIGNAL, \ - QRegExp, QSettings, QSize, QModelIndex + QRegExp, QSettings, QSize, QModelIndex, \ + QAbstractButton, QPainter from calibre.gui2 import human_readable, NONE, TableView, \ qstring_to_unicode, error_dialog @@ -234,6 +235,13 @@ class LocationView(QListView): self.setCursor(Qt.PointingHandCursor) self.currentChanged = self.current_changed + self.eject_button = EjectButton(self) + self.eject_button.hide() + + self.connect(self, SIGNAL('entered(QModelIndex)'), self.show_eject) + self.connect(self, SIGNAL('viewportEntered()'), self.hide_eject) + self.connect(self.eject_button, SIGNAL('clicked()'), lambda: self.emit(SIGNAL('umount_device()'))) + def count_changed(self, new_count): self.model().count = new_count self.model().reset() @@ -249,6 +257,33 @@ class LocationView(QListView): if 0 <= row and row <= 3: self.model().location_changed(row) + def show_eject(self, location): + self.eject_button.hide() + + if location.row() == 1: + rect = self.visualRect(location) + + self.eject_button.resize(rect.height()/2, rect.height()/2) + + x, y = rect.left(), rect.top() + x = x + (rect.width() - self.eject_button.width() - 2) + y += 6 + + self.eject_button.move(x, y) + self.eject_button.show() + + def hide_eject(self): + self.eject_button.hide() + + +class EjectButton(QAbstractButton): + + def paintEvent(self, event): + painter = QPainter(self) + painter.setClipRect(event.rect()); + painter.drawPixmap(0, 0, QPixmap(':/images/eject').scaledToHeight(event.rect().height(), Qt.SmoothTransformation)) + + class DetailView(QDialog, Ui_Dialog): def __init__(self, parent, job): @@ -617,4 +652,3 @@ class PythonHighlighter(QSyntaxHighlighter): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QSyntaxHighlighter.rehighlight(self) QApplication.restoreOverrideCursor() -