Initial import of new GUI. Nowhere near functional

This commit is contained in:
Kovid Goyal 2007-06-05 18:48:46 +00:00
parent 3bab77322e
commit a2836dba1d
19 changed files with 1574 additions and 2 deletions

View File

@ -233,8 +233,6 @@ class HTMLConverter(object):
(re.compile(r'page-break-before:\s*\w+([\s;\}])'),
lambda match: match.group(1)) ]
class Link(object):
def __init__(self, para, tag):
self.para = para

View File

@ -0,0 +1,8 @@
main_ui.py : main.ui
pyuic4 main.ui | sed -s s/^.*Margin.*// | sed -e s/^.*boxlayout\.setObjectName.*// > main_ui.py
images_rc.py : images.qrc
pyrcc4 images.qrc > images_rc.py
clean :
rm main_ui.py images_rc.py

View File

@ -0,0 +1,16 @@
<RCC>
<qresource prefix="/" >
<file>images/addfile.png</file>
<file alias="default_cover" >images/cherubs.jpg</file>
<file>images/clear.png</file>
<file>images/delfile.png</file>
<file>images/edit.png</file>
<file>images/fileopen.png</file>
<file alias="library" >images/library.png</file>
<file alias="card" >images/memory_stick_unmount.png</file>
<file>images/minus.png</file>
<file>images/plus.png</file>
<file>images/upload.png</file>
<file alias="reader" >images/reader.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,86 @@
## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.Warning
import os, tempfile, sys
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
QSettings, QVariant, QSize, QEventLoop, QString, \
QBuffer, QIODevice, QModelIndex
from PyQt4.QtGui import QPixmap, QErrorMessage, QLineEdit, \
QMessageBox, QFileDialog, QIcon, QDialog, QInputDialog
from PyQt4.Qt import qDebug, qFatal, qWarning, qCritical
from libprs500.gui2 import APP_TITLE, installErrorHandler
from libprs500.gui2.main_ui import Ui_MainWindow
class Main(QObject, Ui_MainWindow):
def __init__(self, window):
QObject.__init__(self)
Ui_MainWindow.__init__(self)
self.window = window
self.setupUi(window)
self.read_settings()
####################### Setup books view ########################
self.library_view.set_database(self.database_path)
self.library_view.connect_to_search_box(self.search)
window.closeEvent = self.close_event
window.show()
self.library_view.migrate_database()
self.library_view.sortByColumn(3, Qt.DescendingOrder)
self.library_view.resizeColumnsToContents()
self.library_view.resizeRowsToContents()
self.search.setFocus(Qt.OtherFocusReason)
def read_settings(self):
settings = QSettings()
settings.beginGroup("MainWindow")
self.window.resize(settings.value("size", QVariant(QSize(1000, 700))).\
toSize())
settings.endGroup()
self.database_path = settings.value("database path", QVariant(os.path\
.expanduser("~/library1.db"))).toString()
def write_settings(self):
settings = QSettings()
settings.beginGroup("MainWindow")
settings.setValue("size", QVariant(self.window.size()))
settings.endGroup()
def close_event(self, e):
self.write_settings()
e.accept()
def main():
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)
from PyQt4.Qt import QApplication, QMainWindow
app = QApplication(sys.argv)
window = QMainWindow()
window.setWindowTitle(APP_TITLE)
#window.setWindowIcon(QIcon(":/icon"))
installErrorHandler(QErrorMessage(window))
QCoreApplication.setOrganizationName("KovidsBrain")
QCoreApplication.setApplicationName(APP_TITLE)
Main(window)
return app.exec_()
if __name__ == '__main__':
main()

405
src/libprs500/gui2/main.ui Normal file
View File

@ -0,0 +1,405 @@
<ui version="4.0" >
<author>Kovid Goyal</author>
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>728</width>
<height>822</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<string/>
</property>
<property name="windowIcon" >
<iconset/>
</property>
<widget class="QWidget" name="centralwidget" >
<layout class="QVBoxLayout" >
<property name="spacing" >
<number>6</number>
</property>
<property name="leftMargin" >
<number>9</number>
</property>
<property name="topMargin" >
<number>9</number>
</property>
<property name="rightMargin" >
<number>9</number>
</property>
<property name="bottomMargin" >
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="spacing" >
<number>6</number>
</property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<widget class="DeviceView" name="device_tree" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize" >
<size>
<width>10000</width>
<height>90</height>
</size>
</property>
<property name="verticalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="dragDropMode" >
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="flow" >
<enum>QListView::TopToBottom</enum>
</property>
<property name="spacing" >
<number>20</number>
</property>
<property name="viewMode" >
<enum>QListView::IconMode</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="df" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize" >
<size>
<width>16777215</width>
<height>90</height>
</size>
</property>
<property name="text" >
<string>For help visit &lt;a href="https://libprs500.kovidgoyal.net/wiki/GuiUsage">http://libprs500.kovidgoyal.net&lt;/a>&lt;br>&lt;br>&lt;b>libprs500&lt;/b>: %1 by &lt;b>Kovid Goyal&lt;/b> &amp;copy; 2006&lt;br>%2 %3 %4</string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
<property name="openExternalLinks" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="spacing" >
<number>6</number>
</property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>&amp;Search:</string>
</property>
<property name="buddy" >
<cstring>search</cstring>
</property>
</widget>
</item>
<item>
<widget class="SearchBox" name="search" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="acceptDrops" >
<bool>false</bool>
</property>
<property name="toolTip" >
<string>Search the list of books by title or author&lt;br>&lt;br>Words separated by spaces are ANDed</string>
</property>
<property name="whatsThis" >
<string>Search the list of books by title, author, publisher, tags and comments&lt;br>&lt;br>Words separated by spaces are ANDed</string>
</property>
<property name="autoFillBackground" >
<bool>false</bool>
</property>
<property name="text" >
<string/>
</property>
<property name="frame" >
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="clear_button" >
<property name="toolTip" >
<string>Reset Quick Search</string>
</property>
<property name="text" >
<string>...</string>
</property>
<property name="icon" >
<iconset resource="images.qrc" >:/images/clear.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" >
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<property name="horizontalSpacing" >
<number>6</number>
</property>
<property name="verticalSpacing" >
<number>6</number>
</property>
<item row="1" column="0" >
<widget class="BooksView" name="library_view" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops" >
<bool>true</bool>
</property>
<property name="dragEnabled" >
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode" >
<bool>false</bool>
</property>
<property name="dragDropMode" >
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="selectionBehavior" >
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid" >
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="spacing" >
<number>6</number>
</property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<widget class="CoverDisplay" name="book_cover" >
<property name="maximumSize" >
<size>
<width>60</width>
<height>80</height>
</size>
</property>
<property name="acceptDrops" >
<bool>true</bool>
</property>
<property name="pixmap" >
<pixmap/>
</property>
<property name="scaledContents" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="book_info" >
<property name="text" >
<string>&lt;table>&lt;tr>&lt;td>&lt;b>Title: &lt;/b>%1&lt;/td>&lt;td>&lt;b>&amp;nbsp;Size:&lt;/b> %2&lt;/td>&lt;/tr>&lt;tr>&lt;td>&lt;b>Author: &lt;/b>%3&lt;/td>&lt;td>&lt;b>&amp;nbsp;Type: &lt;/b>%4&lt;/td>&lt;/tr>&lt;/table></string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QToolBar" name="tool_bar" >
<property name="minimumSize" >
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="movable" >
<bool>false</bool>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="iconSize" >
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="toolButtonStyle" >
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
<attribute name="toolBarArea" >
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak" >
<bool>false</bool>
</attribute>
<addaction name="action_add" />
<addaction name="action_del" />
<addaction name="action_edit" />
</widget>
<action name="action_add" >
<property name="icon" >
<iconset resource="images.qrc" >:/images/addfile.png</iconset>
</property>
<property name="text" >
<string>Add books to Library</string>
</property>
<property name="shortcut" >
<string>A</string>
</property>
<property name="autoRepeat" >
<bool>false</bool>
</property>
</action>
<action name="action_del" >
<property name="icon" >
<iconset resource="images.qrc" >:/images/delfile.png</iconset>
</property>
<property name="text" >
<string>Delete books</string>
</property>
<property name="shortcut" >
<string>Del</string>
</property>
</action>
<action name="action_edit" >
<property name="icon" >
<iconset resource="images.qrc" >:/images/edit.png</iconset>
</property>
<property name="text" >
<string>Edit meta-information</string>
</property>
<property name="shortcut" >
<string>E</string>
</property>
<property name="autoRepeat" >
<bool>false</bool>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CoverDisplay</class>
<extends>QLabel</extends>
<header>widgets.h</header>
</customwidget>
<customwidget>
<class>DeviceView</class>
<extends>QListView</extends>
<header>widgets.h</header>
</customwidget>
<customwidget>
<class>BooksView</class>
<extends>QTableView</extends>
<header>library.h</header>
</customwidget>
<customwidget>
<class>SearchBox</class>
<extends>QLineEdit</extends>
<header>library.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="images.qrc" />
</resources>
<connections>
<connection>
<sender>clear_button</sender>
<signal>clicked()</signal>
<receiver>search</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel" >
<x>853</x>
<y>61</y>
</hint>
<hint type="destinationlabel" >
<x>784</x>
<y>58</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,196 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main.ui'
#
# Created: Sun May 27 10:53:01 2007
# by: PyQt4 UI code generator 4-snapshot-20070525
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(QtCore.QSize(QtCore.QRect(0,0,728,822).size()).expandedTo(MainWindow.minimumSizeHint()))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.vboxlayout = QtGui.QVBoxLayout(self.centralwidget)
self.vboxlayout.setSpacing(6)
self.hboxlayout = QtGui.QHBoxLayout()
self.hboxlayout.setSpacing(6)
self.device_tree = DeviceView(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.device_tree.sizePolicy().hasHeightForWidth())
self.device_tree.setSizePolicy(sizePolicy)
self.device_tree.setMaximumSize(QtCore.QSize(10000,90))
self.device_tree.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.device_tree.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.device_tree.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.device_tree.setFlow(QtGui.QListView.TopToBottom)
self.device_tree.setSpacing(20)
self.device_tree.setViewMode(QtGui.QListView.IconMode)
self.device_tree.setObjectName("device_tree")
self.hboxlayout.addWidget(self.device_tree)
self.df = QtGui.QLabel(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.df.sizePolicy().hasHeightForWidth())
self.df.setSizePolicy(sizePolicy)
self.df.setMaximumSize(QtCore.QSize(16777215,90))
self.df.setTextFormat(QtCore.Qt.RichText)
self.df.setOpenExternalLinks(True)
self.df.setObjectName("df")
self.hboxlayout.addWidget(self.df)
self.vboxlayout.addLayout(self.hboxlayout)
self.hboxlayout1 = QtGui.QHBoxLayout()
self.hboxlayout1.setSpacing(6)
self.hboxlayout1.setObjectName("hboxlayout1")
self.label = QtGui.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.hboxlayout1.addWidget(self.label)
self.search = SearchBox(self.centralwidget)
self.search.setEnabled(True)
self.search.setAcceptDrops(False)
self.search.setAutoFillBackground(False)
self.search.setFrame(True)
self.search.setObjectName("search")
self.hboxlayout1.addWidget(self.search)
self.clear_button = QtGui.QToolButton(self.centralwidget)
self.clear_button.setIcon(QtGui.QIcon(":/images/clear.png"))
self.clear_button.setObjectName("clear_button")
self.hboxlayout1.addWidget(self.clear_button)
self.vboxlayout.addLayout(self.hboxlayout1)
self.gridlayout = QtGui.QGridLayout()
self.gridlayout.setHorizontalSpacing(6)
self.gridlayout.setVerticalSpacing(6)
self.gridlayout.setObjectName("gridlayout")
self.library_view = BooksView(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(10)
sizePolicy.setHeightForWidth(self.library_view.sizePolicy().hasHeightForWidth())
self.library_view.setSizePolicy(sizePolicy)
self.library_view.setAcceptDrops(True)
self.library_view.setDragEnabled(True)
self.library_view.setDragDropOverwriteMode(False)
self.library_view.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.library_view.setAlternatingRowColors(True)
self.library_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.library_view.setShowGrid(False)
self.library_view.setObjectName("library_view")
self.gridlayout.addWidget(self.library_view,1,0,1,1)
self.vboxlayout.addLayout(self.gridlayout)
self.hboxlayout2 = QtGui.QHBoxLayout()
self.hboxlayout2.setSpacing(6)
self.hboxlayout2.setObjectName("hboxlayout2")
self.book_cover = CoverDisplay(self.centralwidget)
self.book_cover.setMaximumSize(QtCore.QSize(60,80))
self.book_cover.setAcceptDrops(True)
self.book_cover.setScaledContents(True)
self.book_cover.setObjectName("book_cover")
self.hboxlayout2.addWidget(self.book_cover)
self.book_info = QtGui.QLabel(self.centralwidget)
self.book_info.setTextFormat(QtCore.Qt.RichText)
self.book_info.setObjectName("book_info")
self.hboxlayout2.addWidget(self.book_info)
self.vboxlayout.addLayout(self.hboxlayout2)
MainWindow.setCentralWidget(self.centralwidget)
self.tool_bar = QtGui.QToolBar(MainWindow)
self.tool_bar.setMinimumSize(QtCore.QSize(0,0))
self.tool_bar.setMovable(False)
self.tool_bar.setOrientation(QtCore.Qt.Horizontal)
self.tool_bar.setIconSize(QtCore.QSize(64,64))
self.tool_bar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
self.tool_bar.setObjectName("tool_bar")
MainWindow.addToolBar(self.tool_bar)
self.action_add = QtGui.QAction(MainWindow)
self.action_add.setIcon(QtGui.QIcon(":/images/addfile.png"))
self.action_add.setAutoRepeat(False)
self.action_add.setObjectName("action_add")
self.action_del = QtGui.QAction(MainWindow)
self.action_del.setIcon(QtGui.QIcon(":/images/delfile.png"))
self.action_del.setObjectName("action_del")
self.action_edit = QtGui.QAction(MainWindow)
self.action_edit.setIcon(QtGui.QIcon(":/images/edit.png"))
self.action_edit.setAutoRepeat(False)
self.action_edit.setObjectName("action_edit")
self.tool_bar.addAction(self.action_add)
self.tool_bar.addAction(self.action_del)
self.tool_bar.addAction(self.action_edit)
self.label.setBuddy(self.search)
self.retranslateUi(MainWindow)
QtCore.QObject.connect(self.clear_button,QtCore.SIGNAL("clicked()"),self.search.clear)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
self.df.setText(QtGui.QApplication.translate("MainWindow", "For help visit <a href=\"https://libprs500.kovidgoyal.net/wiki/GuiUsage\">http://libprs500.kovidgoyal.net</a><br><br><b>libprs500</b>: %1 by <b>Kovid Goyal</b> &copy; 2006<br>%2 %3 %4", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("MainWindow", "&Search:", None, QtGui.QApplication.UnicodeUTF8))
self.search.setToolTip(QtGui.QApplication.translate("MainWindow", "Search the list of books by title or author<br><br>Words separated by spaces are ANDed", None, QtGui.QApplication.UnicodeUTF8))
self.search.setWhatsThis(QtGui.QApplication.translate("MainWindow", "Search the list of books by title or author<br><br>Words separated by spaces are ANDed", None, QtGui.QApplication.UnicodeUTF8))
self.clear_button.setToolTip(QtGui.QApplication.translate("MainWindow", "Reset Quick Search", None, QtGui.QApplication.UnicodeUTF8))
self.clear_button.setText(QtGui.QApplication.translate("MainWindow", "...", None, QtGui.QApplication.UnicodeUTF8))
self.book_info.setText(QtGui.QApplication.translate("MainWindow", "<table><tr><td><b>Title: </b>%1</td><td><b>&nbsp;Size:</b> %2</td></tr><tr><td><b>Author: </b>%3</td><td><b>&nbsp;Type: </b>%4</td></tr></table>", None, QtGui.QApplication.UnicodeUTF8))
self.action_add.setText(QtGui.QApplication.translate("MainWindow", "Add books to Library", None, QtGui.QApplication.UnicodeUTF8))
self.action_add.setShortcut(QtGui.QApplication.translate("MainWindow", "A", None, QtGui.QApplication.UnicodeUTF8))
self.action_del.setText(QtGui.QApplication.translate("MainWindow", "Delete books", None, QtGui.QApplication.UnicodeUTF8))
self.action_del.setShortcut(QtGui.QApplication.translate("MainWindow", "Del", None, QtGui.QApplication.UnicodeUTF8))
self.action_edit.setText(QtGui.QApplication.translate("MainWindow", "Edit meta-information", None, QtGui.QApplication.UnicodeUTF8))
self.action_edit.setShortcut(QtGui.QApplication.translate("MainWindow", "E", None, QtGui.QApplication.UnicodeUTF8))
from widgets import DeviceView, CoverDisplay
from library import BooksView, SearchBox
import images_rc

View File

@ -0,0 +1,863 @@
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import re
import os
import textwrap
import time
import traceback
from operator import itemgetter, attrgetter
from socket import gethostname
from urlparse import urlparse, urlunparse
from urllib import quote, unquote
from math import sin, cos, pi
from libprs500.gui import Error, _Warning
from libprs500.ptempfile import PersistentTemporaryFile
from libprs500 import iswindows
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.Qt import QApplication, QString, QFont, QAbstractListModel, \
QVariant, QAbstractTableModel, QTableView, QListView, \
QLabel, QAbstractItemView, QPixmap, QIcon, QSize, \
QSpinBox, QPoint, QPainterPath, QItemDelegate, QPainter, \
QPen, QColor, QLinearGradient, QBrush, QStyle, \
QByteArray, QBuffer, QMimeData, \
QDrag, QRect
NONE = QVariant() #: Null value to return from the data function of item models
TIME_WRITE_FMT = "%d %b %Y" #: The display format used to show dates
class FileDragAndDrop(object):
_drag_start_position = QPoint()
_dragged_files = []
@classmethod
def _bytes_to_string(cls, qba):
"""
Assumes qba is encoded in ASCII which is usually fine, since
this method is used mainly for escaped URIs.
@type qba: QByteArray
"""
return str(QString.fromAscii(qba.data())).strip()
@classmethod
def _get_r_ok_files(cls, event):
"""
Return list of paths from event that point to files to
which the user has read permission.
"""
files = []
md = event.mimeData()
if md.hasFormat("text/uri-list"):
candidates = cls._bytes_to_string(md.data("text/uri-list")).split()
for url in candidates:
o = urlparse(url)
if o.scheme and o.scheme != 'file':
_Warning(o.scheme + " not supported in drop events", None)
continue
path = unquote(o.path)
if iswindows and path.startswith('/'):
path = path[1:]
if not os.access(path, os.R_OK):
_Warning("You do not have read permission for: " + path, None)
continue
if os.path.isdir(path):
root, dirs, files2 = os.walk(path)
for _file in files2:
path = root + _file
if os.access(path, os.R_OK):
files.append(path)
else:
files.append(path)
return files
def __init__(self, QtBaseClass, enable_drag=True):
self.QtBaseClass = QtBaseClass
self.enable_drag = enable_drag
def mousePressEvent(self, event):
self.QtBaseClass.mousePressEvent(self, event)
if self.enable_drag:
if event.button == Qt.LeftButton:
self._drag_start_position = event.pos()
def mouseMoveEvent(self, event):
self.QtBaseClass.mousePressEvent(self, event)
if self.enable_drag:
if event.buttons() & Qt.LeftButton != Qt.LeftButton:
return
if (event.pos() - self._drag_start_position).manhattanLength() < \
QApplication.startDragDistance():
return
self.start_drag(self._drag_start_position)
def start_drag(self, pos):
raise NotImplementedError()
def dragEnterEvent(self, event):
if event.mimeData().hasFormat("text/uri-list"):
event.acceptProposedAction()
def dragMoveEvent(self, event):
event.acceptProposedAction()
def dropEvent(self, event):
files = self._get_r_ok_files(event)
if files:
try:
event.setDropAction(Qt.CopyAction)
if self.files_dropped(files, event):
event.accept()
except Exception, e:
Error("There was an error processing the dropped files.", e)
raise e
def files_dropped(self, files, event):
raise NotImplementedError()
def drag_object_from_files(self, files):
if files:
drag = QDrag(self)
mime_data = QMimeData()
self._dragged_files, urls = [], []
for _file in files:
urls.append(urlunparse(('file', quote(gethostname()), \
quote(_file.name.encode('utf-8')), '', '', '')))
self._dragged_files.append(_file)
mime_data.setData("text/uri-list", QByteArray("\n".join(urls)))
user = os.getenv('USER')
if user:
mime_data.setData("text/x-xdnd-username", QByteArray(user))
drag.setMimeData(mime_data)
return drag
def drag_object(self, extensions):
if extensions:
files = []
for ext in extensions:
f = PersistentTemporaryFile(suffix="."+ext)
files.append(f)
return self.drag_object_from_files(files), self._dragged_files
class TableView(FileDragAndDrop, QTableView):
wrapper = textwrap.TextWrapper(width=20)
def __init__(self, parent):
FileDragAndDrop.__init__(self, QTableView)
QTableView.__init__(self, parent)
@classmethod
def wrap(cls, s, width=20):
cls.wrapper.width = width
return cls.wrapper.fill(s)
@classmethod
def human_readable(cls, size):
""" Convert a size in bytes into a human readable form """
if size < 1024:
divisor, suffix = 1, "B"
elif size < 1024*1024:
divisor, suffix = 1024., "KB"
elif size < 1024*1024*1024:
divisor, suffix = 1024*1024, "MB"
elif size < 1024*1024*1024*1024:
divisor, suffix = 1024*1024, "GB"
size = str(size/divisor)
if size.find(".") > -1:
size = size[:size.find(".")+2]
return size + " " + suffix
def render_to_pixmap(self, indices):
rect = self.visualRect(indices[0])
rects = []
for i in range(len(indices)):
rects.append(self.visualRect(indices[i]))
rect |= rects[i]
rect = rect.intersected(self.viewport().rect())
pixmap = QPixmap(rect.size())
pixmap.fill(self.palette().base().color())
painter = QPainter(pixmap)
option = self.viewOptions()
option.state |= QStyle.State_Selected
for j in range(len(indices)):
option.rect = QRect(rects[j].topLeft() - rect.topLeft(), \
rects[j].size())
self.itemDelegate(indices[j]).paint(painter, option, indices[j])
painter.end()
return pixmap
def drag_object_from_files(self, files):
drag = FileDragAndDrop.drag_object_from_files(self, files)
drag.setPixmap(self.render_to_pixmap(self.selectedIndexes()))
return drag
class CoverDisplay(FileDragAndDrop, QLabel):
def __init__(self, parent):
FileDragAndDrop.__init__(self, QLabel)
QLabel.__init__(self, parent)
def files_dropped(self, files, event):
pix = QPixmap()
for _file in files:
pix = QPixmap(_file)
if not pix.isNull(): break
if not pix.isNull():
self.emit(SIGNAL("cover_received(QPixmap)"), pix)
return True
def start_drag(self, event):
drag, files = self.drag_object(["jpeg"])
if drag and files:
_file = files[0]
_file.close()
drag.setPixmap(self.pixmap().scaledToHeight(68, \
Qt.SmoothTransformation))
self.pixmap().save(os.path.abspath(_file.name))
drag.start(Qt.MoveAction)
class DeviceView(FileDragAndDrop, QListView):
def __init__(self, parent):
FileDragAndDrop.__init__(self, QListView, enable_drag=False)
QListView.__init__(self, parent)
def hide_reader(self, x):
self.model().update_devices(reader=not x)
def hide_card(self, x):
self.model().update_devices(card=not x)
def files_dropped(self, files, event):
ids = []
md = event.mimeData()
if md.hasFormat("application/x-libprs500-id"):
ids = [ int(id) for id in FileDragAndDrop._bytes_to_string(\
md.data("application/x-libprs500-id")).split()]
index = self.indexAt(event.pos())
if index.isValid():
return self.model().files_dropped(files, index, ids)
class DeviceBooksView(TableView):
def __init__(self, parent):
TableView.__init__(self, parent)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSortingEnabled(True)
class LibraryBooksView(TableView):
def __init__(self, parent):
TableView.__init__(self, parent)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSortingEnabled(True)
self.setItemDelegate(LibraryDelegate(self, rating_column=4))
def dragEnterEvent(self, event):
if not event.mimeData().hasFormat("application/x-libprs500-id"):
FileDragAndDrop.dragEnterEvent(self, event)
def start_drag(self, pos):
index = self.indexAt(pos)
if index.isValid():
rows = frozenset([ index.row() for index in self.selectedIndexes()])
files = self.model().extract_formats(rows)
drag = self.drag_object_from_files(files)
if drag:
ids = [ str(self.model().id_from_row(row)) for row in rows ]
drag.mimeData().setData("application/x-libprs500-id", \
QByteArray("\n".join(ids)))
drag.start()
def files_dropped(self, files, event):
if not files: return
index = self.indexAt(event.pos())
if index.isValid():
self.model().add_formats(files, index)
else: self.emit(SIGNAL('books_dropped'), files)
class LibraryDelegate(QItemDelegate):
COLOR = QColor("blue")
SIZE = 16
PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
def __init__(self, parent, rating_column=-1):
QItemDelegate.__init__(self, parent)
self.rating_column = rating_column
self.star_path = QPainterPath()
self.star_path.moveTo(90, 50)
for i in range(1, 5):
self.star_path.lineTo(50 + 40 * cos(0.8 * i * pi), \
50 + 40 * sin(0.8 * i * pi))
self.star_path.closeSubpath()
self.star_path.setFillRule(Qt.WindingFill)
gradient = QLinearGradient(0, 0, 0, 100)
gradient.setColorAt(0.0, self.COLOR)
gradient.setColorAt(1.0, self.COLOR)
self. brush = QBrush(gradient)
self.factor = self.SIZE/100.
def sizeHint(self, option, index):
if index.column() != self.rating_column:
return QItemDelegate.sizeHint(self, option, index)
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
return QSize(num*(self.SIZE), self.SIZE+4)
def paint(self, painter, option, index):
if index.column() != self.rating_column:
return QItemDelegate.paint(self, painter, option, index)
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
def draw_star():
painter.save()
painter.scale(self.factor, self.factor)
painter.translate(50.0, 50.0)
painter.rotate(-20)
painter.translate(-50.0, -50.0)
painter.drawPath(self.star_path)
painter.restore()
painter.save()
try:
if option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
painter.setRenderHint(QPainter.Antialiasing)
y = option.rect.center().y()-self.SIZE/2.
x = option.rect.right() - self.SIZE
painter.setPen(self.PEN)
painter.setBrush(self.brush)
painter.translate(x, y)
for i in range(num):
draw_star()
painter.translate(-self.SIZE, 0)
except Exception, e:
traceback.print_exc(e)
painter.restore()
def createEditor(self, parent, option, index):
if index.column() != 4:
return QItemDelegate.createEditor(self, parent, option, index)
editor = QSpinBox(parent)
editor.setSuffix(" stars")
editor.setMinimum(0)
editor.setMaximum(5)
editor.installEventFilter(self)
return editor
def setEditorData(self, editor, index):
if index.column() != 4:
return QItemDelegate.setEditorData(self, editor, index)
val = index.model()._data[index.row()]["rating"]
if not val: val = 0
editor.setValue(val)
def setModelData(self, editor, model, index):
if index.column() != 4:
return QItemDelegate.setModelData(self, editor, model, index)
editor.interpretText()
index.model().setData(index, QVariant(editor.value()), Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
if index.column() != 4:
return QItemDelegate.updateEditorGeometry(self, editor, option, index)
editor.setGeometry(option.rect)
class LibraryBooksModel(QAbstractTableModel):
FIELDS = ["id", "title", "authors", "size", "date", "rating", "publisher", \
"tags", "comments"]
TIME_READ_FMT = "%Y-%m-%d %H:%M:%S"
def __init__(self, parent):
QAbstractTableModel.__init__(self, parent)
self.db = None
self._data = None
self._orig_data = None
def extract_formats(self, rows):
files = []
for row in rows:
_id = self.id_from_row(row)
au = self._data[row]["authors"] if self._data[row]["authors"] \
else "Unknown"
basename = re.sub("\n", "", "_"+str(_id)+"_"+\
self._data[row]["title"]+" by "+ au)
exts = self.db.get_extensions(_id)
for ext in exts:
fmt = self.db.get_format(_id, ext)
if not ext:
ext =""
else:
ext = "."+ext
name = basename+ext
file = PersistentTemporaryFile(suffix=name)
if not fmt:
continue
file.write(fmt)
file.close()
files.append(file)
return files
def update_cover(self, index, pix):
spix = pix.scaledToHeight(68, Qt.SmoothTransformation)
_id = self.id_from_index(index)
qb, sqb = QBuffer(), QBuffer()
qb.open(QBuffer.ReadWrite)
sqb.open(QBuffer.ReadWrite)
pix.save(qb, "JPG")
spix.save(sqb, "JPG")
data = str(qb.data())
sdata = str(sqb.data())
qb.close()
sqb.close()
self.db.update_cover(_id, data, scaled=sdata)
self.refresh_row(index.row())
def add_formats(self, paths, index):
for path in paths:
f = open(path, "rb")
title = os.path.basename(path)
ext = title[title.rfind(".")+1:].lower() if "." in title > -1 else None
_id = self.id_from_index(index)
self.db.add_format(_id, ext, f)
f.close()
self.refresh_row(index.row())
self.emit(SIGNAL('formats_added'), index)
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return len(self.FIELDS)-3
def setData(self, index, value, role):
done = False
if role == Qt.EditRole:
row = index.row()
_id = self._data[row]["id"]
col = index.column()
val = unicode(value.toString().toUtf8(), 'utf-8').strip()
if col == 0:
col = "title"
elif col == 1:
col = "authors"
elif col == 2:
return False
elif col == 3:
return False
elif col == 4:
col, val = "rating", int(value.toInt()[0])
if val < 0:
val = 0
if val > 5:
val = 5
elif col == 5:
col = "publisher"
else:
return False
self.db.set_metadata_item(_id, col, val)
self._data[row][col] = val
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
index, index)
for i in range(len(self._orig_data)):
if self._orig_data[i]["id"] == self._data[row]["id"]:
self._orig_data[i][col] = self._data[row][col]
break
done = True
return done
def update_tags_and_comments(self, index, tags, comments):
_id = self.id_from_index(index)
self.db.set_metadata_item(_id, "tags", tags)
self.db.set_metadata_item(_id, "comments", comments)
self.refresh_row(index.row())
def flags(self, index):
flags = QAbstractTableModel.flags(self, index)
if index.isValid():
if index.column() not in [2, 3]:
flags |= Qt.ItemIsEditable
return flags
def set_data(self, db):
self.db = db
self._data = self.db.get_table(self.FIELDS)
self._orig_data = self._data
self.sort(0, Qt.DescendingOrder)
self.reset()
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return NONE
text = ""
if orientation == Qt.Horizontal:
if section == 0: text = "Title"
elif section == 1: text = "Author(s)"
elif section == 2: text = "Size"
elif section == 3: text = "Date"
elif section == 4: text = "Rating"
elif section == 5: text = "Publisher"
return QVariant(self.trUtf8(text))
else: return QVariant(str(1+section))
def info(self, row):
row = self._data[row]
cover = self.db.get_cover(row["id"])
exts = ",".join(self.db.get_extensions(row["id"]))
if cover:
pix = QPixmap()
pix.loadFromData(cover, "", Qt.AutoColor)
cover = None if pix.isNull() else pix
tags = row["tags"]
if not tags: tags = ""
comments = row["comments"]
if not comments:
comments = ""
comments = TableView.wrap(comments, width=80)
return exts, tags, comments, cover
def id_from_index(self, index): return self._data[index.row()]["id"]
def id_from_row(self, row): return self._data[row]["id"]
def refresh_row(self, row):
datum = self.db.get_row_by_id(self._data[row]["id"], self.FIELDS)
self._data[row:row+1] = [datum]
for i in range(len(self._orig_data)):
if self._orig_data[i]["id"] == datum["id"]:
self._orig_data[i:i+1] = [datum]
break
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
self.index(row, 0), self.index(row, self.columnCount(0)-1))
def book_info(self, _id):
""" Return title, authors and cover in a dict """
cover = self.db.get_cover(_id)
info = self.db.get_row_by_id(_id, ["title", "authors"])
info["cover"] = cover
return info
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
row, col = index.row(), index.column()
text = None
row = self._data[row]
if col == 4:
r = row["rating"] if row["rating"] else 0
if r < 0:
r = 0
if r > 5:
r = 5
return QVariant(r)
if col == 0:
text = TableView.wrap(row["title"], width=35)
elif col == 1:
au = row["authors"]
if au:
au = au.split("&")
jau = [ TableView.wrap(a, width=30).strip() for a in au ]
text = "\n".join(jau)
elif col == 2:
text = TableView.human_readable(row["size"])
elif col == 3:
text = time.strftime(TIME_WRITE_FMT, \
time.strptime(row["date"], self.TIME_READ_FMT))
elif col == 5:
pub = row["publisher"]
if pub:
text = TableView.wrap(pub, 20)
if text == None:
text = "Unknown"
return QVariant(text)
elif role == Qt.TextAlignmentRole and index.column() in [2, 3, 4]:
return QVariant(Qt.AlignRight | Qt.AlignVCenter)
elif role == Qt.ToolTipRole and index.isValid():
if index.column() in [0, 1, 4, 5]:
edit = "Double click to <b>edit</b> me<br><br>"
else:
edit = ""
return QVariant(edit + "You can <b>drag and drop</b> me to the \
desktop to save all my formats to your hard disk.")
return NONE
def sort(self, col, order):
descending = order != Qt.AscendingOrder
def getter(key, func):
return lambda x : func(itemgetter(key)(x))
if col == 0: key, func = "title", lambda x : x.lower()
if col == 1: key, func = "authors", lambda x : x.split()[-1:][0].lower()\
if x else ""
if col == 2: key, func = "size", int
if col == 3: key, func = "date", lambda x: time.mktime(\
time.strptime(x, self.TIME_READ_FMT))
if col == 4: key, func = "rating", lambda x: x if x else 0
if col == 5: key, func = "publisher", lambda x : x.lower() if x else ""
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data.sort(key=getter(key, func))
if descending: self._data.reverse()
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("sorted()"))
def search(self, query):
def query_in(book, q):
au = book["authors"]
if not au : au = "unknown"
pub = book["publisher"]
if not pub : pub = "unknown"
return q in book["title"].lower() or q in au.lower() or \
q in pub.lower()
queries = unicode(query, 'utf-8').lower().split()
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data = []
for book in self._orig_data:
match = True
for q in queries:
if query_in(book, q) : continue
else:
match = False
break
if match: self._data.append(book)
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("searched()"))
def delete(self, indices):
if len(indices): self.emit(SIGNAL("layoutAboutToBeChanged()"))
items = [ self._data[index.row()] for index in indices ]
for item in items:
_id = item["id"]
try:
self._data.remove(item)
except ValueError: continue
self.db.delete_by_id(_id)
for x in self._orig_data:
if x["id"] == _id: self._orig_data.remove(x)
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("deleted()"))
self.db.commit()
def add_book(self, path):
""" Must call search and sort on this models view after this """
_id = self.db.add_book(path)
self._orig_data.append(self.db.get_row_by_id(_id, self.FIELDS))
class DeviceBooksModel(QAbstractTableModel):
@apply
def booklist():
doc = """ The booklist this model is based on """
def fget(self):
return self._orig_data
return property(doc=doc, fget=fget)
def __init__(self, parent):
QAbstractTableModel.__init__(self, parent)
self._data = []
self._orig_data = []
def set_data(self, book_list):
self._data = book_list
self._orig_data = book_list
self.reset()
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return 4
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return NONE
text = ""
if orientation == Qt.Horizontal:
if section == 0: text = "Title"
elif section == 1: text = "Author(s)"
elif section == 2: text = "Size"
elif section == 3: text = "Date"
return QVariant(self.trUtf8(text))
else: return QVariant(str(1+section))
def data(self, index, role):
if role == Qt.DisplayRole:
row, col = index.row(), index.column()
book = self._data[row]
if col == 0:
text = TableView.wrap(book.title, width=40)
elif col == 1:
au = book.author
au = au.split("&")
jau = [ TableView.wrap(a, width=25).strip() for a in au ]
text = "\n".join(jau)
elif col == 2:
text = TableView.human_readable(book.size)
elif col == 3:
text = time.strftime(TIME_WRITE_FMT, book.datetime)
return QVariant(text)
elif role == Qt.TextAlignmentRole and index.column() in [2, 3]:
return QVariant(Qt.AlignRight | Qt.AlignVCenter)
return NONE
def info(self, row):
row = self._data[row]
cover = None
try:
cover = row.thumbnail
pix = QPixmap()
pix.loadFromData(cover, "", Qt.AutoColor)
cover = None if pix.isNull() else pix
except:
traceback.print_exc()
au = row.author if row.author else "Unknown"
return row.title, au, TableView.human_readable(row.size), row.mime, cover
def sort(self, col, order):
def getter(key, func):
return lambda x : func(attrgetter(key)(x))
if col == 0: key, func = "title", lambda x : x.lower()
if col == 1: key, func = "author", lambda x : x.split()[-1:][0].lower()
if col == 2: key, func = "size", int
if col == 3: key, func = "datetime", lambda x: x
descending = order != Qt.AscendingOrder
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data.sort(key=getter(key, func))
if descending: self._data.reverse()
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("sorted()"))
def search(self, query):
queries = unicode(query, 'utf-8').lower().split()
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data = []
for book in self._orig_data:
match = True
for q in queries:
if q in book.title.lower() or q in book.author.lower(): continue
else:
match = False
break
if match: self._data.append(book)
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("searched()"))
def delete(self, indices):
paths = []
rows = [ index.row() for index in indices ]
if not rows:
return
self.emit(SIGNAL("layoutAboutToBeChanged()"))
elems = [ self._data[row] for row in rows ]
for e in elems:
_id = e.id
paths.append(e.path)
self._orig_data.delete_book(_id)
try:
self._data.remove(e)
except ValueError:
pass
self.emit(SIGNAL("layoutChanged()"))
return paths
def path(self, index):
return self._data[index.row()].path
def title(self, index):
return self._data[index.row()].title
class DeviceModel(QAbstractListModel):
memory_free = 0
card_free = 0
show_reader = False
show_card = False
def update_devices(self, reader=None, card=None):
if reader != None:
self.show_reader = reader
if card != None:
self.show_card = card
self.emit(SIGNAL("layoutChanged()"))
def rowCount(self, parent):
base = 1
if self.show_reader:
base += 1
if self.show_card:
base += 1
return base
def update_free_space(self, reader, card):
self.memory_free = reader
self.card_free = card
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
self.index(1), self.index(2))
def data(self, index, role):
row = index.row()
data = NONE
if role == Qt.DisplayRole:
text = None
if row == 0:
text = "Library"
if row == 1 and self.show_reader:
text = "Reader\n" + TableView.human_readable(self.memory_free) \
+ " available"
elif row == 2 and self.show_card:
text = "Card\n" + TableView.human_readable(self.card_free) \
+ " available"
if text:
data = QVariant(text)
elif role == Qt.DecorationRole:
icon = None
if row == 0:
icon = QIcon(":/library")
elif row == 1 and self.show_reader:
icon = QIcon(":/reader")
elif self.show_card:
icon = QIcon(":/card")
if icon:
data = QVariant(icon)
elif role == Qt.SizeHintRole:
if row == 1:
return QVariant(QSize(150, 70))
elif role == Qt.FontRole:
font = QFont()
font.setBold(True)
data = QVariant(font)
return data
def is_library(self, index):
return index.row() == 0
def is_reader(self, index):
return index.row() == 1
def is_card(self, index):
return index.row() == 2
def files_dropped(self, files, index, ids):
ret = False
if self.is_library(index) and not ids:
self.emit(SIGNAL("books_dropped"), files)
ret = True
elif self.is_reader(index):
self.emit(SIGNAL("upload_books"), "reader", files, ids)
elif self.is_card(index):
self.emit(SIGNAL("upload_books"), "card", files, ids)
return ret