@ -1,8 +0,0 @@
|
|||||||
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
|
|
@ -1,52 +0,0 @@
|
|||||||
## 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.
|
|
||||||
""" The GUI for libprs500. """
|
|
||||||
import sys, os, re, StringIO, traceback
|
|
||||||
from PyQt4.QtCore import QVariant
|
|
||||||
|
|
||||||
__docformat__ = "epytext"
|
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
|
||||||
APP_TITLE = "libprs500"
|
|
||||||
NONE = QVariant() #: Null value to return from the data function of item models
|
|
||||||
|
|
||||||
|
|
||||||
error_dialog = None
|
|
||||||
|
|
||||||
def extension(path):
|
|
||||||
return os.path.splitext(path)[1][1:].lower()
|
|
||||||
|
|
||||||
def installErrorHandler(dialog):
|
|
||||||
''' Create the error dialog for unhandled exceptions'''
|
|
||||||
global error_dialog
|
|
||||||
error_dialog = dialog
|
|
||||||
error_dialog.resize(600, 400)
|
|
||||||
error_dialog.setWindowTitle(APP_TITLE + " - Error")
|
|
||||||
error_dialog.setModal(True)
|
|
||||||
|
|
||||||
|
|
||||||
def _Warning(msg, e):
|
|
||||||
print >> sys.stderr, msg
|
|
||||||
if e:
|
|
||||||
traceback.print_exc(e)
|
|
||||||
|
|
||||||
def Error(msg, e):
|
|
||||||
if error_dialog:
|
|
||||||
if e:
|
|
||||||
msg += "<br>" + traceback.format_exc(e)
|
|
||||||
msg = re.sub("Traceback", "<b>Traceback</b>", msg)
|
|
||||||
msg = re.sub(r"\n", "<br>", msg)
|
|
||||||
error_dialog.showMessage(msg)
|
|
||||||
error_dialog.show()
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
<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>
|
|
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1,308 +0,0 @@
|
|||||||
## 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.
|
|
||||||
import os, textwrap, traceback, time, re
|
|
||||||
from datetime import timedelta
|
|
||||||
from math import cos, sin, pi
|
|
||||||
from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \
|
|
||||||
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
|
|
||||||
QPen, QStyle, QPainter, QLineEdit, QApplication, \
|
|
||||||
QPalette
|
|
||||||
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
|
|
||||||
QCoreApplication, SIGNAL, QObject, QSize
|
|
||||||
|
|
||||||
from libprs500.library.database import LibraryDatabase
|
|
||||||
from libprs500.gui2 import NONE
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
class BooksView(QTableView):
|
|
||||||
wrapper = textwrap.TextWrapper(width=20)
|
|
||||||
|
|
||||||
@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 __init__(self, parent):
|
|
||||||
QTableView.__init__(self, parent)
|
|
||||||
self.display_parent = parent
|
|
||||||
self.model = BooksModel(self)
|
|
||||||
self.setModel(self.model)
|
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
||||||
self.setSortingEnabled(True)
|
|
||||||
self.setItemDelegate(LibraryDelegate(self, rating_column=4))
|
|
||||||
QObject.connect(self.model, SIGNAL('sorted()'), self.resizeRowsToContents)
|
|
||||||
QObject.connect(self.model, SIGNAL('searched()'), self.resizeRowsToContents)
|
|
||||||
self.verticalHeader().setVisible(False)
|
|
||||||
|
|
||||||
def set_database(self, db):
|
|
||||||
self.model.set_database(db)
|
|
||||||
|
|
||||||
def migrate_database(self):
|
|
||||||
if self.model.database_needs_migration():
|
|
||||||
print 'Migrating database from pre 0.4.0 version'
|
|
||||||
path = os.path.abspath(os.path.expanduser('~/library.db'))
|
|
||||||
progress = QProgressDialog('Upgrading database from pre 0.4.0 version.<br>'+\
|
|
||||||
'The new database is stored in the file <b>'+self.model.db.dbpath,
|
|
||||||
QString(), 0, LibraryDatabase.sizeof_old_database(path),
|
|
||||||
self)
|
|
||||||
progress.setModal(True)
|
|
||||||
app = QCoreApplication.instance()
|
|
||||||
def meter(count):
|
|
||||||
progress.setValue(count)
|
|
||||||
app.processEvents()
|
|
||||||
progress.setWindowTitle('Upgrading database')
|
|
||||||
progress.show()
|
|
||||||
LibraryDatabase.import_old_database(path, self.model.db.conn, meter)
|
|
||||||
|
|
||||||
def connect_to_search_box(self, sb):
|
|
||||||
QObject.connect(sb, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.model.search)
|
|
||||||
|
|
||||||
|
|
||||||
class BooksModel(QAbstractTableModel):
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
QAbstractTableModel.__init__(self, parent)
|
|
||||||
self.db = None
|
|
||||||
self.cols = ['title', 'authors', 'size', 'date', 'rating', 'publisher']
|
|
||||||
self.sorted_on = None
|
|
||||||
|
|
||||||
def set_database(self, db):
|
|
||||||
if isinstance(db, (QString, basestring)):
|
|
||||||
db = LibraryDatabase(os.path.expanduser(str(db)))
|
|
||||||
self.db = db
|
|
||||||
|
|
||||||
def search(self, text, refinement):
|
|
||||||
tokens = []
|
|
||||||
quot = re.search('"(.*?)"', text)
|
|
||||||
while quot:
|
|
||||||
tokens.append(quot.group(1))
|
|
||||||
text = text.replace('"'+quot.group(1)+'"', '')
|
|
||||||
quot = re.search('"(.*?)"', text)
|
|
||||||
tokens += text.split(' ')
|
|
||||||
self.db.filter(tokens, refinement)
|
|
||||||
self.reset()
|
|
||||||
self.emit(SIGNAL('searched()'))
|
|
||||||
|
|
||||||
def sort(self, col, order):
|
|
||||||
if not self.db:
|
|
||||||
return
|
|
||||||
ascending = order == Qt.AscendingOrder
|
|
||||||
self.db.refresh(self.cols[col], ascending)
|
|
||||||
self.reset()
|
|
||||||
self.emit(SIGNAL('sorted()'))
|
|
||||||
self.sorted_on = (col, order)
|
|
||||||
|
|
||||||
def database_needs_migration(self):
|
|
||||||
path = os.path.expanduser('~/library.db')
|
|
||||||
return self.db.is_empty() and \
|
|
||||||
os.path.exists(path) and\
|
|
||||||
LibraryDatabase.sizeof_old_database(path) > 0
|
|
||||||
|
|
||||||
def columnCount(self, parent):
|
|
||||||
return len(self.cols)
|
|
||||||
|
|
||||||
def rowCount(self, parent):
|
|
||||||
return self.db.rows() if self.db else 0
|
|
||||||
|
|
||||||
def data(self, index, role):
|
|
||||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
|
||||||
row, col = index.row(), index.column()
|
|
||||||
if col == 0:
|
|
||||||
text = self.db.title(row)
|
|
||||||
if text:
|
|
||||||
return QVariant(BooksView.wrap(text, width=35))
|
|
||||||
elif col == 1:
|
|
||||||
au = self.db.authors(row)
|
|
||||||
if au:
|
|
||||||
au = au.split(',')
|
|
||||||
jau = [ BooksView.wrap(a, width=30).strip() for a in au ]
|
|
||||||
return QVariant("\n".join(jau))
|
|
||||||
elif col == 2:
|
|
||||||
size = self.db.max_size(row)
|
|
||||||
if size:
|
|
||||||
return QVariant(BooksView.human_readable(size))
|
|
||||||
elif col == 3:
|
|
||||||
dt = self.db.timestamp(row)
|
|
||||||
if dt:
|
|
||||||
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
|
||||||
return QVariant(dt.strftime('%d %b %Y'))
|
|
||||||
elif col == 4:
|
|
||||||
r = self.db.rating(row)
|
|
||||||
r = r/2 if r else 0
|
|
||||||
return QVariant(r)
|
|
||||||
elif col == 5:
|
|
||||||
pub = self.db.publisher(row)
|
|
||||||
if pub:
|
|
||||||
return QVariant(BooksView.wrap(pub, 20))
|
|
||||||
return NONE
|
|
||||||
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 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 NONE
|
|
||||||
|
|
||||||
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 setData(self, index, value, role):
|
|
||||||
done = False
|
|
||||||
if role == Qt.EditRole:
|
|
||||||
row, col = index.row(), index.column()
|
|
||||||
if col in [2,3]:
|
|
||||||
return False
|
|
||||||
val = unicode(value.toString().toUtf8(), 'utf-8').strip() if col != 4 else \
|
|
||||||
int(value.toInt()[0])
|
|
||||||
if col == 4:
|
|
||||||
val = 0 if val < 0 else 5 if val > 5 else val
|
|
||||||
val *= 2
|
|
||||||
column = self.cols[col]
|
|
||||||
self.db.set(row, column, val)
|
|
||||||
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
|
|
||||||
index, index)
|
|
||||||
if col == self.sorted_on[0]:
|
|
||||||
self.sort(col, self.sorted_on[1])
|
|
||||||
done = True
|
|
||||||
return done
|
|
||||||
|
|
||||||
class SearchBox(QLineEdit):
|
|
||||||
def __init__(self, parent):
|
|
||||||
QLineEdit.__init__(self, parent)
|
|
||||||
self.setText('Search by title, author, publisher, tags and comments')
|
|
||||||
self.home(False)
|
|
||||||
QObject.connect(self, SIGNAL('textEdited(QString)'), self.text_edited_slot)
|
|
||||||
self.default_palette = QApplication.palette(self)
|
|
||||||
gray = QPalette(self.default_palette)
|
|
||||||
gray.setBrush(QPalette.Text, QBrush(QColor('lightgray')))
|
|
||||||
self.setPalette(gray)
|
|
||||||
self.initial_state = True
|
|
||||||
self.prev_search = ''
|
|
||||||
self.timer = None
|
|
||||||
self.interval = 1000 #: Time to wait before emitting search signal
|
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
|
||||||
if self.initial_state:
|
|
||||||
self.setText('')
|
|
||||||
self.initial_state = False
|
|
||||||
self.setPalette(self.default_palette)
|
|
||||||
QLineEdit.keyPressEvent(self, event)
|
|
||||||
|
|
||||||
def text_edited_slot(self, text):
|
|
||||||
text = str(text)
|
|
||||||
self.prev_text = text
|
|
||||||
self.timer = self.startTimer(self.interval)
|
|
||||||
|
|
||||||
def timerEvent(self, event):
|
|
||||||
self.killTimer(event.timerId())
|
|
||||||
if event.timerId() == self.timer:
|
|
||||||
text = str(self.text())
|
|
||||||
refinement = text.startswith(self.prev_search)
|
|
||||||
self.prev_search = text
|
|
||||||
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), text, refinement)
|
|
@ -1,86 +0,0 @@
|
|||||||
## 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()
|
|
@ -1,405 +0,0 @@
|
|||||||
<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 <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</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>&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<br><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<br><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><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></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>
|
|
@ -1,196 +0,0 @@
|
|||||||
# -*- 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> © 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> Size:</b> %2</td></tr><tr><td><b>Author: </b>%3</td><td><b> 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
|
|
@ -1,863 +0,0 @@
|
|||||||
## 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
|
|