%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/preferences/ |
| Current File : //lib/calibre/calibre/gui2/preferences/columns.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import copy, sys
from contextlib import suppress
from qt.core import Qt, QTableWidgetItem, QIcon
from calibre.gui2 import gprefs, Application
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.columns_ui import Ui_Form
from calibre.gui2.preferences.create_custom_column import CreateCustomColumn
from calibre.gui2 import error_dialog, question_dialog
class ConfigWidget(ConfigWidgetBase, Ui_Form):
restart_critical = True
def genesis(self, gui):
self.gui = gui
db = self.gui.library_view.model().db
self.custcols = copy.deepcopy(db.field_metadata.custom_field_metadata())
for k, cc in self.custcols.items():
cc['original_key'] = k
# Using max() in this way requires python 3.4+
self.initial_created_count = max((x['colnum'] for x in self.custcols.values()),
default=0) + 1
self.created_count = self.initial_created_count
self.column_up.clicked.connect(self.up_column)
self.column_down.clicked.connect(self.down_column)
self.del_custcol_button.clicked.connect(self.del_custcol)
self.add_custcol_button.clicked.connect(self.add_custcol)
self.add_col_button.clicked.connect(self.add_custcol)
self.edit_custcol_button.clicked.connect(self.edit_custcol)
self.opt_columns.currentItemChanged.connect(self.set_up_down_enabled)
for signal in ('Activated', 'Changed', 'DoubleClicked', 'Clicked'):
signal = getattr(self.opt_columns, 'item'+signal)
signal.connect(self.columns_changed)
def initialize(self):
ConfigWidgetBase.initialize(self)
self.init_columns()
def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self)
self.init_columns(defaults=True)
self.changed_signal.emit()
def commit(self):
widths = []
for i in range(0, self.opt_columns.columnCount()):
widths.append(self.opt_columns.columnWidth(i))
gprefs.set('custcol-prefs-table-geometry', widths)
rr = ConfigWidgetBase.commit(self)
return self.apply_custom_column_changes() or rr
def init_columns(self, defaults=False):
# Set up columns
self.opt_columns.blockSignals(True)
self.model = model = self.gui.library_view.model()
colmap = list(model.column_map)
state = self.columns_state(defaults)
self.hidden_cols = state['hidden_columns']
positions = state['column_positions']
colmap.sort(key=lambda x: positions[x])
self.opt_columns.clear()
db = model.db
self.field_metadata = db.field_metadata
self.opt_columns.setColumnCount(6)
self.opt_columns.setHorizontalHeaderItem(0, QTableWidgetItem(_('Order')))
self.opt_columns.setHorizontalHeaderItem(1, QTableWidgetItem(_('Column header')))
self.opt_columns.setHorizontalHeaderItem(2, QTableWidgetItem(_('Lookup name')))
self.opt_columns.setHorizontalHeaderItem(3, QTableWidgetItem(_('Type')))
self.opt_columns.setHorizontalHeaderItem(4, QTableWidgetItem(_('Description')))
self.opt_columns.setHorizontalHeaderItem(5, QTableWidgetItem(_('Status')))
self.opt_columns.horizontalHeader().sectionClicked.connect(self.table_sorted)
self.opt_columns.verticalHeader().hide()
self.opt_columns.setRowCount(len(colmap))
self.column_desc = dict(map(lambda x:(CreateCustomColumn.column_types[x]['datatype'],
CreateCustomColumn.column_types[x]['text']),
CreateCustomColumn.column_types))
for row, key in enumerate(colmap):
self.setup_row(row, key, row)
self.initial_row_count = row
self.opt_columns.setSortingEnabled(True)
self.opt_columns.horizontalHeader().setSortIndicator(0, Qt.SortOrder.AscendingOrder)
self.restore_geometry()
self.opt_columns.cellDoubleClicked.connect(self.row_double_clicked)
self.opt_columns.setCurrentCell(0, 1)
self.set_up_down_enabled(self.opt_columns.currentItem(), None)
self.opt_columns.blockSignals(False)
def set_up_down_enabled(self, current_item, _):
h = self.opt_columns.horizontalHeader()
row = current_item.row()
if h.sortIndicatorSection() == 0 and h.sortIndicatorOrder() == Qt.SortOrder.AscendingOrder:
self.column_up.setEnabled(row > 0 and row <= self.initial_row_count)
self.column_down.setEnabled(row < self.initial_row_count)
def columns_changed(self, *args):
self.changed_signal.emit()
def columns_state(self, defaults=False):
if defaults:
return self.gui.library_view.get_default_state()
return self.gui.library_view.get_state()
def table_sorted(self, column):
h = self.opt_columns.horizontalHeader()
enabled = column == 0 and h.sortIndicatorOrder() == Qt.SortOrder.AscendingOrder
self.column_up.setEnabled(enabled)
self.column_down.setEnabled(enabled)
self.opt_columns.scrollTo(self.opt_columns.currentIndex())
self.set_up_down_enabled(self.opt_columns.currentItem(), _)
def row_double_clicked(self, r, c):
self.edit_custcol()
def restore_geometry(self):
geom = gprefs.get('custcol-prefs-table-geometry', None)
if geom is not None and len(geom) == self.opt_columns.columnCount():
with suppress(Exception):
for i in range(0, self.opt_columns.columnCount()):
self.opt_columns.setColumnWidth(i, geom[i])
return
self.opt_columns.resizeColumnsToContents()
def setup_row(self, row, key, order):
flags = Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable
if self.is_custom_key(key):
cc = self.custcols[key]
original_key = cc['original_key']
else:
cc = self.field_metadata[key]
original_key = key
self.opt_columns.setSortingEnabled(False)
item = QTableWidgetItem()
item.setData(Qt.ItemDataRole.DisplayRole, order)
item.setToolTip(str(order))
item.setData(Qt.ItemDataRole.UserRole, key)
item.setFlags(flags)
self.opt_columns.setItem(row, 0, item)
flags |= Qt.ItemFlag.ItemIsUserCheckable
if key == 'ondevice':
item.setFlags(flags & ~Qt.ItemFlag.ItemIsEnabled)
item.setCheckState(Qt.CheckState.PartiallyChecked)
else:
item.setFlags(flags)
item.setCheckState(Qt.CheckState.Unchecked if key in self.hidden_cols else
Qt.CheckState.Checked)
item = QTableWidgetItem(cc['name'])
item.setToolTip(cc['name'])
item.setFlags(flags)
if self.is_custom_key(key):
item.setData(Qt.ItemDataRole.DecorationRole, (QIcon(I('column.png'))))
self.opt_columns.setItem(row, 1, item)
item = QTableWidgetItem(key)
item.setToolTip(key)
item.setFlags(flags)
self.opt_columns.setItem(row, 2, item)
if key == 'title':
coltype = _('Text')
elif key == 'ondevice':
coltype = _('Yes/No with text')
else:
dt = cc['datatype']
if cc['is_multiple']:
if key == 'authors' or cc.get('display', {}).get('is_names', False):
coltype = _('Ampersand separated text, shown in the Tag browser')
else:
coltype = self.column_desc['*' + dt]
else:
coltype = self.column_desc[dt]
item = QTableWidgetItem(coltype)
item.setToolTip(coltype)
item.setFlags(flags)
self.opt_columns.setItem(row, 3, item)
desc = cc['display'].get('description', "")
item = QTableWidgetItem(desc)
item.setToolTip(desc)
item.setFlags(flags)
self.opt_columns.setItem(row, 4, item)
if '*deleted' in cc:
col_status = _('Deleted column. Double-click to undelete it')
elif self.is_new_custom_column(cc):
col_status = _('New column')
elif original_key != key:
col_status = _('Edited. Lookup name was {}').format(original_key)
elif '*edited' in cc:
col_status = _('Edited')
else:
col_status = ''
item = QTableWidgetItem(col_status)
item.setToolTip(col_status)
item.setFlags(flags)
self.opt_columns.setItem(row, 5, item)
self.opt_columns.setSortingEnabled(True)
def up_column(self):
self.opt_columns.setSortingEnabled(False)
row = self.opt_columns.currentRow()
if row > 0:
for i in range(0, self.opt_columns.columnCount()):
lower = self.opt_columns.takeItem(row-1, i)
upper = self.opt_columns.takeItem(row, i)
self.opt_columns.setItem(row, i, lower)
self.opt_columns.setItem(row-1, i, upper)
self.setup_row(row-1, self.opt_columns.item(row-1, 2).text(), row-1)
self.setup_row(row, self.opt_columns.item(row, 2).text(), row)
self.opt_columns.setCurrentCell(row-1, 1)
self.changed_signal.emit()
self.opt_columns.setSortingEnabled(True)
def down_column(self):
self.opt_columns.setSortingEnabled(False)
row = self.opt_columns.currentRow()
if row < self.opt_columns.rowCount()-1:
for i in range(0, self.opt_columns.columnCount()):
lower = self.opt_columns.takeItem(row, i)
upper = self.opt_columns.takeItem(row+1, i)
self.opt_columns.setItem(row+1, i, lower)
self.opt_columns.setItem(row, i, upper)
self.setup_row(row+1, self.opt_columns.item(row+1, 2).text(), row+1)
self.setup_row(row, self.opt_columns.item(row, 2).text(), row)
self.opt_columns.setCurrentCell(row+1, 1)
self.changed_signal.emit()
self.opt_columns.setSortingEnabled(True)
def is_new_custom_column(self, cc):
return 'colnum' in cc and cc['colnum'] >= self.initial_created_count
def set_new_custom_column(self, cc):
self.created_count += 1
cc['colnum'] = self.created_count
def del_custcol(self):
row = self.opt_columns.currentRow()
if row < 0:
return error_dialog(self, '', _('You must select a column to delete it'),
show=True)
key = str(self.opt_columns.item(row, 0).data(Qt.ItemDataRole.UserRole) or '')
if key not in self.custcols:
return error_dialog(self, '',
_('The selected column is not a custom column'), show=True)
if not question_dialog(self, _('Are you sure?'),
_('Do you really want to delete column %s and all its data?') %
self.custcols[key]['name'], show_copy_button=False):
return
if self.is_new_custom_column(self.custcols[key]):
del self.custcols[key] # A newly-added column was deleted
self.opt_columns.removeRow(row)
else:
self.custcols[key]['*deleted'] = True
self.setup_row(row, key, self.column_order_val(row))
self.changed_signal.emit()
def add_custcol(self):
model = self.gui.library_view.model()
CreateCustomColumn(self.gui, self, None, model.orig_headers)
if self.cc_column_key is None:
return
cc = self.custcols[self.cc_column_key]
self.set_new_custom_column(cc)
cc['original_key'] = self.cc_column_key
row = self.opt_columns.rowCount()
o = self.opt_columns
o.setRowCount(row + 1)
self.setup_row(row, self.cc_column_key, row)
# We need to find the new item after sorting
for i in range(0, o.rowCount()):
if self.column_order_val(i) == row:
o.setCurrentCell(i, 1)
o.scrollTo(o.currentIndex())
break
self.changed_signal.emit()
def label_to_lookup_name(self, label):
return '#' + label
def is_custom_key(self, key):
return key.startswith('#')
def column_order_val(self, row):
return int(self.opt_columns.item(row, 0).text())
def edit_custcol(self):
model = self.gui.library_view.model()
row = self.opt_columns.currentRow()
try:
key = str(self.opt_columns.item(row, 0).data(Qt.ItemDataRole.UserRole))
if key not in self.custcols:
return error_dialog(self, '',
_('The selected column is not a user-defined column'),
show=True)
cc = self.custcols[key]
if '*deleted' in cc:
if question_dialog(self, _('Undelete the column?'),
_('The column is to be deleted. Do you want to undelete it?'),
show_copy_button=False):
cc.pop('*deleted', None)
self.setup_row(row, key, self.column_order_val(row))
return
CreateCustomColumn(self.gui, self,
self.label_to_lookup_name(self.custcols[key]['label']),
model.orig_headers)
new_key = self.cc_column_key
if new_key is None:
return
if key != new_key:
self.custcols[new_key] = self.custcols[key]
self.custcols.pop(key, None)
cc = self.custcols[new_key]
if self.is_new_custom_column(cc):
cc.pop('*edited', None)
self.setup_row(row, new_key, self.column_order_val(row))
self.opt_columns.scrollTo(self.opt_columns.currentIndex())
self.changed_signal.emit()
except:
import traceback
traceback.print_exc()
def apply_custom_column_changes(self):
model = self.gui.library_view.model()
db = model.db
self.opt_columns.sortItems(0, Qt.SortOrder.AscendingOrder)
config_cols = [str(self.opt_columns.item(i, 0).data(Qt.ItemDataRole.UserRole) or '')
for i in range(self.opt_columns.rowCount())]
if not config_cols:
config_cols = ['title']
removed_cols = set(model.column_map) - set(config_cols)
hidden_cols = {str(self.opt_columns.item(i, 0).data(Qt.ItemDataRole.UserRole) or '')
for i in range(self.opt_columns.rowCount())
if self.opt_columns.item(i, 0).checkState()==Qt.CheckState.Unchecked}
hidden_cols = hidden_cols.union(removed_cols) # Hide removed cols
hidden_cols = list(hidden_cols.intersection(set(model.column_map)))
if 'ondevice' in hidden_cols:
hidden_cols.remove('ondevice')
def col_pos(x):
return config_cols.index(x) if x in config_cols else sys.maxsize
positions = {}
for i, col in enumerate(sorted(model.column_map, key=col_pos)):
positions[col] = i
state = {'hidden_columns': hidden_cols, 'column_positions':positions}
self.gui.library_view.apply_state(state)
self.gui.library_view.save_state()
must_restart = False
for cc in self.custcols.values():
if '*deleted' in cc:
db.delete_custom_column(label=cc['label'])
must_restart = True
elif '*edited' in cc:
db.set_custom_column_metadata(cc['colnum'], name=cc['name'],
label=cc['label'],
display=cc['display'],
notify=False)
if '*must_restart' in cc:
must_restart = True
elif self.is_new_custom_column(cc):
db.create_custom_column(label=cc['label'], name=cc['name'],
datatype=cc['datatype'], is_multiple=cc['is_multiple'],
display=cc['display'])
must_restart = True
return must_restart
if __name__ == '__main__':
app = Application([])
test_widget('Interface', 'Custom Columns')