Implement a 'Browse books by cover' feature

This commit is contained in:
Kovid Goyal 2008-05-23 19:07:41 -07:00
parent d669477b23
commit fcf35ac0b3
11 changed files with 372 additions and 97 deletions

View file

@ -25,15 +25,17 @@ manual:
pictureflow :
mkdir -p src/calibre/plugins && rm -f src/calibre/plugins/*pictureflow* && \
cd src/calibre/gui2/pictureflow && rm -f *.o *.so* && \
cd src/calibre/gui2/pictureflow && rm -f *.o && \
mkdir -p .build && cd .build && rm -f * && \
qmake ../pictureflow-lib.pro && make && \
qmake ../pictureflow.pro && make && \
cd ../PyQt && \
mkdir -p .build && \
cd .build && rm -f * && \
python ../configure.py && make && \
cd ../../../../../.. && \
cp src/calibre/gui2/pictureflow/libpictureflow.so.?.?.? src/calibre/gui2/pictureflow/PyQt/.build/pictureflow.so src/calibre/plugins/ && \
python -c "import os, glob; lp = glob.glob('src/calibre/plugins/libpictureflow.so.*')[0]; os.rename(lp, lp[:-4])"
cp src/calibre/gui2/pictureflow/.build/libpictureflow.so.?.?.? src/calibre/gui2/pictureflow/PyQt/.build/pictureflow.so src/calibre/plugins/ && \
python -c "import os, glob; lp = glob.glob('src/calibre/plugins/libpictureflow.so.*')[0]; os.rename(lp, lp[:-4])" && \
rm -rf src/calibre/gui2/pictureflow/.build rm -rf src/calibre/gui2/pictureflow/PyQt/.build

View file

@ -177,11 +177,18 @@ def build_plugins(self):
try:
print 'Building pictureflow'
os.chdir('src/calibre/gui2/pictureflow')
for f in glob.glob('*.o'): os.unlink(f)
subprocess.check_call([qmake, 'pictureflow.pro'])
if not os.path.exists('.build'):
os.mkdir('.build')
os.chdir('.build')
for f in glob.glob('*'): os.unlink(f)
subprocess.check_call([qmake, '../pictureflow.pro'])
subprocess.check_call(['make'])
files.append((os.path.abspath(os.path.realpath('libpictureflow.dylib')), 'libpictureflow.dylib'))
os.chdir('PyQt/.build')
os.chdir('../PyQt')
if not os.path.exists('.build'):
os.mkdir('.build')
os.chdir('.build')
for f in glob.glob('*'): os.unlink(f)
subprocess.check_call([PYTHON, '../configure.py'])
subprocess.check_call(['/usr/bin/make'])
files.append((os.path.abspath('pictureflow.so'), 'pictureflow.so'))

View file

@ -555,6 +555,9 @@ def strftime(fmt, t=time.localtime()):
sys.path.insert(1, plugins)
cwd = os.getcwd()
os.chdir(plugins)
if iswindows and hasattr(sys, 'frozen'):
sys.path.insert(1, os.path.dirname(sys.executable))
try:
import pictureflow
pictureflowerror = ''

View file

@ -8,14 +8,17 @@
'''
import sys, os
from collections import deque
from PyQt4.QtGui import QImage
from PyQt4.QtCore import Qt, QSize, QTimer, SIGNAL
from PyQt4.QtGui import QImage, QSizePolicy
from PyQt4.QtCore import Qt, QSize, SIGNAL
from calibre import pictureflow
if pictureflow is not None:
class EmptyImageList(pictureflow.FlowImages):
def __init__(self):
pictureflow.FlowImages.__init__(self)
class FileSystemImages(pictureflow.FlowImages):
def __init__(self, dirpath):
@ -46,60 +49,35 @@ class DatabaseImages(pictureflow.FlowImages):
def __init__(self, model, buffer=20):
pictureflow.FlowImages.__init__(self)
self.model = model
self.default_image = QImage(':/images/book.svg')
self.buffer_size = buffer
self.timer = QTimer()
self.connect(self.timer, SIGNAL('timeout()'), self.load)
self.timer.start(50)
self.clear()
self.connect(self.model, SIGNAL('modelReset()'), self.reset)
def count(self):
return self.model.rowCount(None)
return self.model.count()
def caption(self, index):
return self.model.title(index)
def clear(self):
self.buffer = {}
self.load_queue = deque()
def reset(self):
self.emit(SIGNAL('dataChanged()'))
def load(self):
if self.load_queue:
index = self.load_queue.popleft()
if self.buffer.has_key(index):
return
img = QImage()
img.loadFromData(self.model.cover(index))
if img.isNull():
img = self.default_image
self.buffer[index] = img
def image(self, index):
img = self.buffer.get(index)
if img is None:
img = QImage()
img.loadFromData(self.model.cover(index))
if img.isNull():
img = self.default_image
self.buffer[index] = img
return img
return self.model.cover(index)
def currentChanged(self, index):
for key in self.buffer.keys():
if abs(key - index) > self.buffer_size:
self.buffer.pop(key)
for i in range(max(0, index-self.buffer_size), min(self.count(), index+self.buffer_size)):
if not self.buffer.has_key(i):
self.load_queue.append(i)
class CoverFlow(pictureflow.PictureFlow):
def __init__(self, height=300, parent=None):
pictureflow.PictureFlow.__init__(self, parent)
self.setSlideSize(QSize(int(2/3. * height), height))
self.setMinimumSize(QSize(int(2.35*0.67*height), (5/3.)*height))
self.setMinimumSize(QSize(int(2.35*0.67*height), (5/3.)*height+25))
self.setFocusPolicy(Qt.WheelFocus)
self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
else:
CoverFlow = None
DatabaseImages = None
FileSystemImages = None
def main(args=sys.argv):
return 0
@ -112,16 +90,7 @@ def main(args=sys.argv):
cf.resize(cf.minimumSize())
w.resize(cf.minimumSize()+QSize(30, 20))
path = sys.argv[1]
if path.endswith('.db'):
from calibre.library.database import LibraryDatabase
from calibre.gui2.library import BooksModel
from calibre.gui2 import images_rc
bm = BooksModel()
bm.set_database(LibraryDatabase(path))
bm.sort(1, Qt.AscendingOrder)
model = DatabaseImages(bm)
else:
model = FileSystemImages(sys.argv[1])
model = FileSystemImages(sys.argv[1])
cf.setImages(model)
cf.connect(cf, SIGNAL('currentChanged(int)'), model.currentChanged)
w.setCentralWidget(cf)

View file

@ -7,7 +7,7 @@
'''
import textwrap
from PyQt4.QtCore import Qt, QCoreApplication
from PyQt4.QtCore import QCoreApplication
from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
@ -19,11 +19,6 @@ def __init__(self, parent, info):
Ui_BookInfo.__init__(self)
self.setupUi(self)
self.default_pixmap = QPixmap(':/images/book.svg').scaled(80,
100,
Qt.IgnoreAspectRatio,
Qt.SmoothTransformation)
self.setWindowTitle(info[_('Title')])
desktop = QCoreApplication.instance().desktop()
screen_height = desktop.availableGeometry().height() - 100
@ -32,11 +27,7 @@ def __init__(self, parent, info):
self.comments.setText(info.pop(_('Comments'), ''))
cdata = info.pop('cover', '')
pixmap = QPixmap()
pixmap.loadFromData(cdata)
if pixmap.isNull():
pixmap = self.default_pixmap
pixmap = QPixmap.fromImage(cdata)
self.setWindowIcon(QIcon(pixmap))
self.scene = QGraphicsScene()

View file

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.45"
version="1.0"
sodipodi:docname="emblem-link.svgz"
sodipodi:docbase="/home/david/Oxygen/Oxygen/scalable/emblems"
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
inkscape:export-filename="/home/david/Oxygen/Oxygen/scalable/emblems/emblem-link.png"
inkscape:export-xdpi="33.75"
inkscape:export-ydpi="33.75"
sodipodi:modified="TRUE">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient3185">
<stop
style="stop-color:#eeeeee;stop-opacity:1;"
offset="0"
id="stop3187" />
<stop
style="stop-color:#eeeeee;stop-opacity:0;"
offset="1"
id="stop3189" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3133">
<stop
style="stop-color:#646661;stop-opacity:1"
offset="0"
id="stop3135" />
<stop
style="stop-color:#111111;stop-opacity:1"
offset="1"
id="stop3137" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3133"
id="radialGradient3139"
cx="64"
cy="35.686314"
fx="64"
fy="35.686314"
r="40"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.1360441,0,0,1.446027,-72.706823,-26.184217)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3185"
id="linearGradient3191"
x1="112"
y1="98.41069"
x2="61.978939"
y2="11.771669"
gradientUnits="userSpaceOnUse" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3393">
<path
style="opacity:1;fill:#dbdbdb;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 13.8125,8 C 10.584167,8 8,10.584167 8,13.8125 L 8,114.1875 C 8,117.41583 10.584167,120 13.8125,120 L 114.1875,120 C 117.41583,120 120,117.41583 120,114.1875 L 120,13.8125 C 120,10.584167 117.41583,8 114.1875,8 L 13.8125,8 z M 21.8125,16 L 106.1875,16 C 109.41583,16 112,18.584167 112,21.8125 L 112,106.1875 C 112,109.41583 109.41583,112 106.1875,112 L 21.8125,112 C 18.584166,112 16,109.41583 16,106.1875 L 16,21.8125 C 16,18.584166 18.584167,16 21.8125,16 z "
id="path3395" />
</clipPath>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3133"
id="radialGradient3413"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.1360441,0,0,-1.446027,-72.706823,154.18422)"
cx="64"
cy="35.686314"
fx="64"
fy="35.686314"
r="40" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3133"
id="radialGradient3417"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.1360441,0,0,1.446027,-72.706823,-25.184217)"
cx="64"
cy="35.686314"
fx="64"
fy="35.686314"
r="40" />
<filter
inkscape:collect="always"
id="filter3351">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="2"
id="feGaussianBlur3353" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="4.9765625"
inkscape:cx="64"
inkscape:cy="64"
inkscape:document-units="px"
inkscape:current-layer="layer1"
width="128px"
height="128px"
gridempspacing="2"
gridspacingx="4px"
gridspacingy="4px"
showgrid="false"
inkscape:window-width="748"
inkscape:window-height="734"
inkscape:window-x="400"
inkscape:window-y="0"
inkscape:grid-points="true" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:0.7;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3351)"
id="rect3305"
width="100"
height="100"
x="14"
y="14"
rx="7.4348507"
ry="7.4348507"
clip-path="url(#clipPath3393)" />
<rect
ry="5.6263733"
rx="5.6263733"
y="16"
x="16"
height="96"
width="96"
id="rect3141"
style="opacity:0.7;fill:#eeeeee;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="opacity:1;fill:url(#radialGradient3139);fill-opacity:1.0;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect2160"
width="80"
height="80"
x="24"
y="24"
rx="5.6263733"
ry="5.6263733" />
<rect
style="opacity:0.7;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect3178"
width="95"
height="95"
x="16.5"
y="16.5"
rx="5.6263733"
ry="5.6263733" />
<g
id="g3160"
transform="matrix(1.6656201,0,0,1.6656201,-62.574569,-26.624804)"
style="fill:#ffffff;fill-opacity:1">
<path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 44.03125 40 L 55.34375 51.3125 C 43.467571 59.035057 35.734919 71.703669 57.34375 93.3125 C 52.734046 74.873684 60.878036 66.021115 70.75 66.71875 L 84 79.96875 L 84 73.3125 L 84 40 L 44.03125 40 z "
transform="matrix(0.600377,0,0,0.600377,37.568332,15.98492)"
id="path2179" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

@ -3,13 +3,15 @@
import os, textwrap, traceback, time, re, sre_constants
from datetime import timedelta, datetime
from operator import attrgetter
from collections import deque
from math import cos, sin, pi
from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
QPen, QStyle, QPainter, QLineEdit, QApplication, \
QPalette
QPalette, QImage
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
QCoreApplication, SIGNAL, QObject, QSize, QModelIndex
QCoreApplication, SIGNAL, QObject, QSize, QModelIndex, \
QTimer
from calibre import Settings, preferred_encoding
from calibre.ptempfile import PersistentTemporaryFile
@ -92,15 +94,25 @@ def roman(cls, num):
num -= d
return ''.join(result)
def __init__(self, parent=None):
def __init__(self, parent=None, buffer=20):
QAbstractTableModel.__init__(self, parent)
self.db = None
self.cols = ['title', 'authors', 'size', 'date', 'rating', 'publisher', 'tags', 'series']
self.editable_cols = [0, 1, 4, 5, 6]
self.default_image = QImage(':/images/book.svg')
self.sorted_on = (3, Qt.AscendingOrder)
self.last_search = '' # The last search performed on this model
self.read_config()
self.buffer_size = buffer
self.clear_caches()
self.load_timer = QTimer()
self.connect(self.load_timer, SIGNAL('timeout()'), self.load)
self.load_timer.start(50)
def clear_caches(self):
self.buffer = {}
self.load_queue = deque()
def read_config(self):
self.use_roman_numbers = bool(Settings().value('use roman numerals for series number',
QVariant(True)).toBool())
@ -164,6 +176,7 @@ def search(self, text, refinement, reset=True):
self.db.filter(tokens, refilter=refinement, OR=OR)
self.last_search = text
if reset:
self.clear_caches()
self.reset()
def sort(self, col, order, reset=True):
@ -173,6 +186,7 @@ def sort(self, col, order, reset=True):
self.db.refresh(self.cols[col], ascending)
self.research()
if reset:
self.clear_caches()
self.reset()
self.sorted_on = (col, order)
@ -194,10 +208,32 @@ def columnCount(self, parent):
def rowCount(self, parent):
return self.db.rows() if self.db else 0
def count(self):
return self.rowCount(None)
def load(self):
if self.load_queue:
index = self.load_queue.popleft()
if self.buffer.has_key(index):
return
data = self.db.cover(index)
img = QImage()
img.loadFromData(data)
if img.isNull():
img = self.default_image
self.buffer[index] = img
def current_changed(self, current, previous, emit_signal=True):
data = {}
idx = current.row()
cdata = self.db.cover(idx)
cdata = self.cover(idx)
for key in self.buffer.keys():
if abs(key - idx) > self.buffer_size:
self.buffer.pop(key)
for i in range(max(0, idx-self.buffer_size), min(self.count(), idx+self.buffer_size)):
if not self.buffer.has_key(i):
self.load_queue.append(i)
if cdata:
data['cover'] = cdata
tags = self.db.tags(idx)
@ -296,7 +332,15 @@ def title(self, row_number):
return self.db.title(row_number)
def cover(self, row_number):
return self.db.cover(row_number)
img = self.buffer.get(row_number, -1)
if img == -1:
data = self.db.cover(row_number)
img = QImage()
img.loadFromData(data)
if img.isNull():
img = self.default_image
self.buffer[row_number] = img
return img
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:

View file

@ -5,7 +5,7 @@
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
QVariant, QThread, QString
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
QToolButton, QDialog
QToolButton, QDialog, QSizePolicy
from PyQt4.QtSvg import QSvgRenderer
from calibre import __version__, __appname__, islinux, sanitize_file_name, launch, \
@ -19,7 +19,7 @@
pixmap_to_data, choose_dir, ORG_NAME, \
qstring_to_unicode, set_sidebar_directories, \
SingleApplication, Application
from calibre.gui2.cover_flow import CoverFlow
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages
from calibre.library.database import LibraryDatabase
from calibre.gui2.update import CheckForUpdates
from calibre.gui2.main_window import MainWindow
@ -199,6 +199,25 @@ def __init__(self, single_instance, parent=None):
self.library_view.resizeRowsToContents()
self.search.setFocus(Qt.OtherFocusReason)
########################### Cover Flow ################################
self.cover_flow = None
if CoverFlow is not None:
self.cover_flow = CoverFlow(height=220)
self.cover_flow.setVisible(False)
self.library.layout().addWidget(self.cover_flow)
self.connect(self.cover_flow, SIGNAL('currentChanged(int)'), self.sync_cf_to_listview)
self.library_view.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Expanding))
self.connect(self.cover_flow, SIGNAL('itemActivated(int)'), self.show_book_info)
self.connect(self.status_bar.cover_flow_button, SIGNAL('toggled(bool)'), self.toggle_cover_flow)
QObject.connect(self.library_view.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
self.sync_cf_to_listview)
self.db_images = DatabaseImages(self.library_view.model())
self.cover_flow.setImages(self.db_images)
else:
self.status_bar.cover_flow_button.disable(pictureflowerror)
####################### Setup device detection ########################
self.detector = DeviceDetector(sleep_time=2000)
QObject.connect(self.detector, SIGNAL('connected(PyQt_PyObject, PyQt_PyObject)'),
@ -207,6 +226,29 @@ def __init__(self, single_instance, parent=None):
self.news_menu.set_custom_feeds(self.library_view.model().db.get_feeds())
def toggle_cover_flow(self, show):
if show:
self.cover_flow.setCurrentSlide(self.library_view.currentIndex().row())
self.cover_flow.setVisible(True)
self.cover_flow.setFocus(Qt.OtherFocusReason)
self.status_bar.book_info.book_data.setMaximumHeight(100)
self.status_bar.setMaximumHeight(120)
self.library_view.scrollTo(self.library_view.currentIndex())
else:
self.cover_flow.setVisible(False)
self.status_bar.book_info.book_data.setMaximumHeight(1000)
self.status_bar.setMaximumHeight(1200)
def sync_cf_to_listview(self, index, *args):
if not hasattr(index, 'row') and self.library_view.currentIndex().row() != index:
index = self.library_view.model().index(index, 0)
self.library_view.setCurrentIndex(index)
if hasattr(index, 'row') and self.cover_flow.isVisible() and self.cover_flow.currentSlide() != index.row():
self.cover_flow.setCurrentSlide(index.row())
def another_instance_wants_to_talk(self, msg):
if msg.startswith('launched:'):
argv = eval(msg[len('launched:'):])
@ -942,7 +984,7 @@ def do_config(self):
################################ Book info #################################
def show_book_info(self):
def show_book_info(self, *args):
if self.current_view() is not self.library_view:
error_dialog(self, _('No detailed info available'),
_('No detailed information is available for books on the device.')).exec_()

View file

@ -30,7 +30,7 @@
# Add the library we are wrapping. The name doesn't include any platform
# specific prefixes or extensions (e.g. the "lib" prefix on UNIX, or the
# ".dll" extension on Windows).
makefile.extra_lib_dirs = ['../..', '..\\..\\release']
makefile.extra_lib_dirs = ['..\\..\\.build\\release', '../../.build']
makefile.extra_libs = ['pictureflow0' if 'win' in sys.platform and 'darwin' not in sys.platform else "pictureflow"]
makefile.extra_cflags = ['-arch i386', '-arch ppc'] if 'darwin' in sys.platform else []
makefile.extra_cxxflags = makefile.extra_cflags

View file

@ -3,7 +3,7 @@
import textwrap
from PyQt4.QtGui import QStatusBar, QMovie, QLabel, QFrame, QHBoxLayout, QPixmap, \
QVBoxLayout, QSizePolicy
QVBoxLayout, QSizePolicy, QToolButton, QIcon
from PyQt4.QtCore import Qt, QSize, SIGNAL
from calibre import fit_image
from calibre.gui2 import qstring_to_unicode
@ -64,13 +64,7 @@ def mouseReleaseEvent(self, ev):
def show_data(self, data):
if data.has_key('cover'):
cover_data = data.pop('cover')
pixmap = QPixmap()
pixmap.loadFromData(cover_data)
if pixmap.isNull():
self.cover_display.setPixmap(self.cover_display.default_pixmap)
else:
self.cover_display.setPixmap(pixmap)
self.cover_display.setPixmap(QPixmap.fromImage(data.pop('cover')))
else:
self.cover_display.setPixmap(self.cover_display.default_pixmap)
@ -120,16 +114,39 @@ def mouseReleaseEvent(self, event):
self.jobs_dialog.jobs_view.read_settings()
self.jobs_dialog.show()
self.jobs_dialog.jobs_view.restore_column_widths()
class CoverFlowButton(QToolButton):
def __init__(self, parent=None):
QToolButton.__init__(self, parent)
self.setIconSize(QSize(80, 80))
self.setIcon(QIcon(':/images/cover_flow.svg'))
self.setCheckable(True)
self.setChecked(False)
self.setAutoRaise(True)
self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding))
self.connect(self, SIGNAL('toggled(bool)'), self.adjust_tooltip)
self.adjust_tooltip(False)
def adjust_tooltip(self, on):
tt = _('Click to turn off Cover Browsing') if on else _('Click to browse books by their covers')
self.setToolTip(tt)
def disable(self, reason):
self.setDisabled(True)
self.setToolTip(_('<p>Browsing books by their covers is disabled.<br>Import of pictureflow module failed:<br>')+reason)
class StatusBar(QStatusBar):
def __init__(self, jobs_dialog):
QStatusBar.__init__(self)
self.movie_button = MovieButton(QMovie(':/images/jobs-animated.mng'), jobs_dialog)
self.cover_flow_button = CoverFlowButton()
self.addPermanentWidget(self.cover_flow_button)
self.addPermanentWidget(self.movie_button)
self.book_info = BookInfoDisplay(self.clearMessage)
self.connect(self.book_info, SIGNAL('show_book_info()'), self.show_book_info)
self.addWidget(self.book_info)
self.setMinimumHeight(120)
def reset_info(self):
self.book_info.show_data({})

View file

@ -539,26 +539,27 @@ class BuildEXE(build_exe):
'''
def build_plugins(self):
cwd = os.getcwd()
dd = os.path.join(cwd, self.dist_dir)
try:
os.chdir(os.path.join('src', 'calibre', 'gui2', 'pictureflow'))
if os.path.exists('release'):
shutil.rmtree('release')
if os.path.exists('debug'):
shutil.rmtree('debug')
subprocess.check_call(['qmake', 'pictureflow.pro'])
if os.path.exists('.build'):
shutil.rmtree('.build')
os.mkdir('.build')
os.chdir('.build')
subprocess.check_call(['qmake', '../pictureflow.pro'])
subprocess.check_call(['mingw32-make', '-f', 'Makefile.Release'])
os.chdir('PyQt')
shutil.copyfile('release\\pictureflow0.dll', os.path.join(dd, 'pictureflow0.dll'))
os.chdir('..\\PyQt')
if not os.path.exists('.build'):
os.mkdir('.build')
os.chdir('.build')
subprocess.check_call(['python', '..\\configure.py'])
subprocess.check_call(['mingw32-make', '-f', 'Makefile'])
dd = os.path.join(cwd, self.dist_dir)
shutil.copyfile('pictureflow.pyd', os.path.join(dd, 'pictureflow.pyd'))
os.chdir('..\\..')
shutil.copyfile('release\\pictureflow0.dll', os.path.join(dd, 'pictureflow0.dll'))
shutil.rmtree('Release', True)
shutil.rmtree('Debug', True)
os.chdir('..')
shutil.rmtree('.build')
os.chdir('..')
shutil.rmtree('.build')
finally:
os.chdir(cwd)