Allow user to set text alignment for columns. Cleanup and fix default column alignment

This commit is contained in:
Kovid Goyal 2010-05-18 09:23:08 -06:00
parent ee374cac13
commit ab33f4eb29
2 changed files with 84 additions and 29 deletions

View File

@ -30,6 +30,9 @@ def human_readable(size, precision=1):
TIME_FMT = '%d %b %Y' TIME_FMT = '%d %b %Y'
ALIGNMENT_MAP = {'left': Qt.AlignLeft, 'right': Qt.AlignRight, 'center':
Qt.AlignHCenter}
class BooksModel(QAbstractTableModel): # {{{ class BooksModel(QAbstractTableModel): # {{{
about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted')
@ -64,6 +67,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.last_search = '' # The last search performed on this model self.last_search = '' # The last search performed on this model
self.column_map = [] self.column_map = []
self.headers = {} self.headers = {}
self.alignment_map = {}
self.buffer_size = buffer self.buffer_size = buffer
self.cover_cache = None self.cover_cache = None
self.bool_yes_icon = QIcon(I('ok.svg')) self.bool_yes_icon = QIcon(I('ok.svg'))
@ -72,6 +76,19 @@ class BooksModel(QAbstractTableModel): # {{{
self.device_connected = False self.device_connected = False
self.read_config() self.read_config()
def change_alignment(self, colname, alignment):
if colname in self.column_map and alignment in ('left', 'right', 'center'):
old = self.alignment_map.get(colname, 'left')
if old == alignment:
return
self.alignment_map.pop(colname, None)
if alignment != 'left':
self.alignment_map[colname] = alignment
col = self.column_map.index(colname)
for row in xrange(self.rowCount(QModelIndex())):
self.dataChanged.emit(self.index(row, col), self.index(row,
col))
def is_custom_column(self, cc_label): def is_custom_column(self, cc_label):
return cc_label in self.custom_columns return cc_label in self.custom_columns
@ -593,14 +610,17 @@ class BooksModel(QAbstractTableModel): # {{{
# the column map does not accurately represent the screen. In these cases, # the column map does not accurately represent the screen. In these cases,
# we will get asked to display columns we don't know about. Must test for this. # we will get asked to display columns we don't know about. Must test for this.
if col >= len(self.column_to_dc_map): if col >= len(self.column_to_dc_map):
return None return NONE
if role in (Qt.DisplayRole, Qt.EditRole): if role in (Qt.DisplayRole, Qt.EditRole):
return self.column_to_dc_map[col](index.row()) return self.column_to_dc_map[col](index.row())
elif role == Qt.DecorationRole: elif role == Qt.DecorationRole:
if self.column_to_dc_decorator_map[col] is not None: if self.column_to_dc_decorator_map[col] is not None:
return self.column_to_dc_decorator_map[index.column()](index.row()) return self.column_to_dc_decorator_map[index.column()](index.row())
#elif role == Qt.TextAlignmentRole and self.column_map[index.column()] in ('size', 'timestamp'): elif role == Qt.TextAlignmentRole:
# return QVariant(Qt.AlignVCenter | Qt.AlignCenter) cname = self.column_map[index.column()]
ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname,
'left')]
return QVariant(ans)
#elif role == Qt.ToolTipRole and index.isValid(): #elif role == Qt.ToolTipRole and index.isValid():
# if self.column_map[index.column()] in self.editable_cols: # if self.column_map[index.column()] in self.editable_cols:
# return QVariant(_("Double click to <b>edit</b> me<br><br>")) # return QVariant(_("Double click to <b>edit</b> me<br><br>"))

View File

@ -59,6 +59,7 @@ class BooksView(QTableView): # {{{
self._model.about_to_be_sorted.connect(self.about_to_be_sorted) self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
self._model.sorting_done.connect(self.sorting_done) self._model.sorting_done.connect(self.sorting_done)
# Column Header Context Menu {{{
def column_header_context_handler(self, action=None, column=None): def column_header_context_handler(self, action=None, column=None):
if not action or not column: if not action or not column:
return return
@ -78,6 +79,9 @@ class BooksView(QTableView): # {{{
self.sortByColumn(idx, Qt.DescendingOrder) self.sortByColumn(idx, Qt.DescendingOrder)
elif action == 'defaults': elif action == 'defaults':
self.apply_state(self.get_default_state()) self.apply_state(self.get_default_state())
elif action.startswith('align_'):
alignment = action.partition('_')[-1]
self._model.change_alignment(column, alignment)
self.save_state() self.save_state()
@ -93,14 +97,22 @@ class BooksView(QTableView): # {{{
name, name,
partial(self.column_header_context_handler, action='hide', partial(self.column_header_context_handler, action='hide',
column=col)) column=col))
self.column_header_context_menu.addAction( m = self.column_header_context_menu.addMenu(
_('Sort on column %s (ascending)') % name, _('Sort on %s') % name)
m.addAction(_('Ascending'),
partial(self.column_header_context_handler, partial(self.column_header_context_handler,
action='ascending', column=col)) action='ascending', column=col))
self.column_header_context_menu.addAction( m.addAction(_('Descending'),
_('Sort on column %s (descending)') % name,
partial(self.column_header_context_handler, partial(self.column_header_context_handler,
action='descending', column=col)) action='descending', column=col))
m = self.column_header_context_menu.addMenu(
_('Change text alignment for %s') % name)
for x, t in (('left', _('Left')), ('right', _('Right')), ('center',
_('Center'))):
m.addAction(t,
partial(self.column_header_context_handler,
action='align_'+x, column=col))
hidden_cols = [self.column_map[i] for i in hidden_cols = [self.column_map[i] for i in
range(self.column_header.count()) if range(self.column_header.count()) if
@ -120,6 +132,7 @@ class BooksView(QTableView): # {{{
partial(self.column_header_context_handler, partial(self.column_header_context_handler,
action='show', column=col)) action='show', column=col))
self.column_header_context_menu.addSeparator() self.column_header_context_menu.addSeparator()
self.column_header_context_menu.addAction( self.column_header_context_menu.addAction(
_('Restore default layout'), _('Restore default layout'),
@ -127,8 +140,9 @@ class BooksView(QTableView): # {{{
action='defaults', column=col)) action='defaults', column=col))
self.column_header_context_menu.popup(self.column_header.mapToGlobal(pos)) self.column_header_context_menu.popup(self.column_header.mapToGlobal(pos))
# }}}
# Sorting {{{
def about_to_be_sorted(self, idc): def about_to_be_sorted(self, idc):
selected_rows = [r.row() for r in self.selectionModel().selectedRows()] selected_rows = [r.row() for r in self.selectionModel().selectedRows()]
self.selected_ids = [idc(r) for r in selected_rows] self.selected_ids = [idc(r) for r in selected_rows]
@ -141,13 +155,9 @@ class BooksView(QTableView): # {{{
for idx in indices: for idx in indices:
sm.select(idx, sm.Select|sm.Rows) sm.select(idx, sm.Select|sm.Rows)
self.selected_ids = [] self.selected_ids = []
# }}}
def scrollContentsBy(self, dx, dy): # Ondevice column {{{
# Needed as Qt bug causes headerview to not always update when scrolling
QTableView.scrollContentsBy(self, dx, dy)
if dy != 0:
self.column_header.update()
def set_ondevice_column_visibility(self): def set_ondevice_column_visibility(self):
m = self._model m = self._model
self.column_header.setSectionHidden(m.column_map.index('ondevice'), self.column_header.setSectionHidden(m.column_map.index('ondevice'),
@ -156,6 +166,7 @@ class BooksView(QTableView): # {{{
def set_device_connected(self, is_connected): def set_device_connected(self, is_connected):
self._model.set_device_connected(is_connected) self._model.set_device_connected(is_connected)
self.set_ondevice_column_visibility() self.set_ondevice_column_visibility()
# }}}
# Save/Restore State {{{ # Save/Restore State {{{
def get_state(self): def get_state(self):
@ -168,6 +179,7 @@ class BooksView(QTableView): # {{{
self.cleanup_sort_history(self.model().sort_history) self.cleanup_sort_history(self.model().sort_history)
state['column_positions'] = {} state['column_positions'] = {}
state['column_sizes'] = {} state['column_sizes'] = {}
state['column_alignment'] = self._model.alignment_map
for i in range(h.count()): for i in range(h.count()):
name = cm[i] name = cm[i]
state['column_positions'][name] = h.visualIndex(i) state['column_positions'][name] = h.visualIndex(i)
@ -211,7 +223,7 @@ class BooksView(QTableView): # {{{
for col, pos in positions.items(): for col, pos in positions.items():
if col in cmap: if col in cmap:
pmap[pos] = col pmap[pos] = col
for pos in sorted(pmap.keys(), reverse=True): for pos in sorted(pmap.keys()):
col = pmap[pos] col = pmap[pos]
idx = cmap[col] idx = cmap[col]
current_pos = h.visualIndex(idx) current_pos = h.visualIndex(idx)
@ -225,18 +237,28 @@ class BooksView(QTableView): # {{{
if sz < 3: if sz < 3:
sz = h.sectionSizeHint(cmap[col]) sz = h.sectionSizeHint(cmap[col])
h.resizeSection(cmap[col], sz) h.resizeSection(cmap[col], sz)
self.apply_sort_history(state.get('sort_history', None)) self.apply_sort_history(state.get('sort_history', None))
for col, alignment in state.get('column_alignment', {}).items():
self._model.change_alignment(col, alignment)
def get_default_state(self): def get_default_state(self):
old_state = {'hidden_columns': [], old_state = {
'hidden_columns': [],
'sort_history':[DEFAULT_SORT], 'sort_history':[DEFAULT_SORT],
'column_positions': {}, 'column_positions': {},
'column_sizes': {}} 'column_sizes': {},
'column_alignment': {
'size':'center',
'timestamp':'center',
'pubdate':'center'},
}
h = self.column_header h = self.column_header
cm = self.column_map cm = self.column_map
for i in range(h.count()): for i in range(h.count()):
name = cm[i] name = cm[i]
old_state['column_positions'][name] = h.logicalIndex(i) old_state['column_positions'][name] = i
if name != 'ondevice': if name != 'ondevice':
old_state['column_sizes'][name] = \ old_state['column_sizes'][name] = \
max(self.sizeHintForColumn(i), h.sectionSizeHint(i)) max(self.sizeHintForColumn(i), h.sectionSizeHint(i))
@ -259,9 +281,15 @@ class BooksView(QTableView): # {{{
# }}} # }}}
@property # Initialization/Delegate Setup {{{
def column_map(self):
return self._model.column_map def set_database(self, db):
self.save_state()
self._model.set_database(db)
self.tags_delegate.set_database(db)
self.authors_delegate.set_auto_complete_function(db.all_authors)
self.series_delegate.set_auto_complete_function(db.all_series)
self.publisher_delegate.set_auto_complete_function(db.all_publishers)
def database_changed(self, db): def database_changed(self, db):
for i in range(self.model().columnCount(None)): for i in range(self.model().columnCount(None)):
@ -299,7 +327,9 @@ class BooksView(QTableView): # {{{
self.restore_state() self.restore_state()
self.set_ondevice_column_visibility() self.set_ondevice_column_visibility()
#}}}
# Context Menu {{{
def set_context_menu(self, edit_metadata, send_to_device, convert, view, def set_context_menu(self, edit_metadata, send_to_device, convert, view,
save, open_folder, book_details, delete, similar_menu=None): save, open_folder, book_details, delete, similar_menu=None):
self.setContextMenuPolicy(Qt.DefaultContextMenu) self.setContextMenuPolicy(Qt.DefaultContextMenu)
@ -324,8 +354,9 @@ class BooksView(QTableView): # {{{
def contextMenuEvent(self, event): def contextMenuEvent(self, event):
self.context_menu.popup(event.globalPos()) self.context_menu.popup(event.globalPos())
event.accept() event.accept()
# }}}
# Drag 'n Drop {{{
@classmethod @classmethod
def paths_from_event(cls, event): def paths_from_event(cls, event):
''' '''
@ -354,13 +385,17 @@ class BooksView(QTableView): # {{{
event.accept() event.accept()
self.files_dropped.emit(paths) self.files_dropped.emit(paths)
def set_database(self, db): # }}}
self.save_state()
self._model.set_database(db) @property
self.tags_delegate.set_database(db) def column_map(self):
self.authors_delegate.set_auto_complete_function(db.all_authors) return self._model.column_map
self.series_delegate.set_auto_complete_function(db.all_series)
self.publisher_delegate.set_auto_complete_function(db.all_publishers) def scrollContentsBy(self, dx, dy):
# Needed as Qt bug causes headerview to not always update when scrolling
QTableView.scrollContentsBy(self, dx, dy)
if dy != 0:
self.column_header.update()
def close(self): def close(self):
self._model.close() self._model.close()