GUI eject button working for Linux.

This commit is contained in:
John Schember 2009-06-05 07:29:25 -04:00
parent 724ee9e7a1
commit e01c01aec7
8 changed files with 386 additions and 13 deletions

View File

@ -67,11 +67,19 @@ class DevicePlugin(Plugin):
For example: For devices that present themselves as USB Mass storage For example: For devices that present themselves as USB Mass storage
devices, this method would be responsible for mounting the device or devices, this method would be responsible for mounting the device or
if the device has been automounted, for finding out where it has been 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 mounted. The base class within USBMS device.py has a implementation of
that should serve as a good example for USB Mass storage devices. this function that should serve as a good example for USB Mass storage
devices.
''' '''
raise NotImplementedError() 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): def set_progress_reporter(self, report_progress):
''' '''
@param report_progress: Function that is called with a % progress @param report_progress: Function that is called with a % progress

View File

@ -279,6 +279,9 @@ class PRS500(DeviceConfig, DevicePlugin):
if res.code != 0: if res.code != 0:
raise ProtocolError("Could not set time on device") raise ProtocolError("Could not set time on device")
def eject(self):
pass
def close(self): def close(self):
""" Release device interface """ """ Release device interface """
try: try:

View File

@ -491,3 +491,39 @@ class Device(DeviceConfig, DevicePlugin):
time.sleep(3) time.sleep(3)
self.open_osx() 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

View File

@ -123,6 +123,10 @@ class DeviceManager(Thread):
self.connected_slot(False) self.connected_slot(False)
device[1] ^= True device[1] ^= True
def umount_device(self):
self.device.eject()
self.device = None
def next(self): def next(self):
if not self.jobs.empty(): if not self.jobs.empty():
try: try:
@ -852,5 +856,3 @@ class DeviceGUI(object):
getattr(f, 'close', lambda : True)() getattr(f, 'close', lambda : True)()
if memory and memory[1]: if memory and memory[1]:
self.library_view.model().delete_books_by_id(memory[1]) self.library_view.model().delete_books_by_id(memory[1])

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
id="svg3186"
sodipodi:version="0.32"
inkscape:version="0.45"
version="1.0"
sodipodi:docname="player_end.svg"
sodipodi:docbase="/home/david/Progetti/sandbox"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
sodipodi:modified="true">
<defs
id="defs3188">
<radialGradient
inkscape:collect="always"
xlink:href="#radialGradient3163"
id="radialGradient2197"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(4.7377213e-8,-0.1315545,0.7519296,0,12.61245,94.56941)"
cx="80.342453"
cy="68.340897"
fx="80.342453"
fy="68.340897"
r="40.0294" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.6667,0,0,0.7574,20.7214,14.064)"
r="40.0294"
cy="59.1865"
cx="53.1978"
id="radialGradient3163">
<stop
id="stop3165"
style="stop-color:#000000;stop-opacity:1;"
offset="0" />
<stop
id="stop3175"
style="stop-color:#666666;stop-opacity:1;"
offset="1" />
</radialGradient>
<linearGradient
id="XMLID_9_"
gradientUnits="userSpaceOnUse"
x1="11.9487"
y1="34"
x2="104.0518"
y2="34"
gradientTransform="translate(559.14286,-264.28571)">
<stop
offset="0"
style="stop-color:#FFFFFF"
id="stop56" />
<stop
offset="0.80000001"
style="stop-color:#ffffff;stop-opacity:0;"
id="stop58" />
</linearGradient>
<radialGradient
id="XMLID_8_"
cx="53.1978"
cy="59.1865"
r="40.0294"
gradientTransform="matrix(0.6667,0,0,0.7574,20.7214,14.064)"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
style="stop-color:#323232"
id="stop41" />
<stop
offset="0.2083"
style="stop-color:#363636"
id="stop43" />
<stop
offset="0.4278"
style="stop-color:#434343"
id="stop45" />
<stop
offset="0.6526"
style="stop-color:#585858"
id="stop47" />
<stop
offset="0.8796"
style="stop-color:#757575"
id="stop49" />
<stop
offset="1"
style="stop-color:#888888"
id="stop51" />
</radialGradient>
<radialGradient
id="XMLID_7_"
cx="58"
cy="58"
r="48"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(559.14286,-264.28571)">
<stop
offset="0"
style="stop-color:#FFFFFF"
id="stop26" />
<stop
offset="0.574"
style="stop-color:#FFFFFF"
id="stop28" />
<stop
offset="0.6842"
style="stop-color:#FBFBFB"
id="stop30" />
<stop
offset="0.8001"
style="stop-color:#EEEEEE"
id="stop32" />
<stop
offset="0.9"
style="stop-color:#DDDDDD"
id="stop34" />
<stop
offset="1"
style="stop-color:#BBBBBB"
id="stop36" />
</radialGradient>
<filter
id="AI_Sfocatura_4">
<feGaussianBlur
stdDeviation="4"
id="feGaussianBlur6" />
</filter>
<linearGradient
id="XMLID_6_"
gradientUnits="userSpaceOnUse"
x1="58.0005"
y1="116"
x2="58.0005"
y2="4.882812e-04">
<stop
offset="0"
style="stop-color:#555555"
id="stop9" />
<stop
offset="0.2736"
style="stop-color:#595959"
id="stop11" />
<stop
offset="0.562"
style="stop-color:#666666"
id="stop13" />
<stop
offset="0.8561"
style="stop-color:#7B7B7B"
id="stop15" />
<stop
offset="1"
style="stop-color:#888888"
id="stop17" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#XMLID_9_"
id="linearGradient3242"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6.0000006,6.0000006)"
x1="11.9487"
y1="34"
x2="104.0518"
y2="34" />
<radialGradient
inkscape:collect="always"
xlink:href="#XMLID_7_"
id="radialGradient3246"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0833333,0,0,1.0833333,1.1666673,1.1666673)"
cx="58"
cy="58"
r="48" />
<linearGradient
inkscape:collect="always"
xlink:href="#XMLID_6_"
id="linearGradient3251"
gradientUnits="userSpaceOnUse"
x1="58.0005"
y1="116"
x2="58.0005"
y2="4.882812e-04" />
<radialGradient
inkscape:collect="always"
xlink:href="#radialGradient3163"
id="radialGradient3253"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.6667,0,0,0.7574,20.7214,14.064)"
cx="53.1978"
cy="59.186501"
r="40.0294" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="62"
inkscape:cy="44.566099"
inkscape:document-units="px"
inkscape:current-layer="layer1"
width="128px"
height="128px"
showgrid="true"
gridspacingx="8px"
gridspacingy="8px"
inkscape:window-width="792"
inkscape:window-height="581"
inkscape:window-x="225"
inkscape:window-y="112" />
<metadata
id="metadata3191">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
cx="58"
cy="58"
r="58"
id="circle19"
style="fill:url(#linearGradient3251)"
sodipodi:cx="58"
sodipodi:cy="58"
sodipodi:rx="58"
sodipodi:ry="58"
transform="matrix(1.1034483,0,0,1.1034483,0,-2.8e-6)" />
<g
id="g21"
transform="matrix(1.0833333,0,0,1.0833333,1.1666686,1.1666686)"
style="filter:url(#AI_Sfocatura_4);opacity:0.8">
<path
d="M 10,58 C 10,84.467 31.533,106 58,106 C 84.467,106 106,84.467 106,58 C 106,31.533 84.467,10 58,10 C 31.533,10 10,31.533 10,58 z "
id="path23" />
</g>
<path
d="M 12,63.999999 C 12,92.672581 35.327414,116 63.999998,116 C 92.672584,116 116,92.672581 116,63.999999 C 116,35.327415 92.672584,12 63.999998,12 C 35.327414,12 12,35.327415 12,63.999999 z "
id="path38"
style="fill:url(#radialGradient3246)" />
<polygon
points="42,26 90,58 42,90 42,26 "
id="polygon53"
style="fill:url(#radialGradient3253);fill-opacity:1"
transform="matrix(0,-0.8333333,1,0,6,107)" />
<path
sodipodi:nodetypes="ccccc"
id="path2195"
d="M 40,88 L 40.38871,80 L 87.95798,80.005265 L 88,87.94911 L 40,88 z "
style="fill:url(#radialGradient2197);fill-opacity:1" />
<path
d="M 63.999998,63.999999 C 81.788999,63.999999 97.967006,58.87 110.05199,50.491 C 104.19999,30.582 85.775999,16 63.999998,16 C 42.222999,16 23.798999,30.582999 17.949,50.491 C 30.032,58.87 46.209999,63.999999 63.999998,63.999999 z "
id="path60"
style="opacity:0.5;fill:url(#linearGradient3242)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -176,10 +176,23 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
SIGNAL('activated(QSystemTrayIcon::ActivationReason)'), SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
self.system_tray_icon_activated) self.system_tray_icon_activated)
self.tool_bar.contextMenuEvent = self.no_op 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 ######################## ####################### Location View ########################
QObject.connect(self.location_view, QObject.connect(self.location_view,
SIGNAL('location_selected(PyQt_PyObject)'), SIGNAL('location_selected(PyQt_PyObject)'),
self.location_selected) self.location_selected)
QObject.connect(self.location_view,
SIGNAL('umount_device()'),
self.device_manager.umount_device)
####################### Vanity ######################## ####################### Vanity ########################
self.vanity_template = _('<p>For help visit <a href="http://%s.' self.vanity_template = _('<p>For help visit <a href="http://%s.'
@ -462,13 +475,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.setMaximumHeight(max_available_height()) self.setMaximumHeight(max_available_height())
####################### 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()
if config['autolaunch_server']: if config['autolaunch_server']:

View File

@ -45,6 +45,9 @@
<height>75</height> <height>75</height>
</size> </size>
</property> </property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="verticalScrollBarPolicy"> <property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
</property> </property>

View File

@ -9,7 +9,8 @@ from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
QSyntaxHighlighter, QCursor, QColor, QWidget, \ QSyntaxHighlighter, QCursor, QColor, QWidget, \
QPixmap, QMovie, QPalette, QTimer, QDialog, \ QPixmap, QMovie, QPalette, QTimer, QDialog, \
QAbstractListModel, QVariant, Qt, SIGNAL, \ QAbstractListModel, QVariant, Qt, SIGNAL, \
QRegExp, QSettings, QSize, QModelIndex QRegExp, QSettings, QSize, QModelIndex, \
QAbstractButton, QPainter
from calibre.gui2 import human_readable, NONE, TableView, \ from calibre.gui2 import human_readable, NONE, TableView, \
qstring_to_unicode, error_dialog qstring_to_unicode, error_dialog
@ -234,6 +235,13 @@ class LocationView(QListView):
self.setCursor(Qt.PointingHandCursor) self.setCursor(Qt.PointingHandCursor)
self.currentChanged = self.current_changed 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): def count_changed(self, new_count):
self.model().count = new_count self.model().count = new_count
self.model().reset() self.model().reset()
@ -249,6 +257,33 @@ class LocationView(QListView):
if 0 <= row and row <= 3: if 0 <= row and row <= 3:
self.model().location_changed(row) 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): class DetailView(QDialog, Ui_Dialog):
def __init__(self, parent, job): def __init__(self, parent, job):
@ -617,4 +652,3 @@ class PythonHighlighter(QSyntaxHighlighter):
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
QSyntaxHighlighter.rehighlight(self) QSyntaxHighlighter.rehighlight(self)
QApplication.restoreOverrideCursor() QApplication.restoreOverrideCursor()