Another attempt at enhancement #1415714. The user has the choice of putting quickview in its own window (original implementation) or as a splitter panel containing library view and the quickview panes.

This commit is contained in:
Charles Haley 2015-01-30 13:33:11 +01:00
parent 9e7d1a6ed4
commit f26495354f
4 changed files with 151 additions and 28 deletions

View File

@ -25,6 +25,7 @@ class ShowQuickviewAction(InterfaceAction):
def show_quickview(self, *args): def show_quickview(self, *args):
if self.current_instance: if self.current_instance:
if not self.current_instance.is_closed: if not self.current_instance.is_closed:
self.current_instance.set_focus()
return return
self.current_instance = None self.current_instance = None
if self.gui.current_view() is not self.gui.library_view: if self.gui.current_view() is not self.gui.library_view:
@ -34,8 +35,7 @@ class ShowQuickviewAction(InterfaceAction):
return return
index = self.gui.library_view.currentIndex() index = self.gui.library_view.currentIndex()
if index.isValid(): if index.isValid():
self.current_instance = \ self.current_instance = Quickview(self.gui, index)
Quickview(self.gui, self.gui.library_view, index)
self.current_instance.show() self.current_instance.show()
def change_quickview_column(self, idx): def change_quickview_column(self, idx):

View File

@ -5,8 +5,8 @@ __docformat__ = 'restructuredtext en'
from PyQt5.Qt import (Qt, QDialog, QAbstractItemView, QTableWidgetItem, from PyQt5.Qt import (Qt, QDialog, QAbstractItemView, QTableWidgetItem,
QListWidgetItem, QByteArray, QCoreApplication, QListWidgetItem, QCoreApplication, QEvent, QObject,
QApplication, pyqtSignal, QDialogButtonBox) QApplication, pyqtSignal, QDialogButtonBox, QByteArray)
from calibre.customize.ui import find_plugin from calibre.customize.ui import find_plugin
from calibre.gui2 import gprefs from calibre.gui2 import gprefs
@ -42,33 +42,52 @@ class TableItem(QTableWidgetItem):
return self.sort_idx < other.sort_idx return self.sort_idx < other.sort_idx
return 0 return 0
class KeyPressFilter(QObject):
return_pressed_signal = pyqtSignal()
def eventFilter(self, obj, event):
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Return:
self.return_pressed_signal.emit()
return True;
else:
return QObject.eventFilter(self, obj, event);
class Quickview(QDialog, Ui_Quickview): class Quickview(QDialog, Ui_Quickview):
change_quickview_column = pyqtSignal(object) change_quickview_column = pyqtSignal(object)
def __init__(self, gui, view, row): def __init__(self, gui, row):
QDialog.__init__(self, gui, flags=Qt.Window) self.is_pane = gprefs.get('quickview_is_pane', False)
if not self.is_pane:
QDialog.__init__(self, gui, flags=Qt.Window)
else:
QDialog.__init__(self, gui)
Ui_Quickview.__init__(self) Ui_Quickview.__init__(self)
self.setupUi(self) self.setupUi(self)
self.isClosed = False self.isClosed = False
self.books_table_column_widths = None self.books_table_column_widths = None
try: try:
self.books_table_column_widths = \ self.books_table_column_widths = \
gprefs.get('quickview_dialog_books_table_widths', None) gprefs.get('quickview_dialog_books_table_widths', None)
geom = gprefs.get('quickview_dialog_geometry', bytearray('')) if not self.is_pane:
self.restoreGeometry(QByteArray(geom)) geom = gprefs.get('quickview_dialog_geometry', bytearray(''))
self.restoreGeometry(QByteArray(geom))
except: except:
pass pass
# Remove the help button from the window title bar if not self.is_pane:
icon = self.windowIcon() # Remove the help button from the window title bar
self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint)) icon = self.windowIcon()
self.setWindowFlags(self.windowFlags()|Qt.WindowStaysOnTopHint) self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint))
self.setWindowIcon(icon) self.setWindowFlags(self.windowFlags()|Qt.WindowStaysOnTopHint)
self.setWindowIcon(icon)
self.db = view.model().db self.view = gui.library_view
self.view = view self.db = self.view.model().db
self.gui = gui self.gui = gui
self.is_closed = False self.is_closed = False
self.current_book_id = None self.current_book_id = None
@ -89,6 +108,9 @@ class Quickview(QDialog, Ui_Quickview):
self.items.currentTextChanged.connect(self.item_selected) self.items.currentTextChanged.connect(self.item_selected)
# Set up the books table columns # Set up the books table columns
return_filter = KeyPressFilter(self.books_table)
return_filter.return_pressed_signal.connect(self.return_pressed)
self.books_table.installEventFilter(return_filter)
self.books_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.books_table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.books_table.setSelectionMode(QAbstractItemView.SingleSelection) self.books_table.setSelectionMode(QAbstractItemView.SingleSelection)
self.books_table.setColumnCount(3) self.books_table.setColumnCount(3)
@ -118,10 +140,28 @@ class Quickview(QDialog, Ui_Quickview):
self.change_quickview_column.connect(self.slave) self.change_quickview_column.connect(self.slave)
QCoreApplication.instance().aboutToQuit.connect(self.save_state) QCoreApplication.instance().aboutToQuit.connect(self.save_state)
self.search_button.clicked.connect(self.do_search) self.search_button.clicked.connect(self.do_search)
view.model().new_bookdisplay_data.connect(self.book_was_changed) self.view.model().new_bookdisplay_data.connect(self.book_was_changed)
close_button = self.buttonBox.button(QDialogButtonBox.Close) close_button = self.buttonBox.button(QDialogButtonBox.Close)
close_button.setAutoDefault(False) close_button.setDefault(False)
if self.is_pane:
close_button.setText(_('&Close'))
self.books_table.horizontalHeader().sectionResized.connect(self.section_resized)
if self.is_pane:
self.gui.quickview_splitter.add_quickview_dialog(self)
self.set_focus()
self.show_as_pane.setChecked(self.is_pane)
self.show_as_pane.stateChanged.connect(self.show_as_pane_changed)
def show(self):
QDialog.show(self)
if self.is_pane:
self.gui.quickview_splitter.show_quickview_widget()
def show_as_pane_changed(self, new_state):
gprefs['quickview_is_pane'] = self.show_as_pane.isChecked()
# search button # search button
def do_search(self): def do_search(self):
@ -261,6 +301,11 @@ class Quickview(QDialog, Ui_Quickview):
# correct until the first paint. # correct until the first paint.
def resizeEvent(self, *args): def resizeEvent(self, *args):
QDialog.resizeEvent(self, *args) QDialog.resizeEvent(self, *args)
# Do this if we are resizing for the first time to reset state.
if self.is_pane and self.height() == 0:
self.gui.quickview_splitter.set_sizes()
if self.books_table_column_widths is not None: if self.books_table_column_widths is not None:
for c,w in enumerate(self.books_table_column_widths): for c,w in enumerate(self.books_table_column_widths):
self.books_table.setColumnWidth(c, w) self.books_table.setColumnWidth(c, w)
@ -274,9 +319,15 @@ class Quickview(QDialog, Ui_Quickview):
self.books_table.setColumnWidth(c, w) self.books_table.setColumnWidth(c, w)
self.save_state() self.save_state()
def return_pressed(self):
self.select_book(self.books_table.currentRow())
def book_doubleclicked(self, row, column): def book_doubleclicked(self, row, column):
if self.no_valid_items: if self.no_valid_items:
return return
self.select_book(row)
def select_book(self, row):
book_id = int(self.books_table.item(row, self.title_column).data(Qt.UserRole)) book_id = int(self.books_table.item(row, self.title_column).data(Qt.UserRole))
self.view.select_rows([book_id]) self.view.select_rows([book_id])
modifiers = int(QApplication.keyboardModifiers()) modifiers = int(QApplication.keyboardModifiers())
@ -285,6 +336,9 @@ class Quickview(QDialog, Ui_Quickview):
if em is not None: if em is not None:
em.actual_plugin_.edit_metadata(None) em.actual_plugin_.edit_metadata(None)
def set_focus(self):
self.books_table.setFocus(Qt.ActiveWindowFocusReason)
# called when a book is clicked on the library view # called when a book is clicked on the library view
def slave(self, current): def slave(self, current):
if self.is_closed: if self.is_closed:
@ -292,6 +346,9 @@ class Quickview(QDialog, Ui_Quickview):
self.refresh(current) self.refresh(current)
self.view.activateWindow() self.view.activateWindow()
def section_resized(self, logicalIndex, oldSize, newSize):
self.save_state()
def save_state(self): def save_state(self):
if self.is_closed: if self.is_closed:
return return
@ -299,7 +356,8 @@ class Quickview(QDialog, Ui_Quickview):
for c in range(0, self.books_table.columnCount()): for c in range(0, self.books_table.columnCount()):
self.books_table_column_widths.append(self.books_table.columnWidth(c)) self.books_table_column_widths.append(self.books_table.columnWidth(c))
gprefs['quickview_dialog_books_table_widths'] = self.books_table_column_widths gprefs['quickview_dialog_books_table_widths'] = self.books_table_column_widths
gprefs['quickview_dialog_geometry'] = bytearray(self.saveGeometry()) if not self.is_pane:
gprefs['quickview_dialog_geometry'] = bytearray(self.saveGeometry())
def _close(self): def _close(self):
self.save_state() self.save_state()
@ -307,12 +365,10 @@ class Quickview(QDialog, Ui_Quickview):
self.db = self.view = self.gui = None self.db = self.view = self.gui = None
self.is_closed = True self.is_closed = True
# called by the window system
def closeEvent(self, *args):
self._close()
QDialog.closeEvent(self, *args)
# called by the close button # called by the close button
def reject(self): def reject(self):
if self.is_pane:
self.gui.quickview_splitter.hide_quickview_widget()
self.gui.library_view.setFocus(Qt.ActiveWindowFocusReason)
self._close() self._close()
QDialog.reject(self) QDialog.reject(self)

View File

@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@ -68,8 +68,33 @@
<string>&amp;Lock Quickview contents</string> <string>&amp;Lock Quickview contents</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Select to prevent Quickview from changing content when the <string>&lt;p&gt;Select to prevent Quickview from changing content when the
selection on the library view is changed</string> selection on the library view is changed&lt;/p&gt;</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="show_as_pane">
<property name="text">
<string>Sho&amp;w as pane</string>
</property>
<property name="toolTip">
<string>&lt;p&gt;If checked, the next time Quickview is opened it will be shown
as a pane under the library view. If unchecked it will be shown as a
separate window.&lt;/p&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -8,7 +8,8 @@ __docformat__ = 'restructuredtext en'
import functools import functools
from PyQt5.Qt import (Qt, QApplication, QStackedWidget, QMenu, QTimer, from PyQt5.Qt import (Qt, QApplication, QStackedWidget, QMenu, QTimer,
QSize, QSizePolicy, QStatusBar, QLabel, QFont, QAction, QTabBar) QSize, QSizePolicy, QStatusBar, QLabel, QFont, QAction, QTabBar,
QVBoxLayout, QWidget, QSplitter)
from calibre.utils.config import prefs from calibre.utils.config import prefs
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -99,6 +100,37 @@ class LibraryViewMixin(object): # {{{
# }}} # }}}
class QuickviewSplitter(QSplitter): # {{{
def __init__(self, parent=None, orientation=Qt.Vertical, qv_widget=None):
QSplitter.__init__(self, parent=parent, orientation=orientation)
self.splitterMoved.connect(self.splitter_moved)
self.setChildrenCollapsible(False)
self.qv_widget = qv_widget
def splitter_moved(self):
gprefs['quickview_dialog_heights'] = self.sizes()
def resizeEvent(self, *args):
QSplitter.resizeEvent(self, *args)
if self.sizes()[1] != 0:
gprefs['quickview_dialog_heights'] = self.sizes()
def set_sizes(self):
sizes = gprefs.get('quickview_dialog_heights', [])
if len(sizes) == 2:
self.setSizes(sizes)
def add_quickview_dialog(self, qv_dialog):
self.qv_widget.layout().addWidget(qv_dialog)
def show_quickview_widget(self):
self.qv_widget.show()
def hide_quickview_widget(self):
self.qv_widget.hide()
# }}}
class LibraryWidget(Splitter): # {{{ class LibraryWidget(Splitter): # {{{
def __init__(self, parent): def __init__(self, parent):
@ -113,6 +145,10 @@ class LibraryWidget(Splitter): # {{{
connect_button=not config['separate_cover_flow'], connect_button=not config['separate_cover_flow'],
side_index=idx, initial_side_size=size, initial_show=False, side_index=idx, initial_side_size=size, initial_show=False,
shortcut='Shift+Alt+B') shortcut='Shift+Alt+B')
quickview_widget = QWidget()
parent.quickview_splitter = QuickviewSplitter(
parent=self, orientation=Qt.Vertical, qv_widget=quickview_widget)
parent.library_view = BooksView(parent) parent.library_view = BooksView(parent)
parent.library_view.setObjectName('library_view') parent.library_view.setObjectName('library_view')
stack = QStackedWidget(self) stack = QStackedWidget(self)
@ -121,7 +157,13 @@ class LibraryWidget(Splitter): # {{{
parent.grid_view = GridView(parent) parent.grid_view = GridView(parent)
parent.grid_view.setObjectName('grid_view') parent.grid_view.setObjectName('grid_view')
av.add_view('grid', parent.grid_view) av.add_view('grid', parent.grid_view)
self.addWidget(stack) parent.quickview_splitter.addWidget(stack)
quickview_widget.setLayout(QVBoxLayout())
parent.quickview_splitter.addWidget(quickview_widget)
parent.quickview_splitter.hide_quickview_widget()
self.addWidget(parent.quickview_splitter)
# }}} # }}}
class Stack(QStackedWidget): # {{{ class Stack(QStackedWidget): # {{{