Added detection of device busy condition

Added a lock file to ensure that only one instance of the GUI runs
This commit is contained in:
Kovid Goyal 2006-12-09 07:30:15 +00:00
parent 876f994a4a
commit 9962f95b31
6 changed files with 55 additions and 24 deletions

View File

@ -221,8 +221,12 @@ class PRS500Device(object):
self.device = self.device_descriptor.getDevice() self.device = self.device_descriptor.getDevice()
if not self.device: if not self.device:
raise DeviceError() raise DeviceError()
self.handle = self.device.open() try:
self.handle.claimInterface(self.device_descriptor.interface_id) self.handle = self.device.open()
self.handle.claimInterface(self.device_descriptor.interface_id)
except usb.USBError, e:
print >>sys.stderr, e
raise DeviceBusy()
res = self._send_validated_command(GetUSBProtocolVersion(), timeout=20000) # Large timeout as device may still be initializing res = self._send_validated_command(GetUSBProtocolVersion(), timeout=20000) # Large timeout as device may still be initializing
if res.code != 0: raise ProtocolError("Unable to get USB Protocol version.") if res.code != 0: raise ProtocolError("Unable to get USB Protocol version.")
version = self._bulk_read(24, data_type=USBProtocolVersion)[0].version version = self._bulk_read(24, data_type=USBProtocolVersion)[0].version
@ -236,8 +240,10 @@ class PRS500Device(object):
def close(self): def close(self):
""" Release device interface """ """ Release device interface """
self.handle.reset() try:
self.handle.releaseInterface() self.handle.reset()
self.handle.releaseInterface()
except: pass
self.handle, self.device = None, None self.handle, self.device = None, None
def _send_command(self, command, response_type=Response, timeout=1000): def _send_command(self, command, response_type=Response, timeout=1000):

View File

@ -34,6 +34,11 @@ class DeviceError(ProtocolError):
def __init__(self): def __init__(self):
ProtocolError.__init__(self, "Unable to find SONY Reader. Is it connected?") ProtocolError.__init__(self, "Unable to find SONY Reader. Is it connected?")
class DeviceBusy(ProtocolError):
""" Raised when device is busy """
def __init__(self):
ProtocolError.__init__(self, "Device is in use by another application")
class PacketError(ProtocolError): class PacketError(ProtocolError):
""" Errors with creating/interpreting packets """ """ Errors with creating/interpreting packets """

View File

@ -13,7 +13,8 @@
## with this program; if not, write to the Free Software Foundation, Inc., ## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
__docformat__ = "epytext" __docformat__ = "epytext"
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
APP_TITLE = "libprs500"
import pkg_resources, sys, os, re, StringIO, traceback import pkg_resources, sys, os, re, StringIO, traceback
from PyQt4 import QtCore, QtGui # Needed for classes imported with import_ui from PyQt4 import QtCore, QtGui # Needed for classes imported with import_ui
@ -28,7 +29,7 @@ def installErrorHandler(dialog):
global error_dialog global error_dialog
error_dialog = dialog error_dialog = dialog
error_dialog.resize(600, 400) error_dialog.resize(600, 400)
error_dialog.setWindowTitle("SONY Reader - Error") error_dialog.setWindowTitle(APP_TITLE + " - Error")
error_dialog.setModal(True) error_dialog.setModal(True)

View File

@ -20,12 +20,12 @@ from PyQt4 import uic
from libprs500.communicate import PRS500Device as device from libprs500.communicate import PRS500Device as device
from libprs500.errors import * from libprs500.errors import *
from libprs500.lrf.meta import LRFMetaFile, LRFException from libprs500.lrf.meta import LRFMetaFile, LRFException
from libprs500.gui import import_ui, installErrorHandler, Error, Warning, extension from libprs500.gui import import_ui, installErrorHandler, Error, Warning, extension, APP_TITLE
from libprs500.gui.widgets import LibraryBooksModel, DeviceBooksModel, DeviceModel, TableView from libprs500.gui.widgets import LibraryBooksModel, DeviceBooksModel, DeviceModel, TableView
from database import LibraryDatabase from database import LibraryDatabase
from editbook import EditBookDialog from editbook import EditBookDialog
import sys, re, os, traceback import sys, re, os, traceback, tempfile
DEFAULT_BOOK_COVER = None DEFAULT_BOOK_COVER = None
LIBRARY_BOOK_TEMPLATE = QString("<table><tr><td><b>Formats:</b> %1 </td><td><b>Tags:</b> %2</td></tr><tr><td><b>Comments:</b>%3</td></tr></table>") LIBRARY_BOOK_TEMPLATE = QString("<table><tr><td><b>Formats:</b> %1 </td><td><b>Tags:</b> %2</td></tr><tr><td><b>Comments:</b>%3</td></tr></table>")
@ -102,7 +102,7 @@ class MainWindow(QObject, Ui_MainWindow):
def delete(self, action): def delete(self, action):
count = str(len(self.current_view.selectionModel().selectedRows())) count = str(len(self.current_view.selectionModel().selectedRows()))
ret = QMessageBox.question(self.window, self.trUtf8("SONY Reader - confirm"), self.trUtf8("Are you sure you want to <b>permanently delete</b> these ") +count+self.trUtf8(" item(s)?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) ret = QMessageBox.question(self.window, self.trUtf8(APP_TITLE + " - confirm"), self.trUtf8("Are you sure you want to <b>permanently delete</b> these ") +count+self.trUtf8(" item(s)?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if ret != QMessageBox.Yes: return if ret != QMessageBox.Yes: return
self.window.setCursor(Qt.WaitCursor) self.window.setCursor(Qt.WaitCursor)
if self.library_view.isVisible(): if self.library_view.isVisible():
@ -342,7 +342,7 @@ class MainWindow(QObject, Ui_MainWindow):
def device_removed(self, timeout=False): def device_removed(self, timeout=False):
""" @todo: only reset stuff if library is not shown """ """ @todo: only reset stuff if library is not shown """
self.is_connected = False self.is_connected = False
self.df.setText("SONY Reader: <br><br>Storage card:") self.df.setText(self.df_template.arg("").arg("").arg(""))
self.device_tree.hide_reader(True) self.device_tree.hide_reader(True)
self.device_tree.hide_card(True) self.device_tree.hide_card(True)
self.book_cover.hide() self.book_cover.hide()
@ -368,13 +368,15 @@ class MainWindow(QObject, Ui_MainWindow):
self.status("Connecting to device") self.status("Connecting to device")
try: try:
info = self.dev.get_device_information(end_session=False) info = self.dev.get_device_information(end_session=False)
except DeviceBusy, e:
qFatal(str(e))
except DeviceError: except DeviceError:
self.dev.reconnect() self.dev.reconnect()
return return
except ProtocolError, e: except ProtocolError, e:
traceback.print_exc(e) traceback.print_exc(e)
qFatal("Unable to connect to device. Please try unplugging and reconnecting it") qFatal("Unable to connect to device. Please try unplugging and reconnecting it")
self.df.setText(self.df_template.arg(info[0]).arg(info[1]).arg(info[2])) self.df.setText(self.df_template.arg("Connected: "+info[0]).arg(info[1]).arg(info[2]))
space = self.dev.available_space(end_session=False) space = self.dev.available_space(end_session=False)
sc = space[1][1] if space[1][1] else space[2][1] sc = space[1][1] if space[1][1] else space[2][1]
self.device_tree.model().update_free_space(space[0][1], sc) self.device_tree.model().update_free_space(space[0][1], sc)
@ -395,6 +397,11 @@ class MainWindow(QObject, Ui_MainWindow):
def main(): def main():
from optparse import OptionParser from optparse import OptionParser
from libprs500 import __version__ as VERSION from libprs500 import __version__ as VERSION
lock = os.path.join(tempfile.gettempdir(),"libprs500_gui_lock")
if os.access(lock, os.F_OK):
print >>sys.stderr, "Another instance of", APP_TITLE, "is running"
print >>sys.stderr, "If you are sure this is not the case then manually delete the file", lock
sys.exit(1)
parser = OptionParser(usage="usage: %prog [options]", version=VERSION) parser = OptionParser(usage="usage: %prog [options]", version=VERSION)
parser.add_option("--log-packets", help="print out packet stream to stdout. "+\ parser.add_option("--log-packets", help="print out packet stream to stdout. "+\
"The numbers in the left column are byte offsets that allow the packet size to be read off easily.", \ "The numbers in the left column are byte offsets that allow the packet size to be read off easily.", \
@ -405,12 +412,17 @@ def main():
global DEFAULT_BOOK_COVER global DEFAULT_BOOK_COVER
DEFAULT_BOOK_COVER = QPixmap(":/default_cover") DEFAULT_BOOK_COVER = QPixmap(":/default_cover")
window = QMainWindow() window = QMainWindow()
window.setWindowTitle(APP_TITLE)
window.setWindowIcon(QIcon(":/icon")) window.setWindowIcon(QIcon(":/icon"))
installErrorHandler(QErrorMessage(window)) installErrorHandler(QErrorMessage(window))
QCoreApplication.setOrganizationName("KovidsBrain") QCoreApplication.setOrganizationName("KovidsBrain")
QCoreApplication.setApplicationName("SONY Reader") QCoreApplication.setApplicationName(APP_TITLE)
gui = MainWindow(window, options.log_packets) gui = MainWindow(window, options.log_packets)
ret = app.exec_() f = open(lock, "w")
f.close()
try:
ret = app.exec_()
finally: os.remove(lock)
return ret return ret
if __name__ == "__main__": main() if __name__ == "__main__": main()

View File

@ -19,7 +19,10 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
<string>SONY Reader</string> <string/>
</property>
<property name="windowIcon" >
<iconset resource="images.qrc" >:/images/library.png</iconset>
</property> </property>
<widget class="QWidget" name="centralwidget" > <widget class="QWidget" name="centralwidget" >
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >
@ -60,7 +63,7 @@
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
</property> </property>
<property name="dragDropMode" > <property name="dragDropMode" >
<enum>QAbstractItemView::DropOnly</enum> <enum>QAbstractItemView::DragDrop</enum>
</property> </property>
<property name="flow" > <property name="flow" >
<enum>QListView::TopToBottom</enum> <enum>QListView::TopToBottom</enum>
@ -84,11 +87,14 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text" > <property name="text" >
<string>&lt;b>libprs500&lt;/b> was created by &lt;b>Kovid Goyal&lt;/b> &amp;copy; 2006&lt;br>&lt;br>%1 %2 %3</string> <string>For help visit &lt;a href="http://libprs500.kovidgoyal.net">http://libprs500.kovidgoyal.net&lt;/a>&lt;br>&lt;br>&lt;b>libprs500&lt;/b> was created by &lt;b>Kovid Goyal&lt;/b> &amp;copy; 2006&lt;br>%1 %2 %3</string>
</property> </property>
<property name="textFormat" > <property name="textFormat" >
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>
</property> </property>
<property name="openExternalLinks" >
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -275,8 +281,8 @@
<widget class="QToolBar" name="tool_bar" > <widget class="QToolBar" name="tool_bar" >
<property name="minimumSize" > <property name="minimumSize" >
<size> <size>
<width>49</width> <width>163</width>
<height>37</height> <height>58</height>
</size> </size>
</property> </property>
<property name="movable" > <property name="movable" >
@ -291,6 +297,9 @@
<height>22</height> <height>22</height>
</size> </size>
</property> </property>
<property name="toolButtonStyle" >
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
<attribute name="toolBarArea" > <attribute name="toolBarArea" >
<number>4</number> <number>4</number>
</attribute> </attribute>
@ -303,7 +312,7 @@
<iconset resource="images.qrc" >:/images/plus.png</iconset> <iconset resource="images.qrc" >:/images/plus.png</iconset>
</property> </property>
<property name="text" > <property name="text" >
<string>Add files to Library</string> <string>Add books to Library</string>
</property> </property>
<property name="shortcut" > <property name="shortcut" >
<string>A</string> <string>A</string>
@ -317,7 +326,7 @@
<iconset resource="images.qrc" >:/images/minus.png</iconset> <iconset resource="images.qrc" >:/images/minus.png</iconset>
</property> </property>
<property name="text" > <property name="text" >
<string>Delete selected items</string> <string>Delete books</string>
</property> </property>
<property name="shortcut" > <property name="shortcut" >
<string>Del</string> <string>Del</string>
@ -328,7 +337,7 @@
<iconset resource="images.qrc" >:/images/edit.png</iconset> <iconset resource="images.qrc" >:/images/edit.png</iconset>
</property> </property>
<property name="text" > <property name="text" >
<string>Edit meta-information for the currently selected items</string> <string>Edit meta-information</string>
</property> </property>
<property name="shortcut" > <property name="shortcut" >
<string>E</string> <string>E</string>

View File

@ -116,9 +116,7 @@ class FileDragAndDrop(object):
urls.append(urlunparse(('file', quote(gethostname()), quote(str(file.name)), '','',''))) urls.append(urlunparse(('file', quote(gethostname()), quote(str(file.name)), '','','')))
self._dragged_files.append(file) self._dragged_files.append(file)
mime_data.setData("text/uri-list", QByteArray("\n".join(urls))) mime_data.setData("text/uri-list", QByteArray("\n".join(urls)))
user = None user = os.getenv['USER']
try: user = os.environ['USER']
except: pass
if user: mime_data.setData("text/x-xdnd-username", QByteArray(user)) if user: mime_data.setData("text/x-xdnd-username", QByteArray(user))
drag.setMimeData(mime_data) drag.setMimeData(mime_data)
return drag return drag