mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
More CreateNewCustomColumn stuff.
- Improved documentation - Check column headings for duplicates - Method to return the current column headings as a dict - Improved exception handling
This commit is contained in:
parent
fbb5208aac
commit
9a95d8b0c2
@ -676,24 +676,48 @@ class CreateNewCustomColumn(object):
|
||||
"""
|
||||
Provide an API to create new custom columns.
|
||||
|
||||
Use the create_column() method to open a dialog to create a new custom
|
||||
column with given lookup_name, column_heading, datatype, and is_multiple.
|
||||
The lookup name must begin with a '#'. The datatype must be valid and
|
||||
is_multiple must be valid for that datatype. The user cannot change
|
||||
the datatype.
|
||||
Usage:
|
||||
from calibre.gui2.preferences.create_custom_column import CreateNewCustomColumn
|
||||
creator = CreateNewCustomColumn(gui)
|
||||
if creator.must_restart():
|
||||
...
|
||||
else:
|
||||
result = creator.create_column(....)
|
||||
if result[0] == creator.Result.COLUMN_ADDED:
|
||||
|
||||
If generate_unused_lookup_name is False then the provided lookup_name must
|
||||
not already exist. If generate_unused_lookup_name is True then if necessary
|
||||
the method will add the suffix '_n' to the provided lookup_name to allocate
|
||||
an unused lookup_name, where 'n' is an integer. It could be that if a new
|
||||
lookup name is generated then the user will be required to change the
|
||||
column heading to make it is unique.
|
||||
The parameter 'gui' passed when creating a class instance is the main
|
||||
calibre gui (calibre.gui2.ui.get_gui())
|
||||
|
||||
Use the create_column(...) method to open a dialog to create a new custom
|
||||
column with given lookup_name, column_heading, datatype, and is_multiple.
|
||||
You can create as many columns as you wish with a single instance of the
|
||||
CreateNewCustomColumn class. Subsequent class instances will refuse to
|
||||
create columns until calibre is restarted, as will calibre Preferences.
|
||||
|
||||
The lookup name must begin with a '#'. All remaining characters must be
|
||||
lower case letters, digits or underscores. The character after the '#' must
|
||||
be a letter. The lookup name must not end with the suffix '_index'.
|
||||
|
||||
The datatype must be one of calibre's custom column types: 'bool',
|
||||
'comments', 'composite', 'datetime', 'enumeration', 'float', 'int',
|
||||
'rating', 'series', or 'text'. The datatype can't be changed in the dialog.
|
||||
|
||||
is_multiple tells calibre that the column contains multiple values -- is
|
||||
tags-like. The value True is allowed only for 'composite' and 'text' types.
|
||||
|
||||
If generate_unused_lookup_name is False then the provided lookup_name and
|
||||
column_heading must not already exist. If generate_unused_lookup_name is
|
||||
True then if necessary the method will add the suffix '_n' to the provided
|
||||
lookup_name to allocate an unused lookup_name, where 'n' is an integer.
|
||||
The same processing is applied to column_heading to make it is unique, using
|
||||
the same suffix used for the lookup name if possible. In either case the
|
||||
user can change the column heading in the dialog.
|
||||
|
||||
Set freeze_lookup_name to False if you want to allow the user choose a
|
||||
different lookup name. The user will not be allowed to choose the lookup
|
||||
name of an existing column. The provided lookup_name either must not exist
|
||||
or generate_unused_lookup_name must be True, regardless of the value of
|
||||
freeze_lookup_name.
|
||||
name of an existing column. The provided lookup_name and column_heading
|
||||
either must not exist or generate_unused_lookup_name must be True,
|
||||
regardless of the value of freeze_lookup_name.
|
||||
|
||||
The 'display' parameter is used to pass item- and type-specific information
|
||||
for the column. It is a dict. The easiest way to see the current values for
|
||||
@ -740,21 +764,14 @@ class CreateNewCustomColumn(object):
|
||||
|
||||
You or the user must restart calibre for the column(s) to be actually added.
|
||||
|
||||
Result.EXCEPTION_RAISED is returned if the create dialog raises an exception.
|
||||
This can happen if the display contains illegal values, for example a string
|
||||
where a boolean is required. The string is the exception text. Run calibre
|
||||
in debug mode to see the entire traceback.
|
||||
|
||||
The method returns Result.MUST_RESTART if further calibre configuration has
|
||||
been blocked. You can check for this situation in advance by calling
|
||||
must_restart().
|
||||
|
||||
The parameter 'gui' passed when creating a class instance is the main
|
||||
calibre gui (calibre.gui2.ui.get_gui())
|
||||
|
||||
Usage:
|
||||
from calibre.gui2.preferences.create_custom_column import CreateNewCustomColumn
|
||||
creator = CreateNewCustomColumn(gui)
|
||||
if creator.must_restart():
|
||||
...
|
||||
else:
|
||||
result = creator.create_column(....)
|
||||
if result[0] == creator.Result.COLUMN_ADDED:
|
||||
"""
|
||||
|
||||
class Result(Enum):
|
||||
@ -762,10 +779,12 @@ class CreateNewCustomColumn(object):
|
||||
CANCELED = 1
|
||||
INVALID_KEY = 2
|
||||
DUPLICATE_KEY = 3
|
||||
INVALID_TYPE = 4
|
||||
INVALID_IS_MULTIPLE = 5
|
||||
INVALID_DISPLAY = 6
|
||||
MUST_RESTART = 7
|
||||
DUPLICATE_HEADING = 4
|
||||
INVALID_TYPE = 5
|
||||
INVALID_IS_MULTIPLE = 6
|
||||
INVALID_DISPLAY = 7
|
||||
EXCEPTION_RAISED = 8
|
||||
MUST_RESTART = 9
|
||||
|
||||
def __init__(self, gui):
|
||||
self.gui = gui
|
||||
@ -783,14 +802,28 @@ class CreateNewCustomColumn(object):
|
||||
return (self.Result.MUST_RESTART, _("You must restart calibre before making any more changes"))
|
||||
if not lookup_name.startswith('#'):
|
||||
return (self.Result.INVALID_KEY, _("The lookup name must begin with a '#'"))
|
||||
suffix_number = 1
|
||||
if lookup_name in self.custcols:
|
||||
if not generate_unused_lookup_name:
|
||||
return(self.Result.DUPLICATE_KEY, _("The custom column %s already exists") % lookup_name)
|
||||
for i in range(1, 10000):
|
||||
nk = '%s_%d'%(lookup_name, i)
|
||||
for suffix_number in range(suffix_number, 100000):
|
||||
nk = '%s_%d'%(lookup_name, suffix_number)
|
||||
if nk not in self.custcols:
|
||||
lookup_name = nk
|
||||
break
|
||||
if column_heading:
|
||||
headings = {v['name'] for v in self.custcols.values()}
|
||||
if column_heading in headings:
|
||||
if not generate_unused_lookup_name:
|
||||
return(self.Result.DUPLICATE_HEADING,
|
||||
_("The column heading %s already exists") % column_heading)
|
||||
for i in range(suffix_number, 100000):
|
||||
nh = '%s_%d'%(column_heading, i)
|
||||
if nh not in headings:
|
||||
column_heading = nh
|
||||
break
|
||||
else:
|
||||
column_heading = lookup_name
|
||||
if datatype not in CreateCustomColumn.column_types_map:
|
||||
return(self.Result.INVALID_TYPE,
|
||||
_("The custom column type %s doesn't exist") % datatype)
|
||||
@ -800,10 +833,6 @@ class CreateNewCustomColumn(object):
|
||||
if not isinstance(display, dict):
|
||||
return(self.Result.INVALID_DISPLAY,
|
||||
_("The display parameter must a python dict"))
|
||||
|
||||
if not column_heading:
|
||||
column_heading = lookup_name
|
||||
self.key = lookup_name
|
||||
self.created_count += 1
|
||||
self.custcols[lookup_name] = {
|
||||
'label': lookup_name,
|
||||
@ -814,7 +843,9 @@ class CreateNewCustomColumn(object):
|
||||
'colnum': self.created_count,
|
||||
'is_multiple': is_multiple,
|
||||
}
|
||||
dialog = CreateCustomColumn(self.gui, self, lookup_name, self.gui.library_view.model().orig_headers,
|
||||
try:
|
||||
dialog = CreateCustomColumn(self.gui, self, lookup_name,
|
||||
self.gui.library_view.model().orig_headers,
|
||||
freeze_lookup_name=freeze_lookup_name)
|
||||
if dialog.result() == QDialog.DialogCode.Accepted and self.cc_column_key is not None:
|
||||
cc = self.custcols[lookup_name]
|
||||
@ -825,7 +856,13 @@ class CreateNewCustomColumn(object):
|
||||
is_multiple=cc['is_multiple'],
|
||||
display=cc['display'])
|
||||
self.gui.must_restart_before_config = True
|
||||
return ((self.Result.COLUMN_ADDED, self.cc_column_key))
|
||||
return (self.Result.COLUMN_ADDED, self.cc_column_key)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.custcols.pop(lookup_name, None)
|
||||
return (self.Result.EXCEPTION_RAISED, str(e))
|
||||
self.custcols.pop(lookup_name, None)
|
||||
return (self.Result.CANCELED, _('Canceled'))
|
||||
|
||||
def current_columns(self):
|
||||
@ -847,7 +884,21 @@ class CreateNewCustomColumn(object):
|
||||
doesn't use. See calibre.library.field_metadata.add_custom_field() for the
|
||||
complete list.
|
||||
"""
|
||||
return copy.deepcopy(self.custcols) #deepcopy to prevent users from changing it
|
||||
# deepcopy to prevent users from changing it. The new MappingProxyType
|
||||
# isn't enough because only the top-level dict is immutable, not the
|
||||
# items in the dict.
|
||||
return copy.deepcopy(self.custcols)
|
||||
|
||||
|
||||
def current_headings(self):
|
||||
"""
|
||||
Return the currently defined column headings
|
||||
|
||||
Return the column headings including the ones that haven't yet been
|
||||
created. It is a dict. The key is the heading, the value is the lookup
|
||||
name having that heading.
|
||||
"""
|
||||
return {v['name']:('#' + v['label']) for v in self.custcols.values()}
|
||||
|
||||
def must_restart(self):
|
||||
"""Return true if calibre must be restarted before new columns can be added."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user