From f51e63718c971d773df3a7767a5b7fe2030d9e06 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 May 2019 20:39:48 +0530 Subject: [PATCH] Get rid of @dynamic_property --- setup.cfg | 2 +- src/calibre/__init__.py | 3 +- src/calibre/db/backend.py | 52 +- src/calibre/devices/cli.py | 94 ++-- src/calibre/devices/usbms/books.py | 25 +- src/calibre/ebooks/covers.py | 31 +- src/calibre/ebooks/lrf/tags.py | 40 +- src/calibre/ebooks/metadata/opf2.py | 485 ++++++++---------- src/calibre/ebooks/metadata/toc.py | 19 +- src/calibre/ebooks/mobi/writer2/indexer.py | 11 +- src/calibre/ebooks/oeb/base.py | 158 +++--- src/calibre/ebooks/oeb/polish/container.py | 22 +- src/calibre/gui2/__init__.py | 18 +- src/calibre/gui2/actions/toc_edit.py | 19 +- src/calibre/gui2/comments_editor.py | 119 ++--- src/calibre/gui2/complete2.py | 44 +- src/calibre/gui2/covers.py | 11 +- src/calibre/gui2/custom_column_widgets.py | 11 +- src/calibre/gui2/device.py | 39 +- src/calibre/gui2/dialogs/custom_recipes.py | 56 +- src/calibre/gui2/dialogs/progress.py | 57 +- src/calibre/gui2/font_family_chooser.py | 19 +- src/calibre/gui2/languages.py | 27 +- src/calibre/gui2/library/views.py | 50 +- src/calibre/gui2/metadata/basic_widgets.py | 333 ++++++------ src/calibre/gui2/metadata/diff.py | 110 ++-- src/calibre/gui2/preferences/coloring.py | 78 ++- src/calibre/gui2/tweak_book/editor/image.py | 70 ++- .../gui2/tweak_book/editor/snippets.py | 57 +- src/calibre/gui2/tweak_book/editor/text.py | 12 +- src/calibre/gui2/tweak_book/editor/widget.py | 62 ++- src/calibre/gui2/tweak_book/preview.py | 17 +- src/calibre/gui2/tweak_book/reports.py | 11 +- src/calibre/gui2/tweak_book/search.py | 188 ++++--- src/calibre/gui2/tweak_book/text_search.py | 41 +- src/calibre/gui2/viewer/config.py | 17 +- src/calibre/gui2/viewer/documentview.py | 108 ++-- src/calibre/gui2/widgets.py | 42 +- src/calibre/gui2/widgets2.py | 39 +- src/calibre/library/database.py | 9 +- src/calibre/library/database2.py | 57 +- src/calibre/utils/magick/legacy.py | 56 +- src/calibre/web/feeds/__init__.py | 37 +- 43 files changed, 1269 insertions(+), 1487 deletions(-) diff --git a/setup.cfg b/setup.cfg index af4550aa0a..85743dbf49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [flake8] max-line-length = 160 -builtins = _,dynamic_property,__,P,I,lopen,icu_lower,icu_upper,icu_title,ngettext,connect_lambda +builtins = _,__,P,I,lopen,icu_lower,icu_upper,icu_title,ngettext,connect_lambda ignore = E12,E203,E22,E231,E241,E401,E402,E731,W391,E722,E741,W504 [yapf] diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 359a6b49e0..6aa14b8473 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -4,9 +4,8 @@ __copyright__ = '2008, Kovid Goyal ' __docformat__ = 'restructuredtext en' import sys, os, re, time, random, warnings -from polyglot.builtins import (builtins, codepoint_to_chr, iteritems, +from polyglot.builtins import (codepoint_to_chr, iteritems, itervalues, unicode_type, range, filter, hasenv) -builtins.__dict__['dynamic_property'] = lambda func: func(None) from math import floor from functools import partial diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index 72a61a6fe4..b3f26afb16 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -1145,17 +1145,14 @@ class DB(object): def vacuum(self): self.execute('VACUUM') - @dynamic_property + @property def user_version(self): - doc = 'The user version of this database' + '''The user version of this database''' + return self.conn.get('pragma user_version;', all=False) - def fget(self): - return self.conn.get('pragma user_version;', all=False) - - def fset(self, val): - self.execute('pragma user_version=%d'%int(val)) - - return property(doc=doc, fget=fget, fset=fset) + @user_version.setter + def user_version(self, val): + self.execute('pragma user_version=%d'%int(val)) def initialize_database(self): metadata_sqlite = P('metadata_sqlite.sql', data=True, @@ -1252,29 +1249,26 @@ class DB(object): def exists_at(cls, path): return path and os.path.exists(os.path.join(path, 'metadata.db')) - @dynamic_property + @property def library_id(self): - doc = ('The UUID for this library. As long as the user only operates' - ' on libraries with calibre, it will be unique') + '''The UUID for this library. As long as the user only operates on libraries with calibre, it will be unique''' - def fget(self): - if getattr(self, '_library_id_', None) is None: - ans = self.conn.get('SELECT uuid FROM library_id', all=False) - if ans is None: - ans = str(uuid.uuid4()) - self.library_id = ans - else: - self._library_id_ = ans - return self._library_id_ + if getattr(self, '_library_id_', None) is None: + ans = self.conn.get('SELECT uuid FROM library_id', all=False) + if ans is None: + ans = str(uuid.uuid4()) + self.library_id = ans + else: + self._library_id_ = ans + return self._library_id_ - def fset(self, val): - self._library_id_ = unicode_type(val) - self.execute(''' - DELETE FROM library_id; - INSERT INTO library_id (uuid) VALUES (?); - ''', (self._library_id_,)) - - return property(doc=doc, fget=fget, fset=fset) + @library_id.setter + def library_id(self, val): + self._library_id_ = unicode_type(val) + self.execute(''' + DELETE FROM library_id; + INSERT INTO library_id (uuid) VALUES (?); + ''', (self._library_id_,)) def last_modified(self): ''' Return last modified time as a UTC datetime object ''' diff --git a/src/calibre/devices/cli.py b/src/calibre/devices/cli.py index e90a88bce1..412fa985ed 100755 --- a/src/calibre/devices/cli.py +++ b/src/calibre/devices/cli.py @@ -31,73 +31,55 @@ class FileFormatter(object): self.name = file.name self.path = file.path - @dynamic_property + @property def mode_string(self): - doc=""" The mode string for this file. There are only two modes read-only and read-write """ + """ The mode string for this file. There are only two modes read-only and read-write """ + mode, x = "-", "-" + if self.is_dir: + mode, x = "d", "x" + if self.is_readonly: + mode += "r-"+x+"r-"+x+"r-"+x + else: + mode += "rw"+x+"rw"+x+"rw"+x + return mode - def fget(self): - mode, x = "-", "-" - if self.is_dir: - mode, x = "d", "x" - if self.is_readonly: - mode += "r-"+x+"r-"+x+"r-"+x - else: - mode += "rw"+x+"rw"+x+"rw"+x - return mode - return property(doc=doc, fget=fget) - - @dynamic_property + @property def isdir_name(self): - doc='''Return self.name + '/' if self is a directory''' + '''Return self.name + '/' if self is a directory''' + name = self.name + if self.is_dir: + name += '/' + return name - def fget(self): - name = self.name - if self.is_dir: - name += '/' - return name - return property(doc=doc, fget=fget) - - @dynamic_property + @property def name_in_color(self): - doc=""" The name in ANSI text. Directories are blue, ebooks are green """ + """ The name in ANSI text. Directories are blue, ebooks are green """ + cname = self.name + blue, green, normal = "", "", "" + if self.term: + blue, green, normal = self.term.BLUE, self.term.GREEN, self.term.NORMAL + if self.is_dir: + cname = blue + self.name + normal + else: + ext = self.name[self.name.rfind("."):] + if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): + cname = green + self.name + normal + return cname - def fget(self): - cname = self.name - blue, green, normal = "", "", "" - if self.term: - blue, green, normal = self.term.BLUE, self.term.GREEN, self.term.NORMAL - if self.is_dir: - cname = blue + self.name + normal - else: - ext = self.name[self.name.rfind("."):] - if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): - cname = green + self.name + normal - return cname - return property(doc=doc, fget=fget) - - @dynamic_property + @property def human_readable_size(self): - doc=""" File size in human readable form """ + """ File size in human readable form """ + return human_readable(self.size) - def fget(self): - return human_readable(self.size) - return property(doc=doc, fget=fget) - - @dynamic_property + @property def modification_time(self): - doc=""" Last modified time in the Linux ls -l format """ + """ Last modified time in the Linux ls -l format """ + return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime)) - def fget(self): - return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime)) - return property(doc=doc, fget=fget) - - @dynamic_property + @property def creation_time(self): - doc=""" Last modified time in the Linux ls -l format """ - - def fget(self): - return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime)) - return property(doc=doc, fget=fget) + """ Last modified time in the Linux ls -l format """ + return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime)) def info(dev): diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 93fb4d8686..44ec03fdee 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -46,26 +46,21 @@ class Book(Metadata): # use lpath because the prefix can change, changing path return self.lpath == getattr(other, 'lpath', None) - @dynamic_property + @property def db_id(self): - doc = '''The database id in the application database that this file corresponds to''' + '''The database id in the application database that this file corresponds to''' - def fget(self): - match = re.search(r'_(\d+)$', self.lpath.rpartition('.')[0]) - if match: - return int(match.group(1)) - return None - return property(fget=fget, doc=doc) + match = re.search(r'_(\d+)$', self.lpath.rpartition('.')[0]) + if match: + return int(match.group(1)) + return None - @dynamic_property + @property def title_sorter(self): - doc = '''String to sort the title. If absent, title is returned''' + '''String to sort the title. If absent, title is returned''' + return title_sort(self.title) - def fget(self): - return title_sort(self.title) - return property(doc=doc, fget=fget) - - @dynamic_property + @property def thumbnail(self): return None diff --git a/src/calibre/ebooks/covers.py b/src/calibre/ebooks/covers.py index 14f9ac71f2..e0c948d1ae 100644 --- a/src/calibre/ebooks/covers.py +++ b/src/calibre/ebooks/covers.py @@ -169,24 +169,23 @@ class Block(object): def height(self): return int(ceil(sum(l if isinstance(l, numbers.Number) else l.boundingRect().height() for l in self.layouts))) - @dynamic_property + @property def position(self): - def fget(self): - return self._position + return self._position - def fset(self, new_pos): - (x, y) = new_pos - self._position = Point(x, y) - if self.layouts: - self.layouts[0].setPosition(QPointF(x, y)) - y += self.layouts[0].boundingRect().height() - for l in self.layouts[1:]: - if isinstance(l, numbers.Number): - y += l - else: - l.setPosition(QPointF(x, y)) - y += l.boundingRect().height() - return property(fget=fget, fset=fset) + @position.setter + def position(self, new_pos): + (x, y) = new_pos + self._position = Point(x, y) + if self.layouts: + self.layouts[0].setPosition(QPointF(x, y)) + y += self.layouts[0].boundingRect().height() + for l in self.layouts[1:]: + if isinstance(l, numbers.Number): + y += l + else: + l.setPosition(QPointF(x, y)) + y += l.boundingRect().height() def draw(self, painter): for l in self.layouts: diff --git a/src/calibre/ebooks/lrf/tags.py b/src/calibre/ebooks/lrf/tags.py index ae71a6dc97..62de1a3a78 100644 --- a/src/calibre/ebooks/lrf/tags.py +++ b/src/calibre/ebooks/lrf/tags.py @@ -209,37 +209,29 @@ class Tag(object): s += " at %08X, contents: %s" % (self.offset, repr(self.contents)) return s - @dynamic_property + @property def byte(self): - def fget(self): - if len(self.contents) != 1: - raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) - return struct.unpack("'%(self.id, self.href(), self.media_type) @@ -804,172 +803,155 @@ class OPF(object): # {{{ for item in self.iterguide(): item.set('href', get_href(item)) - @dynamic_property + @property def title(self): # TODO: Add support for EPUB 3 refinements - def fget(self): - for elem in self.title_path(self.metadata): - title = self.get_text(elem) - if title and title.strip(): - return re.sub(r'\s+', ' ', title.strip()) + for elem in self.title_path(self.metadata): + title = self.get_text(elem) + if title and title.strip(): + return re.sub(r'\s+', ' ', title.strip()) - def fset(self, val): - val = (val or '').strip() - titles = self.title_path(self.metadata) - if self.package_version < 3: - # EPUB 3 allows multiple title elements containing sub-titles, - # series and other things. We all loooove EPUB 3. - for title in titles: - title.getparent().remove(title) - titles = () - if val: - title = titles[0] if titles else self.create_metadata_element('title') - title.text = re.sub(r'\s+', ' ', unicode_type(val)) + @title.setter + def title(self, val): + val = (val or '').strip() + titles = self.title_path(self.metadata) + if self.package_version < 3: + # EPUB 3 allows multiple title elements containing sub-titles, + # series and other things. We all loooove EPUB 3. + for title in titles: + title.getparent().remove(title) + titles = () + if val: + title = titles[0] if titles else self.create_metadata_element('title') + title.text = re.sub(r'\s+', ' ', unicode_type(val)) - return property(fget=fget, fset=fset) - - @dynamic_property + @property def authors(self): + ans = [] + for elem in self.authors_path(self.metadata): + ans.extend(string_to_authors(self.get_text(elem))) + return ans - def fget(self): - ans = [] - for elem in self.authors_path(self.metadata): - ans.extend(string_to_authors(self.get_text(elem))) - return ans + @authors.setter + def authors(self, val): + remove = list(self.authors_path(self.metadata)) + for elem in remove: + elem.getparent().remove(elem) + # Ensure new author element is at the top of the list + # for broken implementations that always use the first + # element with no attention to the role + for author in reversed(val): + elem = self.metadata.makeelement('{%s}creator'% + self.NAMESPACES['dc'], nsmap=self.NAMESPACES) + elem.tail = '\n' + self.metadata.insert(0, elem) + elem.set('{%s}role'%self.NAMESPACES['opf'], 'aut') + self.set_text(elem, author.strip()) - def fset(self, val): - remove = list(self.authors_path(self.metadata)) - for elem in remove: - elem.getparent().remove(elem) - # Ensure new author element is at the top of the list - # for broken implementations that always use the first - # element with no attention to the role - for author in reversed(val): - elem = self.metadata.makeelement('{%s}creator'% - self.NAMESPACES['dc'], nsmap=self.NAMESPACES) - elem.tail = '\n' - self.metadata.insert(0, elem) - elem.set('{%s}role'%self.NAMESPACES['opf'], 'aut') - self.set_text(elem, author.strip()) - - return property(fget=fget, fset=fset) - - @dynamic_property + @property def author_sort(self): + matches = self.authors_path(self.metadata) + if matches: + for match in matches: + ans = match.get('{%s}file-as'%self.NAMESPACES['opf'], None) + if not ans: + ans = match.get('file-as', None) + if ans: + return ans - def fget(self): - matches = self.authors_path(self.metadata) - if matches: - for match in matches: - ans = match.get('{%s}file-as'%self.NAMESPACES['opf'], None) - if not ans: - ans = match.get('file-as', None) - if ans: - return ans + @author_sort.setter + def author_sort(self, val): + matches = self.authors_path(self.metadata) + if matches: + for key in matches[0].attrib: + if key.endswith('file-as'): + matches[0].attrib.pop(key) + matches[0].set('{%s}file-as'%self.NAMESPACES['opf'], unicode_type(val)) - def fset(self, val): - matches = self.authors_path(self.metadata) - if matches: - for key in matches[0].attrib: - if key.endswith('file-as'): - matches[0].attrib.pop(key) - matches[0].set('{%s}file-as'%self.NAMESPACES['opf'], unicode_type(val)) - - return property(fget=fget, fset=fset) - - @dynamic_property + @property def tags(self): + ans = [] + for tag in self.tags_path(self.metadata): + text = self.get_text(tag) + if text and text.strip(): + ans.extend([x.strip() for x in text.split(',')]) + return ans - def fget(self): - ans = [] - for tag in self.tags_path(self.metadata): - text = self.get_text(tag) - if text and text.strip(): - ans.extend([x.strip() for x in text.split(',')]) - return ans + @tags.setter + def tags(self, val): + for tag in list(self.tags_path(self.metadata)): + tag.getparent().remove(tag) + for tag in val: + elem = self.create_metadata_element('subject') + self.set_text(elem, unicode_type(tag)) - def fset(self, val): - for tag in list(self.tags_path(self.metadata)): - tag.getparent().remove(tag) - for tag in val: - elem = self.create_metadata_element('subject') - self.set_text(elem, unicode_type(tag)) - - return property(fget=fget, fset=fset) - - @dynamic_property + @property def pubdate(self): + ans = None + for match in self.pubdate_path(self.metadata): + try: + val = parse_date(etree.tostring(match, encoding=unicode_type, + method='text', with_tail=False).strip()) + except: + continue + if ans is None or val < ans: + ans = val + return ans - def fget(self): - ans = None - for match in self.pubdate_path(self.metadata): - try: - val = parse_date(etree.tostring(match, encoding=unicode_type, - method='text', with_tail=False).strip()) - except: - continue - if ans is None or val < ans: - ans = val - return ans - - def fset(self, val): - least_val = least_elem = None - for match in self.pubdate_path(self.metadata): - try: - cval = parse_date(etree.tostring(match, encoding=unicode_type, - method='text', with_tail=False).strip()) - except: + @pubdate.setter + def pubdate(self, val): + least_val = least_elem = None + for match in self.pubdate_path(self.metadata): + try: + cval = parse_date(etree.tostring(match, encoding=unicode_type, + method='text', with_tail=False).strip()) + except: + match.getparent().remove(match) + else: + if not val: match.getparent().remove(match) - else: - if not val: - match.getparent().remove(match) - if least_val is None or cval < least_val: - least_val, least_elem = cval, match + if least_val is None or cval < least_val: + least_val, least_elem = cval, match - if val: - if least_val is None: - least_elem = self.create_metadata_element('date') + if val: + if least_val is None: + least_elem = self.create_metadata_element('date') - least_elem.attrib.clear() - least_elem.text = isoformat(val) + least_elem.attrib.clear() + least_elem.text = isoformat(val) - return property(fget=fget, fset=fset) - - @dynamic_property + @property def isbn(self): + for match in self.isbn_path(self.metadata): + return self.get_text(match) or None - def fget(self): - for match in self.isbn_path(self.metadata): - return self.get_text(match) or None + @isbn.setter + def isbn(self, val): + uuid_id = None + for attr in self.root.attrib: + if attr.endswith('unique-identifier'): + uuid_id = self.root.attrib[attr] + break - def fset(self, val): - uuid_id = None - for attr in self.root.attrib: - if attr.endswith('unique-identifier'): - uuid_id = self.root.attrib[attr] - break - - matches = self.isbn_path(self.metadata) - if not val: - for x in matches: - xid = x.get('id', None) - is_package_identifier = uuid_id is not None and uuid_id == xid - if is_package_identifier: - self.set_text(x, str(uuid.uuid4())) - for attr in x.attrib: - if attr.endswith('scheme'): - x.attrib[attr] = 'uuid' - else: - x.getparent().remove(x) - return - if not matches: - attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'ISBN'} - matches = [self.create_metadata_element('identifier', - attrib=attrib)] - self.set_text(matches[0], unicode_type(val)) - - return property(fget=fget, fset=fset) + matches = self.isbn_path(self.metadata) + if not val: + for x in matches: + xid = x.get('id', None) + is_package_identifier = uuid_id is not None and uuid_id == xid + if is_package_identifier: + self.set_text(x, str(uuid.uuid4())) + for attr in x.attrib: + if attr.endswith('scheme'): + x.attrib[attr] = 'uuid' + else: + x.getparent().remove(x) + return + if not matches: + attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'ISBN'} + matches = [self.create_metadata_element('identifier', + attrib=attrib)] + self.set_text(matches[0], unicode_type(val)) def get_identifiers(self): identifiers = {} @@ -1024,85 +1006,73 @@ class OPF(object): # {{{ self.set_text(self.create_metadata_element( 'identifier', attrib=attrib), unicode_type(val)) - @dynamic_property + @property def application_id(self): + for match in self.application_id_path(self.metadata): + return self.get_text(match) or None - def fget(self): - for match in self.application_id_path(self.metadata): - return self.get_text(match) or None + @application_id.setter + def application_id(self, val): + removed_ids = set() + for x in tuple(self.application_id_path(self.metadata)): + removed_ids.add(x.get('id', None)) + x.getparent().remove(x) - def fset(self, val): - removed_ids = set() - for x in tuple(self.application_id_path(self.metadata)): - removed_ids.add(x.get('id', None)) - x.getparent().remove(x) + uuid_id = None + for attr in self.root.attrib: + if attr.endswith('unique-identifier'): + uuid_id = self.root.attrib[attr] + break + attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'calibre'} + if uuid_id and uuid_id in removed_ids: + attrib['id'] = uuid_id + self.set_text(self.create_metadata_element( + 'identifier', attrib=attrib), unicode_type(val)) - uuid_id = None - for attr in self.root.attrib: - if attr.endswith('unique-identifier'): - uuid_id = self.root.attrib[attr] - break - attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'calibre'} - if uuid_id and uuid_id in removed_ids: - attrib['id'] = uuid_id - self.set_text(self.create_metadata_element( - 'identifier', attrib=attrib), unicode_type(val)) - - return property(fget=fget, fset=fset) - - @dynamic_property + @property def uuid(self): + for match in self.uuid_id_path(self.metadata): + return self.get_text(match) or None - def fget(self): - for match in self.uuid_id_path(self.metadata): - return self.get_text(match) or None + @uuid.setter + def uuid(self, val): + matches = self.uuid_id_path(self.metadata) + if not matches: + attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'uuid'} + matches = [self.create_metadata_element('identifier', + attrib=attrib)] + self.set_text(matches[0], unicode_type(val)) - def fset(self, val): - matches = self.uuid_id_path(self.metadata) - if not matches: - attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'uuid'} - matches = [self.create_metadata_element('identifier', - attrib=attrib)] - self.set_text(matches[0], unicode_type(val)) - - return property(fget=fget, fset=fset) - - @dynamic_property + @property def language(self): + ans = self.languages + if ans: + return ans[0] - def fget(self): - ans = self.languages - if ans: - return ans[0] + @language.setter + def language(self, val): + self.languages = [val] - def fset(self, val): - self.languages = [val] - - return property(fget=fget, fset=fset) - - @dynamic_property + @property def languages(self): + ans = [] + for match in self.languages_path(self.metadata): + t = self.get_text(match) + if t and t.strip(): + l = canonicalize_lang(t.strip()) + if l: + ans.append(l) + return ans - def fget(self): - ans = [] - for match in self.languages_path(self.metadata): - t = self.get_text(match) - if t and t.strip(): - l = canonicalize_lang(t.strip()) - if l: - ans.append(l) - return ans + @languages.setter + def languages(self, val): + matches = self.languages_path(self.metadata) + for x in matches: + x.getparent().remove(x) - def fset(self, val): - matches = self.languages_path(self.metadata) - for x in matches: - x.getparent().remove(x) - - for lang in val: - l = self.create_metadata_element('language') - self.set_text(l, unicode_type(lang)) - - return property(fget=fget, fset=fset) + for lang in val: + l = self.create_metadata_element('language') + self.set_text(l, unicode_type(lang)) @property def raw_languages(self): @@ -1111,20 +1081,18 @@ class OPF(object): # {{{ if t and t.strip(): yield t.strip() - @dynamic_property + @property def book_producer(self): + for match in self.bkp_path(self.metadata): + return self.get_text(match) or None - def fget(self): - for match in self.bkp_path(self.metadata): - return self.get_text(match) or None - - def fset(self, val): - matches = self.bkp_path(self.metadata) - if not matches: - matches = [self.create_metadata_element('contributor')] - matches[0].set('{%s}role'%self.NAMESPACES['opf'], 'bkp') - self.set_text(matches[0], unicode_type(val)) - return property(fget=fget, fset=fset) + @book_producer.setter + def book_producer(self, val): + matches = self.bkp_path(self.metadata) + if not matches: + matches = [self.create_metadata_element('contributor')] + matches[0].set('{%s}role'%self.NAMESPACES['opf'], 'bkp') + self.set_text(matches[0], unicode_type(val)) def identifier_iter(self): for item in self.identifier_path(self.metadata): @@ -1238,42 +1206,39 @@ class OPF(object): # {{{ if path and os.path.exists(path): return path - @dynamic_property + @property def cover(self): + if self.guide is not None: + for t in ('cover', 'other.ms-coverimage-standard', 'other.ms-coverimage'): + for item in self.guide: + if item.type and item.type.lower() == t: + return item.path + try: + if self.try_to_guess_cover: + return self.guess_cover() + except: + pass - def fget(self): - if self.guide is not None: - for t in ('cover', 'other.ms-coverimage-standard', 'other.ms-coverimage'): - for item in self.guide: - if item.type and item.type.lower() == t: - return item.path - try: - if self.try_to_guess_cover: - return self.guess_cover() - except: - pass + @cover.setter + def cover(self, path): + if self.guide is not None: + self.guide.set_cover(path) + for item in list(self.iterguide()): + if 'cover' in item.get('type', ''): + item.getparent().remove(item) - def fset(self, path): - if self.guide is not None: - self.guide.set_cover(path) - for item in list(self.iterguide()): - if 'cover' in item.get('type', ''): - item.getparent().remove(item) - - else: - g = self.create_guide_element() - self.guide = Guide() - self.guide.set_cover(path) - etree.SubElement(g, 'opf:reference', nsmap=self.NAMESPACES, - attrib={'type':'cover', 'href':self.guide[-1].href()}) - id = self.manifest.id_for_path(self.cover) - if id is None: - for t in ('cover', 'other.ms-coverimage-standard', 'other.ms-coverimage'): - for item in self.guide: - if item.type.lower() == t: - self.create_manifest_item(item.href(), guess_type(path)[0]) - - return property(fget=fget, fset=fset) + else: + g = self.create_guide_element() + self.guide = Guide() + self.guide.set_cover(path) + etree.SubElement(g, 'opf:reference', nsmap=self.NAMESPACES, + attrib={'type':'cover', 'href':self.guide[-1].href()}) + id = self.manifest.id_for_path(self.cover) + if id is None: + for t in ('cover', 'other.ms-coverimage-standard', 'other.ms-coverimage'): + for item in self.guide: + if item.type.lower() == t: + self.create_manifest_item(item.href(), guess_type(path)[0]) def get_metadata_element(self, name): matches = self.metadata_elem_path(self.metadata, name=name) diff --git a/src/calibre/ebooks/metadata/toc.py b/src/calibre/ebooks/metadata/toc.py index 7d4cc06dd8..2287d12aab 100644 --- a/src/calibre/ebooks/metadata/toc.py +++ b/src/calibre/ebooks/metadata/toc.py @@ -122,19 +122,16 @@ class TOC(list): for i in obj.flat(): yield i - @dynamic_property + @property def abspath(self): - doc='Return the file this toc entry points to as a absolute path to a file on the system.' + 'Return the file this toc entry points to as a absolute path to a file on the system.' - def fget(self): - if self.href is None: - return None - path = self.href.replace('/', os.sep) - if not os.path.isabs(path): - path = os.path.join(self.base_path, path) - return path - - return property(fget=fget, doc=doc) + if self.href is None: + return None + path = self.href.replace('/', os.sep) + if not os.path.isabs(path): + path = os.path.join(self.base_path, path) + return path def read_from_opf(self, opfreader): toc = opfreader.soup.find('spine', toc=True) diff --git a/src/calibre/ebooks/mobi/writer2/indexer.py b/src/calibre/ebooks/mobi/writer2/indexer.py index 2d415fcea4..a36e99b9ba 100644 --- a/src/calibre/ebooks/mobi/writer2/indexer.py +++ b/src/calibre/ebooks/mobi/writer2/indexer.py @@ -131,14 +131,13 @@ class IndexEntry(object): ' parent_index=%r)')%(self.offset, self.depth, self.length, self.index, self.parent_index) - @dynamic_property + @property def size(self): - def fget(self): - return self.length + return self.length - def fset(self, val): - self.length = val - return property(fget=fget, fset=fset, doc='Alias for length') + @size.setter + def size(self, val): + self.length = val @property def next_offset(self): diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index d0bd14ceb0..d17c73c6d6 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -704,20 +704,17 @@ class Metadata(object): if attr != nsattr: attrib[nsattr] = attrib.pop(attr) - @dynamic_property + @property def name(self): - def fget(self): - return self.term - return property(fget=fget) + return self.term - @dynamic_property + @property def content(self): - def fget(self): - return self.value + return self.value - def fset(self, value): - self.value = value - return property(fget=fget, fset=fset) + @content.setter + def content(self, value): + self.value = value scheme = Attribute(lambda term: 'scheme' if term == OPF('meta') else OPF('scheme'), @@ -830,33 +827,27 @@ class Metadata(object): def __getattr__(self, term): return self.items[term] - @dynamic_property + @property def _nsmap(self): - def fget(self): - nsmap = {} - for term in self.items: - for item in self.items[term]: - nsmap.update(item.nsmap) - return nsmap - return property(fget=fget) + nsmap = {} + for term in self.items: + for item in self.items[term]: + nsmap.update(item.nsmap) + return nsmap - @dynamic_property + @property def _opf1_nsmap(self): - def fget(self): - nsmap = self._nsmap - for key, value in nsmap.items(): - if value in OPF_NSES or value in DC_NSES: - del nsmap[key] - return nsmap - return property(fget=fget) + nsmap = self._nsmap + for key, value in nsmap.items(): + if value in OPF_NSES or value in DC_NSES: + del nsmap[key] + return nsmap - @dynamic_property + @property def _opf2_nsmap(self): - def fget(self): - nsmap = self._nsmap - nsmap.update(OPF2_NSMAP) - return nsmap - return property(fget=fget) + nsmap = self._nsmap + nsmap.update(OPF2_NSMAP) + return nsmap def to_opf1(self, parent=None): nsmap = self._opf1_nsmap @@ -1011,9 +1002,9 @@ class Manifest(object): # }}} - @dynamic_property + @property def data(self): - doc = """Provides MIME type sensitive access to the manifest + """Provides MIME type sensitive access to the manifest entry's associated content. - XHTML, HTML, and variant content is parsed as necessary to @@ -1025,40 +1016,39 @@ class Manifest(object): - All other content is returned as a :class:`str` object with no special parsing. """ + data = self._data + if data is None: + if self._loader is None: + return None + data = self._loader(getattr(self, 'html_input_href', + self.href)) + try: + mt = self.media_type.lower() + except Exception: + mt = 'application/octet-stream' + if not isinstance(data, string_or_bytes): + pass # already parsed + elif mt in OEB_DOCS: + data = self._parse_xhtml(data) + elif mt[-4:] in ('+xml', '/xml'): + data = self._parse_xml(data) + elif mt in OEB_STYLES: + data = self._parse_css(data) + elif mt == 'text/plain': + self.oeb.log.warn('%s contains data in TXT format'%self.href, + 'converting to HTML') + data = self._parse_txt(data) + self.media_type = XHTML_MIME + self._data = data + return data - def fget(self): - data = self._data - if data is None: - if self._loader is None: - return None - data = self._loader(getattr(self, 'html_input_href', - self.href)) - try: - mt = self.media_type.lower() - except Exception: - mt = 'application/octet-stream' - if not isinstance(data, string_or_bytes): - pass # already parsed - elif mt in OEB_DOCS: - data = self._parse_xhtml(data) - elif mt[-4:] in ('+xml', '/xml'): - data = self._parse_xml(data) - elif mt in OEB_STYLES: - data = self._parse_css(data) - elif mt == 'text/plain': - self.oeb.log.warn('%s contains data in TXT format'%self.href, - 'converting to HTML') - data = self._parse_txt(data) - self.media_type = XHTML_MIME - self._data = data - return data + @data.setter + def data(self, value): + self._data = value - def fset(self, value): - self._data = value - - def fdel(self): - self._data = None - return property(fget, fset, fdel, doc=doc) + @data.deleter + def data(self): + self._data = None def unload_data_from_memory(self, memory=None): if isinstance(self._data, bytes): @@ -1266,20 +1256,19 @@ class Manifest(object): element(elem, OPF('item'), attrib=attrib) return elem - @dynamic_property + @property def main_stylesheet(self): - def fget(self): - ans = getattr(self, '_main_stylesheet', None) - if ans is None: - for item in self: - if item.media_type.lower() in OEB_STYLES: - ans = item - break - return ans + ans = getattr(self, '_main_stylesheet', None) + if ans is None: + for item in self: + if item.media_type.lower() in OEB_STYLES: + ans = item + break + return ans - def fset(self, item): - self._main_stylesheet = item - return property(fget=fget, fset=fset) + @main_stylesheet.setter + def main_stylesheet(self, item): + self._main_stylesheet = item class Spine(object): @@ -1422,15 +1411,12 @@ class Guide(object): return 'Reference(type=%r, title=%r, href=%r)' \ % (self.type, self.title, self.href) - @dynamic_property + @property def item(self): - doc = """The manifest item associated with this reference.""" - - def fget(self): - path = urldefrag(self.href)[0] - hrefs = self.oeb.manifest.hrefs - return hrefs.get(path, None) - return property(fget=fget, doc=doc) + """The manifest item associated with this reference.""" + path = urldefrag(self.href)[0] + hrefs = self.oeb.manifest.hrefs + return hrefs.get(path, None) def __init__(self, oeb): self.oeb = oeb diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index c889a9c5df..2a3175632f 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -1369,14 +1369,13 @@ class EpubContainer(Container): with self.open(name, 'wb') as f: f.write(data) - @dynamic_property + @property def path_to_ebook(self): - def fget(self): - return self.pathtoepub + return self.pathtoepub - def fset(self, val): - self.pathtoepub = val - return property(fget=fget, fset=fset) + @path_to_ebook.setter + def path_to_ebook(self, val): + self.pathtoepub = val # }}} @@ -1496,14 +1495,13 @@ class AZW3Container(Container): outpath = self.pathtoazw3 opf_to_azw3(self.name_path_map[self.opf_name], outpath, self) - @dynamic_property + @property def path_to_ebook(self): - def fget(self): - return self.pathtoazw3 + return self.pathtoazw3 - def fset(self, val): - self.pathtoazw3 = val - return property(fget=fget, fset=fset) + @path_to_ebook.setter + def path_to_ebook(self, val): + self.pathtoazw3 = val @property def names_that_must_not_be_changed(self): diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index f9a9d252ab..a3cb32d8e0 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1022,19 +1022,19 @@ class Application(QApplication): else: return QApplication.event(self, e) - @dynamic_property + @property def current_custom_colors(self): - from PyQt5.Qt import QColorDialog, QColor + from PyQt5.Qt import QColorDialog - def fget(self): - return [col.getRgb() for col in + return [col.getRgb() for col in (QColorDialog.customColor(i) for i in range(QColorDialog.customCount()))] - def fset(self, colors): - num = min(len(colors), QColorDialog.customCount()) - for i in range(num): - QColorDialog.setCustomColor(i, QColor(*colors[i])) - return property(fget=fget, fset=fset) + @current_custom_colors.setter + def current_custom_colors(self, colors): + from PyQt5.Qt import QColorDialog, QColor + num = min(len(colors), QColorDialog.customCount()) + for i in range(num): + QColorDialog.setCustomColor(i, QColor(*colors[i])) def read_custom_colors(self): colors = self.color_prefs.get('custom_colors_for_color_dialog', None) diff --git a/src/calibre/gui2/actions/toc_edit.py b/src/calibre/gui2/actions/toc_edit.py index 25923733a6..8782f86aa4 100644 --- a/src/calibre/gui2/actions/toc_edit.py +++ b/src/calibre/gui2/actions/toc_edit.py @@ -51,18 +51,17 @@ class ChooseFormat(QDialog): # {{{ b.setChecked(True) self.accept() - @dynamic_property + @property def formats(self): - def fget(self): - for b in self.buttons: - if b.isChecked(): - yield unicode_type(b.text())[1:] + for b in self.buttons: + if b.isChecked(): + yield unicode_type(b.text())[1:] - def fset(self, formats): - formats = {x.upper() for x in formats} - for b in self.buttons: - b.setChecked(b.text()[1:] in formats) - return property(fget=fget, fset=fset) + @formats.setter + def formats(self, formats): + formats = {x.upper() for x in formats} + for b in self.buttons: + b.setChecked(b.text()[1:] in formats) # }}} diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py index 546422014e..da9d790a43 100644 --- a/src/calibre/gui2/comments_editor.py +++ b/src/calibre/gui2/comments_editor.py @@ -336,55 +336,53 @@ class EditorWidget(QWebView, LineEditECM): # {{{ def remove_format_cleanup(self): self.html = self.html - @dynamic_property + @property def html(self): + ans = u'' + try: + if not self.page().mainFrame().documentElement().findFirst('meta[name="calibre-dont-sanitize"]').isNull(): + # Bypass cleanup if special meta tag exists + return unicode_type(self.page().mainFrame().toHtml()) + check = unicode_type(self.page().mainFrame().toPlainText()).strip() + raw = unicode_type(self.page().mainFrame().toHtml()) + raw = xml_to_unicode(raw, strip_encoding_pats=True, + resolve_entities=True)[0] + raw = self.comments_pat.sub('', raw) + if not check and ' 1: - ans = u'
%s
'%(u''.join(elems)) - else: - ans = u''.join(elems) - if not ans.startswith('<'): - ans = '

%s

'%ans - ans = xml_replace_entities(ans) - except: - import traceback - traceback.print_exc() - - return ans - - def fset(self, val): - if self.base_url is None: - self.setHtml(val) + if len(elems) > 1: + ans = u'
%s
'%(u''.join(elems)) else: - self.setHtml(val, self.base_url) - self.set_font_style() - return property(fget=fget, fset=fset) + ans = u''.join(elems) + if not ans.startswith('<'): + ans = '

%s

'%ans + ans = xml_replace_entities(ans) + except: + import traceback + traceback.print_exc() + + return ans + + @html.setter + def html(self, val): + if self.base_url is None: + self.setHtml(val) + else: + self.setHtml(val, self.base_url) + self.set_font_style() def set_base_url(self, qurl): self.base_url = qurl @@ -763,15 +761,14 @@ class Editor(QWidget): # {{{ def set_minimum_height_for_editor(self, val): self.editor.setMinimumHeight(val) - @dynamic_property + @property def html(self): - def fset(self, v): - self.editor.html = v + self.tabs.setCurrentIndex(0) + return self.editor.html - def fget(self): - self.tabs.setCurrentIndex(0) - return self.editor.html - return property(fget=fget, fset=fset) + @html.setter + def html(self, v): + self.editor.html = v def change_tab(self, index): # print 'reloading:', (index and self.wyswyg_dirty) or (not index and @@ -785,14 +782,13 @@ class Editor(QWidget): # {{{ self.editor.html = unicode_type(self.code_edit.toPlainText()) self.source_dirty = False - @dynamic_property + @property def tab(self): - def fget(self): - return 'code' if self.tabs.currentWidget() is self.code_edit else 'wyswyg' + return 'code' if self.tabs.currentWidget() is self.code_edit else 'wyswyg' - def fset(self, val): - self.tabs.setCurrentWidget(self.code_edit if val == 'code' else self.wyswyg) - return property(fget=fget, fset=fset) + @tab.setter + def tab(self, val): + self.tabs.setCurrentWidget(self.code_edit if val == 'code' else self.wyswyg) def wyswyg_dirtied(self, *args): self.wyswyg_dirty = True @@ -816,14 +812,13 @@ class Editor(QWidget): # {{{ if self.toolbar_prefs_name is not None: gprefs.set(self.toolbar_prefs_name, visible) - @dynamic_property + @property def toolbars_visible(self): - def fget(self): - return self.toolbar1.isVisible() or self.toolbar2.isVisible() or self.toolbar3.isVisible() + return self.toolbar1.isVisible() or self.toolbar2.isVisible() or self.toolbar3.isVisible() - def fset(self, val): - getattr(self, ('show' if val else 'hide') + '_toolbars')() - return property(fget=fget, fset=fset) + @toolbars_visible.setter + def toolbars_visible(self, val): + getattr(self, ('show' if val else 'hide') + '_toolbars')() def set_readonly(self, what): self.editor.set_readonly(what) diff --git a/src/calibre/gui2/complete2.py b/src/calibre/gui2/complete2.py index d1f801cf84..8df266d0b4 100644 --- a/src/calibre/gui2/complete2.py +++ b/src/calibre/gui2/complete2.py @@ -333,23 +333,21 @@ class LineEdit(QLineEdit, LineEditECM): def set_add_separator(self, what): self.add_separator = bool(what) - @dynamic_property + @property def all_items(self): - def fget(self): - return self.mcompleter.model().all_items + return self.mcompleter.model().all_items - def fset(self, items): - self.mcompleter.model().set_items(items) - return property(fget=fget, fset=fset) + @all_items.setter + def all_items(self, items): + self.mcompleter.model().set_items(items) - @dynamic_property + @property def disable_popup(self): - def fget(self): - return self.mcompleter.disable_popup + return self.mcompleter.disable_popup - def fset(self, val): - self.mcompleter.disable_popup = bool(val) - return property(fget=fget, fset=fset) + @disable_popup.setter + def disable_popup(self, val): + self.mcompleter.disable_popup = bool(val) # }}} def event(self, ev): @@ -471,23 +469,21 @@ class EditWithComplete(EnComboBox): self.setText(what) self.lineEdit().selectAll() - @dynamic_property + @property def all_items(self): - def fget(self): - return self.lineEdit().all_items + return self.lineEdit().all_items - def fset(self, val): - self.lineEdit().all_items = val - return property(fget=fget, fset=fset) + @all_items.setter + def all_items(self, val): + self.lineEdit().all_items = val - @dynamic_property + @property def disable_popup(self): - def fget(self): - return self.lineEdit().disable_popup + return self.lineEdit().disable_popup - def fset(self, val): - self.lineEdit().disable_popup = bool(val) - return property(fget=fget, fset=fset) + @disable_popup.setter + def disable_popup(self, val): + self.lineEdit().disable_popup = bool(val) # }}} def text(self): diff --git a/src/calibre/gui2/covers.py b/src/calibre/gui2/covers.py index d6103c6072..c0cdde8ee2 100644 --- a/src/calibre/gui2/covers.py +++ b/src/calibre/gui2/covers.py @@ -43,14 +43,13 @@ class ColorButton(QToolButton): self.setIcon(QIcon(self.pix)) self.clicked.connect(self.choose_color) - @dynamic_property + @property def color(self): - def fget(self): - return self._color.name(QColor.HexRgb)[1:] + return self._color.name(QColor.HexRgb)[1:] - def fset(self, val): - self._color = QColor('#' + val) - return property(fget=fget, fset=fset) + @color.setter + def color(self, val): + self._color = QColor('#' + val) def update_display(self): self.pix.fill(self._color) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index c0bfef6bf0..1740271ce0 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -377,14 +377,13 @@ class Comments(Base): val = None return val - @dynamic_property + @property def tab(self): - def fget(self): - return self._tb.tab + return self._tb.tab - def fset(self, val): - self._tb.tab = val - return property(fget=fget, fset=fset) + @tab.setter + def tab(self, val): + self._tb.tab = val def connect_data_changed(self, slot): self._tb.data_changed.connect(slot) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index f918421e6d..c7f96e46c0 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1367,29 +1367,26 @@ class DeviceMixin(object): # {{{ memory=[files, remove]) self.status_bar.show_message(_('Sending catalogs to device.'), 5000) - @dynamic_property + @property def news_to_be_synced(self): - doc = 'Set of ids to be sent to device' + 'Set of ids to be sent to device' + ans = [] + try: + ans = self.library_view.model().db.prefs.get('news_to_be_synced', + []) + except: + import traceback + traceback.print_exc() + return set(ans) - def fget(self): - ans = [] - try: - ans = self.library_view.model().db.prefs.get('news_to_be_synced', - []) - except: - import traceback - traceback.print_exc() - return set(ans) - - def fset(self, ids): - try: - self.library_view.model().db.new_api.set_pref('news_to_be_synced', - list(ids)) - except: - import traceback - traceback.print_exc() - - return property(fget=fget, fset=fset, doc=doc) + @news_to_be_synced.setter + def news_to_be_synced(self, ids): + try: + self.library_view.model().db.new_api.set_pref('news_to_be_synced', + list(ids)) + except: + import traceback + traceback.print_exc() def sync_news(self, send_ids=None, do_auto_convert=True): if self.device_connected: diff --git a/src/calibre/gui2/dialogs/custom_recipes.py b/src/calibre/gui2/dialogs/custom_recipes.py index 7b33412c5c..116c7acd0b 100644 --- a/src/calibre/gui2/dialogs/custom_recipes.py +++ b/src/calibre/gui2/dialogs/custom_recipes.py @@ -378,32 +378,31 @@ class BasicRecipe(QWidget): # {{{ return False return True - @dynamic_property + @property def recipe_source(self): - def fget(self): - title = self.title.text().strip() - feeds = [self.feeds.item(i).data(Qt.UserRole) for i in range(self.feeds.count())] - return options_to_recipe_source(title, self.oldest_article.value(), self.max_articles.value(), feeds) + title = self.title.text().strip() + feeds = [self.feeds.item(i).data(Qt.UserRole) for i in range(self.feeds.count())] + return options_to_recipe_source(title, self.oldest_article.value(), self.max_articles.value(), feeds) - def fset(self, src): - self.feeds.clear() - self.feed_title.clear() - self.feed_url.clear() - if src is None: - self.title.setText(_('My news source')) - self.oldest_article.setValue(7) - self.max_articles.setValue(100) - else: - recipe = compile_recipe(src) - self.title.setText(recipe.title) - self.oldest_article.setValue(recipe.oldest_article) - self.max_articles.setValue(recipe.max_articles_per_feed) - for x in (recipe.feeds or ()): - title, url = ('', x) if len(x) == 1 else x - QListWidgetItem('%s - %s' % (title, url), self.feeds).setData(Qt.UserRole, (title, url)) + @recipe_source.setter + def recipe_source(self, src): + self.feeds.clear() + self.feed_title.clear() + self.feed_url.clear() + if src is None: + self.title.setText(_('My news source')) + self.oldest_article.setValue(7) + self.max_articles.setValue(100) + else: + recipe = compile_recipe(src) + self.title.setText(recipe.title) + self.oldest_article.setValue(recipe.oldest_article) + self.max_articles.setValue(recipe.max_articles_per_feed) + for x in (recipe.feeds or ()): + title, url = ('', x) if len(x) == 1 else x + QListWidgetItem('%s - %s' % (title, url), self.feeds).setData(Qt.UserRole, (title, url)) - return property(fget=fget, fset=fset) # }}} @@ -431,16 +430,13 @@ class AdvancedRecipe(QWidget): # {{{ return False return True - @dynamic_property + @property def recipe_source(self): + return self.editor.toPlainText() - def fget(self): - return self.editor.toPlainText() - - def fset(self, src): - self.editor.load_text(src, syntax='python', doc_name='') - - return property(fget=fget, fset=fset) + @recipe_source.setter + def recipe_source(self, src): + self.editor.load_text(src, syntax='python', doc_name='') def sizeHint(self): return QSize(800, 500) diff --git a/src/calibre/gui2/dialogs/progress.py b/src/calibre/gui2/dialogs/progress.py index 239f8d4c88..63a44a90d3 100644 --- a/src/calibre/gui2/dialogs/progress.py +++ b/src/calibre/gui2/dialogs/progress.py @@ -64,14 +64,13 @@ class ProgressDialog(QDialog): def set_value(self, val): self.value = val - @dynamic_property + @property def value(self): - def fset(self, val): - return self.bar.setValue(val) + return self.bar.value() - def fget(self): - return self.bar.value() - return property(fget=fget, fset=fset) + @value.setter + def value(self, val): + self.bar.setValue(val) def set_min(self, min): self.min = min @@ -79,42 +78,38 @@ class ProgressDialog(QDialog): def set_max(self, max): self.max = max - @dynamic_property + @property def max(self): - def fget(self): - return self.bar.maximum() + return self.bar.maximum() - def fset(self, val): - self.bar.setMaximum(val) - return property(fget=fget, fset=fset) + @max.setter + def max(self, val): + self.bar.setMaximum(val) - @dynamic_property + @property def min(self): - def fget(self): - return self.bar.minimum() + return self.bar.minimum() - def fset(self, val): - self.bar.setMinimum(val) - return property(fget=fget, fset=fset) + @min.setter + def min(self, val): + self.bar.setMinimum(val) - @dynamic_property + @property def title(self): - def fget(self): - return self.title_label.text() + return self.title_label.text() - def fset(self, val): - self.title_label.setText(unicode_type(val or '')) - return property(fget=fget, fset=fset) + @title.setter + def title(self, val): + self.title_label.setText(unicode_type(val or '')) - @dynamic_property + @property def msg(self): - def fget(self): - return self.message.text() + return self.message.text() - def fset(self, val): - val = unicode_type(val or '') - self.message.setText(elided_text(val, self.font(), self.message.minimumWidth()-10)) - return property(fget=fget, fset=fset) + @msg.setter + def msg(self, val): + val = unicode_type(val or '') + self.message.setText(elided_text(val, self.font(), self.message.minimumWidth()-10)) def _canceled(self, *args): self.canceled = True diff --git a/src/calibre/gui2/font_family_chooser.py b/src/calibre/gui2/font_family_chooser.py index 8897ee1cbf..e35702b294 100644 --- a/src/calibre/gui2/font_family_chooser.py +++ b/src/calibre/gui2/font_family_chooser.py @@ -353,18 +353,17 @@ class FontFamilyChooser(QWidget): def clear_family(self): self.font_family = None - @dynamic_property + @property def font_family(self): - def fget(self): - return self._current_family + return self._current_family - def fset(self, val): - if not val: - val = None - self._current_family = val - self.button.setText(val or self.default_text) - self.family_changed.emit(val) - return property(fget=fget, fset=fset) + @font_family.setter + def font_family(self, val): + if not val: + val = None + self._current_family = val + self.button.setText(val or self.default_text) + self.family_changed.emit(val) def show_chooser(self): d = FontFamilyDialog(self.font_family, self) diff --git a/src/calibre/gui2/languages.py b/src/calibre/gui2/languages.py index 70c4302a9d..d9a402c96b 100644 --- a/src/calibre/gui2/languages.py +++ b/src/calibre/gui2/languages.py @@ -60,23 +60,20 @@ class LanguagesEdit(EditWithComplete): parts = [x.strip() for x in raw.split(',')] return [self.comma_rmap.get(x, x) for x in parts] - @dynamic_property + @property def lang_codes(self): + vals = self.vals + ans = [] + for name in vals: + if name: + code = self._rmap.get(lower(name), None) + if code is not None: + ans.append(code) + return ans - def fget(self): - vals = self.vals - ans = [] - for name in vals: - if name: - code = self._rmap.get(lower(name), None) - if code is not None: - ans.append(code) - return ans - - def fset(self, lang_codes): - self.set_lang_codes(lang_codes, allow_undo=False) - - return property(fget=fget, fset=fset) + @lang_codes.setter + def lang_codes(self, lang_codes): + self.set_lang_codes(lang_codes, allow_undo=False) def set_lang_codes(self, lang_codes, allow_undo=True): ans = [] diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index a5ce76815d..d122d58209 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -186,18 +186,17 @@ class PreserveViewState(object): # {{{ view.horizontalScrollBar().setValue(self.hscroll) self.init_vals() - @dynamic_property + @property def state(self): - def fget(self): - self.__enter__() - return {x:getattr(self, x) for x in ('selected_ids', 'current_id', - 'vscroll', 'hscroll')} + self.__enter__() + return {x:getattr(self, x) for x in ('selected_ids', 'current_id', + 'vscroll', 'hscroll')} - def fset(self, state): - for k, v in iteritems(state): - setattr(self, k, v) - self.__exit__() - return property(fget=fget, fset=fset) + @state.setter + def state(self, state): + for k, v in iteritems(state): + setattr(self, k, v) + self.__exit__() # }}} @@ -1161,24 +1160,23 @@ class BooksView(QTableView): # {{{ ans.append(i) return ans - @dynamic_property + @property def current_id(self): - def fget(self): - try: - return self.model().id(self.currentIndex()) - except: - pass - return None + try: + return self.model().id(self.currentIndex()) + except: + pass + return None - def fset(self, val): - if val is None: - return - m = self.model() - for row in range(m.rowCount(QModelIndex())): - if m.id(row) == val: - self.set_current_row(row, select=False) - break - return property(fget=fget, fset=fset) + @current_id.setter + def current_id(self, val): + if val is None: + return + m = self.model() + for row in range(m.rowCount(QModelIndex())): + if m.id(row) == val: + self.set_current_row(row, select=False) + break @property def next_id(self): diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 1bf2148fd1..5eb16fe328 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -72,14 +72,13 @@ class BasicMetadataWidget(object): def commit(self, db, id_): return True - @dynamic_property + @property def current_val(self): - # Present in most but not all basic metadata widgets - def fget(self): - return None - def fset(self, val): - pass - return property(fget=fget, fset=fset) + return None + + @current_val.setter + def current_val(self, val): + pass ''' @@ -225,24 +224,22 @@ class TitleEdit(EnLineEdit, ToMetadataMixin): # to work even if some of the book files are opened in windows. getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False) - @dynamic_property + @property def current_val(self): - def fget(self): - title = clean_text(unicode_type(self.text())) - if not title: - title = self.get_default() - return title.strip() + title = clean_text(unicode_type(self.text())) + if not title: + title = self.get_default() + return title.strip() - def fset(self, val): - if hasattr(val, 'strip'): - val = val.strip() - if not val: - val = self.get_default() - self.set_text(val) - self.setCursorPosition(0) - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if hasattr(val, 'strip'): + val = val.strip() + if not val: + val = self.get_default() + self.set_text(val) + self.setCursorPosition(0) def break_cycles(self): self.dialog = None @@ -416,22 +413,20 @@ class AuthorsEdit(EditWithComplete, ToMetadataMixin): self.books_to_refresh |= db.set_authors(id_, authors, notify=False, allow_case_change=True) - @dynamic_property + @property def current_val(self): - def fget(self): - au = clean_text(unicode_type(self.text())) - if not au: - au = self.get_default() - return string_to_authors(au) + au = clean_text(unicode_type(self.text())) + if not au: + au = self.get_default() + return string_to_authors(au) - def fset(self, val): - if not val: - val = [self.get_default()] - self.set_edit_text(' & '.join([x.strip() for x in val])) - self.lineEdit().setCursorPosition(0) - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if not val: + val = [self.get_default()] + self.set_edit_text(' & '.join([x.strip() for x in val])) + self.lineEdit().setCursorPosition(0) def break_cycles(self): self.db = self.dialog = None @@ -485,19 +480,17 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin): self.first_time = True self.update_state() - @dynamic_property + @property def current_val(self): - def fget(self): - return clean_text(unicode_type(self.text())) + return clean_text(unicode_type(self.text())) - def fset(self, val): - if not val: - val = '' - self.set_text(val.strip()) - self.setCursorPosition(0) - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if not val: + val = '' + self.set_text(val.strip()) + self.setCursorPosition(0) def update_state_and_val(self): # Handle case change if the authors box changed @@ -613,19 +606,17 @@ class SeriesEdit(EditWithComplete, ToMetadataMixin): self.books_to_refresh = set([]) self.lineEdit().textChanged.connect(self.data_changed) - @dynamic_property + @property def current_val(self): - def fget(self): - return clean_text(unicode_type(self.currentText())) + return clean_text(unicode_type(self.currentText())) - def fset(self, val): - if not val: - val = '' - self.set_edit_text(val.strip()) - self.lineEdit().setCursorPosition(0) - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if not val: + val = '' + self.set_edit_text(val.strip()) + self.lineEdit().setCursorPosition(0) def initialize(self, db, id_): self.books_to_refresh = set([]) @@ -672,19 +663,17 @@ class SeriesIndexEdit(make_undoable(QDoubleSpinBox), ToMetadataMixin): def enable(self, *args): self.setEnabled(bool(self.series_edit.current_val)) - @dynamic_property + @property def current_val(self): - def fget(self): - return self.value() + return self.value() - def fset(self, val): - if val is None: - val = 1.0 - val = float(val) - self.set_spinbox_value(val) - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if val is None: + val = 1.0 + val = float(val) + self.set_spinbox_value(val) def initialize(self, db, id_): self.db = db @@ -1255,31 +1244,29 @@ class Cover(ImageView): # {{{ def changed(self): return self.current_val != self.original_val - @dynamic_property + @property def current_val(self): - def fget(self): - return self._cdata + return self._cdata - def fset(self, cdata): - self._cdata = None - self.cdata_before_trim = None - pm = QPixmap() - if cdata: - pm.loadFromData(cdata) - if pm.isNull(): - pm = QPixmap(I('default_cover.png')) - else: - self._cdata = cdata - pm.setDevicePixelRatio(getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) - self.setPixmap(pm) - tt = _('This book has no cover') - if self._cdata: - tt = _('Cover size: %(width)d x %(height)d pixels') % \ - dict(width=pm.width(), height=pm.height()) - self.setToolTip(tt) - self.data_changed.emit() - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, cdata): + self._cdata = None + self.cdata_before_trim = None + pm = QPixmap() + if cdata: + pm.loadFromData(cdata) + if pm.isNull(): + pm = QPixmap(I('default_cover.png')) + else: + self._cdata = cdata + pm.setDevicePixelRatio(getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) + self.setPixmap(pm) + tt = _('This book has no cover') + if self._cdata: + tt = _('Cover size: %(width)d x %(height)d pixels') % \ + dict(width=pm.width(), height=pm.height()) + self.setToolTip(tt) + self.data_changed.emit() def commit(self, db, id_): if self.changed: @@ -1309,20 +1296,19 @@ class CommentsEdit(Editor, ToMetadataMixin): # {{{ FIELD_NAME = 'comments' toolbar_prefs_name = 'metadata-comments-editor-widget-hidden-toolbars' - @dynamic_property + @property def current_val(self): - def fget(self): - return self.html + return self.html - def fset(self, val): - if not val or not val.strip(): - val = '' - else: - val = comments_to_html(val) - self.set_html(val, self.allow_undo) - self.wyswyg_dirtied() - self.data_changed.emit() - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if not val or not val.strip(): + val = '' + else: + val = comments_to_html(val) + self.set_html(val, self.allow_undo) + self.wyswyg_dirtied() + self.data_changed.emit() def initialize(self, db, id_): path = db.abspath(id_, index_is_id=True) @@ -1350,14 +1336,13 @@ class RatingEdit(RatingEditor, ToMetadataMixin): # {{{ self.setWhatsThis(self.TOOLTIP) self.currentTextChanged.connect(self.data_changed) - @dynamic_property + @property def current_val(self): - def fget(self): - return self.rating_value + return self.rating_value - def fset(self, val): - self.rating_value = val - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + self.rating_value = val def initialize(self, db, id_): val = db.rating(id_, index_is_id=True) @@ -1390,17 +1375,16 @@ class TagsEdit(EditWithComplete, ToMetadataMixin): # {{{ self.setToolTip(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP) - @dynamic_property + @property def current_val(self): - def fget(self): - return [clean_text(x) for x in unicode_type(self.text()).split(',')] + return [clean_text(x) for x in unicode_type(self.text()).split(',')] - def fset(self, val): - if not val: - val = [] - self.set_edit_text(', '.join([x.strip() for x in val])) - self.setCursorPosition(0) - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if not val: + val = [] + self.set_edit_text(', '.join([x.strip() for x in val])) + self.setCursorPosition(0) def initialize(self, db, id_): self.books_to_refresh = set([]) @@ -1454,14 +1438,13 @@ class LanguagesEdit(LE, ToMetadataMixin): # {{{ self.textChanged.connect(self.data_changed) self.setToolTip(self.TOOLTIP) - @dynamic_property + @property def current_val(self): - def fget(self): - return self.lang_codes + return self.lang_codes - def fset(self, val): - self.set_lang_codes(val, self.allow_undo) - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + self.set_lang_codes(val, self.allow_undo) def initialize(self, db, id_): self.init_langs(db) @@ -1562,44 +1545,43 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin): if d.exec_() == d.Accepted: self.current_val = d.get_identifiers() - @dynamic_property + @property def current_val(self): - def fget(self): - raw = unicode_type(self.text()).strip() - parts = [clean_text(x) for x in raw.split(',')] - ans = {} - for x in parts: - c = x.split(':') - if len(c) > 1: - itype = c[0].lower() - c = ':'.join(c[1:]) - if itype == 'isbn': - v = check_isbn(c) - if v is not None: - c = v - ans[itype] = c - return ans - - def fset(self, val): - if not val: - val = {} - - def keygen(x): - x = x[0] - if x == 'isbn': - x = '00isbn' - return x - for k in list(val): - if k == 'isbn': - v = check_isbn(k) + raw = unicode_type(self.text()).strip() + parts = [clean_text(x) for x in raw.split(',')] + ans = {} + for x in parts: + c = x.split(':') + if len(c) > 1: + itype = c[0].lower() + c = ':'.join(c[1:]) + if itype == 'isbn': + v = check_isbn(c) if v is not None: - val[k] = v - ids = sorted(iteritems(val), key=keygen) - txt = ', '.join(['%s:%s'%(k.lower(), vl) for k, vl in ids]) - # Use selectAll + insert instead of setText so that undo works - self.selectAll(), self.insert(txt.strip()) - self.setCursorPosition(0) - return property(fget=fget, fset=fset) + c = v + ans[itype] = c + return ans + + @current_val.setter + def current_val(self, val): + if not val: + val = {} + + def keygen(x): + x = x[0] + if x == 'isbn': + x = '00isbn' + return x + for k in list(val): + if k == 'isbn': + v = check_isbn(k) + if v is not None: + val[k] = v + ids = sorted(iteritems(val), key=keygen) + txt = ', '.join(['%s:%s'%(k.lower(), vl) for k, vl in ids]) + # Use selectAll + insert instead of setText so that undo works + self.selectAll(), self.insert(txt.strip()) + self.setCursorPosition(0) def initialize(self, db, id_): self.original_val = db.get_identifiers(id_, index_is_id=True) @@ -1778,19 +1760,17 @@ class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{ self.clear_button.setToolTip(_('Clear publisher')) self.clear_button.clicked.connect(self.clearEditText) - @dynamic_property + @property def current_val(self): - def fget(self): - return clean_text(unicode_type(self.currentText())) + return clean_text(unicode_type(self.currentText())) - def fset(self, val): - if not val: - val = '' - self.set_edit_text(val.strip()) - self.lineEdit().setCursorPosition(0) - - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if not val: + val = '' + self.set_edit_text(val.strip()) + self.lineEdit().setCursorPosition(0) def initialize(self, db, id_): self.books_to_refresh = set([]) @@ -1857,18 +1837,17 @@ class DateEdit(make_undoable(QDateTimeEdit), ToMetadataMixin): def reset_date(self, *args): self.current_val = None - @dynamic_property + @property def current_val(self): - def fget(self): - return qt_to_dt(self.dateTime(), as_utc=False) + return qt_to_dt(self.dateTime(), as_utc=False) - def fset(self, val): - if val is None or is_date_undefined(val): - val = UNDEFINED_DATE - else: - val = as_local_time(val) - self.set_spinbox_value(val) - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + if val is None or is_date_undefined(val): + val = UNDEFINED_DATE + else: + val = as_local_time(val) + self.set_spinbox_value(val) def initialize(self, db, id_): self.current_val = getattr(db, self.ATTR)(id_, index_is_id=True) diff --git a/src/calibre/gui2/metadata/diff.py b/src/calibre/gui2/metadata/diff.py index 9f5b1fe9b6..9fdc166282 100644 --- a/src/calibre/gui2/metadata/diff.py +++ b/src/calibre/gui2/metadata/diff.py @@ -49,29 +49,28 @@ class LineEdit(EditWithComplete): self.set_separator(sep) self.textChanged.connect(self.changed) - @dynamic_property + @property def value(self): - def fget(self): - val = unicode_type(self.text()).strip() - ism = self.metadata['is_multiple'] - if ism: - if not val: - val = [] - else: - val = val.strip(ism['list_to_ui'].strip()) - val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()] - return val + val = unicode_type(self.text()).strip() + ism = self.metadata['is_multiple'] + if ism: + if not val: + val = [] + else: + val = val.strip(ism['list_to_ui'].strip()) + val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()] + return val - def fset(self, val): - ism = self.metadata['is_multiple'] - if ism: - if not val: - val = '' - else: - val = ism['list_to_ui'].join(val) - self.setText(val) - self.setCursorPosition(0) - return property(fget=fget, fset=fset) + @value.setter + def value(self, val): + ism = self.metadata['is_multiple'] + if ism: + if not val: + val = '' + else: + val = ism['list_to_ui'].join(val) + self.setText(val) + self.setCursorPosition(0) def from_mi(self, mi): val = mi.get(self.field, default='') or '' @@ -85,15 +84,14 @@ class LineEdit(EditWithComplete): elif self.field == 'authors': mi.set('author_sort', authors_to_sort_string(val)) - @dynamic_property + @property def current_val(self): - def fget(self): - return unicode_type(self.text()) + return unicode_type(self.text()) - def fset(self, val): - self.setText(val) - self.setCursorPosition(0) - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + self.setText(val) + self.setCursorPosition(0) @property def is_blank(self): @@ -119,14 +117,13 @@ class LanguagesEdit(LE): if not is_new: self.lineEdit().setReadOnly(True) - @dynamic_property + @property def current_val(self): - def fget(self): - return self.lang_codes + return self.lang_codes - def fset(self, val): - self.lang_codes = val - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + self.lang_codes = val def from_mi(self, mi): self.lang_codes = mi.languages @@ -241,17 +238,16 @@ class IdentifiersEdit(LineEdit): def to_mi(self, mi): mi.set_identifiers(self.as_dict) - @dynamic_property + @property def as_dict(self): - def fget(self): - parts = (x.strip() for x in self.current_val.split(',') if x.strip()) - return {k:v for k, v in iteritems({x.partition(':')[0].strip():x.partition(':')[-1].strip() for x in parts}) if k and v} + parts = (x.strip() for x in self.current_val.split(',') if x.strip()) + return {k:v for k, v in iteritems({x.partition(':')[0].strip():x.partition(':')[-1].strip() for x in parts}) if k and v} - def fset(self, val): - val = ('%s:%s' % (k, v) for k, v in iteritems(val)) - self.setText(', '.join(val)) - self.setCursorPosition(0) - return property(fget=fget, fset=fset) + @as_dict.setter + def as_dict(self, val): + val = ('%s:%s' % (k, v) for k, v in iteritems(val)) + self.setText(', '.join(val)) + self.setCursorPosition(0) class CommentsEdit(Editor): @@ -269,15 +265,14 @@ class CommentsEdit(Editor): self.hide_toolbars() self.set_readonly(True) - @dynamic_property + @property def current_val(self): - def fget(self): - return self.html + return self.html - def fset(self, val): - self.html = val or '' - self.changed.emit() - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + self.html = val or '' + self.changed.emit() def from_mi(self, mi): val = mi.get(self.field, default='') @@ -315,16 +310,15 @@ class CoverView(QWidget): def is_blank(self): return self.pixmap is None - @dynamic_property + @property def current_val(self): - def fget(self): - return self.pixmap + return self.pixmap - def fset(self, val): - self.pixmap = val - self.changed.emit() - self.update() - return property(fget=fget, fset=fset) + @current_val.setter + def current_val(self, val): + self.pixmap = val + self.changed.emit() + self.update() def from_mi(self, mi): p = getattr(mi, 'cover', None) diff --git a/src/calibre/gui2/preferences/coloring.py b/src/calibre/gui2/preferences/coloring.py index a3383a7b1a..60ae95b98f 100644 --- a/src/calibre/gui2/preferences/coloring.py +++ b/src/calibre/gui2/preferences/coloring.py @@ -138,35 +138,33 @@ class ConditionEditor(QWidget): # {{{ b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(20) - @dynamic_property + @property def current_col(self): - def fget(self): - idx = self.column_box.currentIndex() - return unicode_type(self.column_box.itemData(idx) or '') + idx = self.column_box.currentIndex() + return unicode_type(self.column_box.itemData(idx) or '') - def fset(self, val): - for idx in range(self.column_box.count()): - c = unicode_type(self.column_box.itemData(idx) or '') - if c == val: - self.column_box.setCurrentIndex(idx) - return - raise ValueError('Column %r not found'%val) - return property(fget=fget, fset=fset) + @current_col.setter + def current_col(self, val): + for idx in range(self.column_box.count()): + c = unicode_type(self.column_box.itemData(idx) or '') + if c == val: + self.column_box.setCurrentIndex(idx) + return + raise ValueError('Column %r not found'%val) - @dynamic_property + @property def current_action(self): - def fget(self): - idx = self.action_box.currentIndex() - return unicode_type(self.action_box.itemData(idx) or '') + idx = self.action_box.currentIndex() + return unicode_type(self.action_box.itemData(idx) or '') - def fset(self, val): - for idx in range(self.action_box.count()): - c = unicode_type(self.action_box.itemData(idx) or '') - if c == val: - self.action_box.setCurrentIndex(idx) - return - raise ValueError('Action %r not valid for current column'%val) - return property(fget=fget, fset=fset) + @current_action.setter + def current_action(self, val): + for idx in range(self.action_box.count()): + c = unicode_type(self.action_box.itemData(idx) or '') + if c == val: + self.action_box.setCurrentIndex(idx) + return + raise ValueError('Action %r not valid for current column'%val) @property def current_val(self): @@ -176,26 +174,24 @@ class ConditionEditor(QWidget): # {{{ ans = rmap.get(lower(ans), ans) return ans - @dynamic_property + @property def condition(self): - def fget(self): - c, a, v = (self.current_col, self.current_action, - self.current_val) - if not c or not a: - return None - return (c, a, v) + c, a, v = (self.current_col, self.current_action, + self.current_val) + if not c or not a: + return None + return (c, a, v) - def fset(self, condition): - c, a, v = condition - if not v: - v = '' - v = v.strip() - self.current_col = c - self.current_action = a - self.value_box.setText(v) - - return property(fget=fget, fset=fset) + @condition.setter + def condition(self, condition): + c, a, v = condition + if not v: + v = '' + v = v.strip() + self.current_col = c + self.current_action = a + self.value_box.setText(v) def init_action_box(self): self.action_box.blockSignals(True) diff --git a/src/calibre/gui2/tweak_book/editor/image.py b/src/calibre/gui2/tweak_book/editor/image.py index 2c1b72aa5c..580b0d02db 100644 --- a/src/calibre/gui2/tweak_book/editor/image.py +++ b/src/calibre/gui2/tweak_book/editor/image.py @@ -62,23 +62,21 @@ class ResizeDialog(QDialog): # {{{ other.setValue(oval) other.blockSignals(False) - @dynamic_property + @property def width(self): - def fget(self): - return self._width.value() + return self._width.value() - def fset(self, val): - self._width.setValue(val) - return property(fget=fget, fset=fset) + @width.setter + def width(self, val): + self._width.setValue(val) - @dynamic_property + @property def height(self): - def fget(self): - return self._height.value() + return self._height.value() - def fset(self, val): - self._height.setValue(val) - return property(fget=fget, fset=fset) + @height.setter + def height(self, val): + self._height.setValue(val) # }}} @@ -111,24 +109,22 @@ class Editor(QMainWindow): self.canvas.undo_redo_state_changed.connect(self.undo_redo_state_changed) self.canvas.selection_state_changed.connect(self.update_clipboard_actions) - @dynamic_property + @property def is_modified(self): - def fget(self): - return self._is_modified + return self._is_modified - def fset(self, val): - self._is_modified = val - self.modification_state_changed.emit(val) - return property(fget=fget, fset=fset) + @is_modified.setter + def is_modified(self, val): + self._is_modified = val + self.modification_state_changed.emit(val) - @dynamic_property + @property def current_editing_state(self): - def fget(self): - return {} + return {} - def fset(self, val): - pass - return property(fget=fget, fset=fset) + @current_editing_state.setter + def current_editing_state(self, val): + pass @property def undo_available(self): @@ -138,14 +134,13 @@ class Editor(QMainWindow): def redo_available(self): return self.canvas.redo_action.isEnabled() - @dynamic_property + @property def current_line(self): - def fget(self): - return 0 + return 0 - def fset(self, val): - pass - return property(fget=fget, fset=fset) + @current_line.setter + def current_line(self, val): + pass @property def number_of_lines(self): @@ -160,15 +155,14 @@ class Editor(QMainWindow): def get_raw_data(self): return self.canvas.get_image_data(quality=self.quality) - @dynamic_property + @property def data(self): - def fget(self): - return self.get_raw_data() + return self.get_raw_data() - def fset(self, val): - self.canvas.load_image(val) - self._is_modified = False # The image_changed signal will have been triggered causing this editor to be incorrectly marked as modified - return property(fget=fget, fset=fset) + @data.setter + def data(self, val): + self.canvas.load_image(val) + self._is_modified = False # The image_changed signal will have been triggered causing this editor to be incorrectly marked as modified def replace_data(self, raw, only_if_different=True): # We ignore only_if_different as it is useless in our case, and diff --git a/src/calibre/gui2/tweak_book/editor/snippets.py b/src/calibre/gui2/tweak_book/editor/snippets.py index 23512db75b..fd78baab4f 100644 --- a/src/calibre/gui2/tweak_book/editor/snippets.py +++ b/src/calibre/gui2/tweak_book/editor/snippets.py @@ -221,26 +221,25 @@ class EditorTabStop(object): with m: m.text = text - @dynamic_property + @property def text(self): - def fget(self): - editor = self.editor() - if editor is None or self.is_deleted: - return '' - c = editor.textCursor() - c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor) - return editor.selected_text_from_cursor(c) + editor = self.editor() + if editor is None or self.is_deleted: + return '' + c = editor.textCursor() + c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor) + return editor.selected_text_from_cursor(c) - def fset(self, text): - editor = self.editor() - if editor is None or self.is_deleted: - return - c = editor.textCursor() - c.joinPreviousEditBlock() if self.join_previous_edit else c.beginEditBlock() - c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor) - c.insertText(text) - c.endEditBlock() - return property(fget=fget, fset=fset) + @text.setter + def text(self, text): + editor = self.editor() + if editor is None or self.is_deleted: + return + c = editor.textCursor() + c.joinPreviousEditBlock() if self.join_previous_edit else c.beginEditBlock() + c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor) + c.insertText(text) + c.endEditBlock() def set_editor_cursor(self, editor): if not self.is_deleted: @@ -537,20 +536,18 @@ class EditSnippet(QWidget): self.types.item(0).setCheckState(Qt.Checked) (self.name if self.creating_snippet else self.template).setFocus(Qt.OtherFocusReason) - @dynamic_property + @property def snip(self): - def fset(self, snip): - self.apply_snip(snip) + ftypes = [] + for i in range(self.types.count()): + i = self.types.item(i) + if i.checkState() == Qt.Checked: + ftypes.append(i.data(Qt.UserRole)) + return {'description':self.name.text().strip(), 'trigger':self.trig.text(), 'template':self.template.toPlainText(), 'syntaxes':ftypes} - def fget(self): - ftypes = [] - for i in range(self.types.count()): - i = self.types.item(i) - if i.checkState() == Qt.Checked: - ftypes.append(i.data(Qt.UserRole)) - return {'description':self.name.text().strip(), 'trigger':self.trig.text(), 'template':self.template.toPlainText(), 'syntaxes':ftypes} - - return property(fget=fget, fset=fset) + @snip.setter + def snip(self, snip): + self.apply_snip(snip) def validate(self): snip = self.snip diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 55fc517013..e8eed6d278 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -206,17 +206,15 @@ class TextEdit(PlainTextEdit): insert_text(md.html()) return - @dynamic_property + @property def is_modified(self): ''' True if the document has been modified since it was loaded or since the last time is_modified was set to False. ''' + return self.document().isModified() - def fget(self): - return self.document().isModified() - - def fset(self, val): - self.document().setModified(bool(val)) - return property(fget=fget, fset=fset) + @is_modified.setter + def is_modified(self, val): + self.document().setModified(bool(val)) def sizeHint(self): return self.size_hint diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 6b31933c5f..21c62f0516 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -159,28 +159,26 @@ class Editor(QMainWindow): self.editor.link_clicked.connect(self.link_clicked) self.editor.smart_highlighting_updated.connect(self.smart_highlighting_updated) - @dynamic_property + @property def current_line(self): - def fget(self): - return self.editor.textCursor().blockNumber() + return self.editor.textCursor().blockNumber() - def fset(self, val): - self.editor.go_to_line(val) - return property(fget=fget, fset=fset) + @current_line.setter + def current_line(self, val): + self.editor.go_to_line(val) - @dynamic_property + @property def current_editing_state(self): - def fget(self): - c = self.editor.textCursor() - return {'cursor':(c.anchor(), c.position())} + c = self.editor.textCursor() + return {'cursor':(c.anchor(), c.position())} - def fset(self, val): - anchor, position = val.get('cursor', (None, None)) - if anchor is not None and position is not None: - c = self.editor.textCursor() - c.setPosition(anchor), c.setPosition(position, c.KeepAnchor) - self.editor.setTextCursor(c) - return property(fget=fget, fset=fset) + @current_editing_state.setter + def current_editing_state(self, val): + anchor, position = val.get('cursor', (None, None)) + if anchor is not None and position is not None: + c = self.editor.textCursor() + c.setPosition(anchor), c.setPosition(position, c.KeepAnchor) + self.editor.setTextCursor(c) def current_tag(self, for_position_sync=True): return self.editor.current_tag(for_position_sync=for_position_sync) @@ -189,18 +187,17 @@ class Editor(QMainWindow): def number_of_lines(self): return self.editor.blockCount() - @dynamic_property + @property def data(self): - def fget(self): - ans = self.get_raw_data() - ans, changed = replace_encoding_declarations(ans, enc='utf-8', limit=4*1024) - if changed: - self.data = ans - return ans.encode('utf-8') + ans = self.get_raw_data() + ans, changed = replace_encoding_declarations(ans, enc='utf-8', limit=4*1024) + if changed: + self.data = ans + return ans.encode('utf-8') - def fset(self, val): - self.editor.load_text(val, syntax=self.syntax, doc_name=editor_name(self)) - return property(fget=fget, fset=fset) + @data.setter + def data(self, val): + self.editor.load_text(val, syntax=self.syntax, doc_name=editor_name(self)) def init_from_template(self, template): self.editor.load_text(template, syntax=self.syntax, process_template=True, doc_name=editor_name(self)) @@ -317,14 +314,13 @@ class Editor(QMainWindow): def has_marked_text(self): return self.editor.current_search_mark is not None - @dynamic_property + @property def is_modified(self): - def fget(self): - return self.editor.is_modified + return self.editor.is_modified - def fset(self, val): - self.editor.is_modified = val - return property(fget=fget, fset=fset) + @is_modified.setter + def is_modified(self, val): + self.editor.is_modified = val def create_toolbars(self): self.action_bar = b = self.addToolBar(_('Edit actions tool bar')) diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index 61b443c443..5a325713fa 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -370,17 +370,16 @@ class WebView(QWebView): def refresh(self): self.pageAction(self.page().Reload).trigger() - @dynamic_property + @property def scroll_pos(self): - def fget(self): - mf = self.page().mainFrame() - return (mf.scrollBarValue(Qt.Horizontal), mf.scrollBarValue(Qt.Vertical)) + mf = self.page().mainFrame() + return (mf.scrollBarValue(Qt.Horizontal), mf.scrollBarValue(Qt.Vertical)) - def fset(self, val): - mf = self.page().mainFrame() - mf.setScrollBarValue(Qt.Horizontal, val[0]) - mf.setScrollBarValue(Qt.Vertical, val[1]) - return property(fget=fget, fset=fset) + @scroll_pos.setter + def scroll_pos(self, val): + mf = self.page().mainFrame() + mf.setScrollBarValue(Qt.Horizontal, val[0]) + mf.setScrollBarValue(Qt.Vertical, val[1]) def clear(self): self.setHtml(_( diff --git a/src/calibre/gui2/tweak_book/reports.py b/src/calibre/gui2/tweak_book/reports.py index 948356121d..64b943085e 100644 --- a/src/calibre/gui2/tweak_book/reports.py +++ b/src/calibre/gui2/tweak_book/reports.py @@ -1061,14 +1061,13 @@ class CSSWidget(QWidget): self.summary = la = QLabel('\xa0') h.addWidget(la) - @dynamic_property + @property def sort_order(self): - def fget(self): - return [Qt.AscendingOrder, Qt.DescendingOrder][self._sort_order.currentIndex()] + return [Qt.AscendingOrder, Qt.DescendingOrder][self._sort_order.currentIndex()] - def fset(self, val): - self._sort_order.setCurrentIndex({Qt.AscendingOrder:0}.get(val, 1)) - return property(fget=fget, fset=fset) + @sort_order.setter + def sort_order(self, val): + self._sort_order.setCurrentIndex({Qt.AscendingOrder:0}.get(val, 1)) def update_summary(self): self.summary.setText(_('{0} rules, {1} unused').format(self.model.rowCount(), self.model.num_unused)) diff --git a/src/calibre/gui2/tweak_book/search.py b/src/calibre/gui2/tweak_book/search.py index bb4598b4ac..d83997b051 100644 --- a/src/calibre/gui2/tweak_book/search.py +++ b/src/calibre/gui2/tweak_book/search.py @@ -152,16 +152,15 @@ class WhereBox(QComboBox): f.setBold(True), f.setItalic(True) self.setFont(f) - @dynamic_property + @property def where(self): wm = {0:'current', 1:'text', 2:'styles', 3:'selected', 4:'open', 5:'selected-text'} + return wm[self.currentIndex()] - def fget(self): - return wm[self.currentIndex()] - - def fset(self, val): - self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val]) - return property(fget=fget, fset=fset) + @where.setter + def where(self, val): + wm = {0:'current', 1:'text', 2:'styles', 3:'selected', 4:'open', 5:'selected-text'} + self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val]) def showPopup(self): # We do it like this so that the popup uses a normal font @@ -190,14 +189,13 @@ class DirectionBox(QComboBox):
Search for the previous match from your current position
''')) - @dynamic_property + @property def direction(self): - def fget(self): - return 'down' if self.currentIndex() == 0 else 'up' + return 'down' if self.currentIndex() == 0 else 'up' - def fset(self, val): - self.setCurrentIndex(1 if val == 'up' else 0) - return property(fget=fget, fset=fset) + @direction.setter + def direction(self, val): + self.setCurrentIndex(1 if val == 'up' else 0) class ModeBox(QComboBox): @@ -216,14 +214,13 @@ class ModeBox(QComboBox):
The search expression is interpreted as a regular expression. The replace expression is an arbitrarily powerful Python function.
''')) - @dynamic_property + @property def mode(self): - def fget(self): - return ('normal', 'regex', 'function')[self.currentIndex()] + return ('normal', 'regex', 'function')[self.currentIndex()] - def fset(self, val): - self.setCurrentIndex({'regex':1, 'function':2}.get(val, 0)) - return property(fget=fget, fset=fset) + @mode.setter + def mode(self, val): + self.setCurrentIndex({'regex':1, 'function':2}.get(val, 0)) class SearchWidget(QWidget): @@ -353,91 +350,82 @@ class SearchWidget(QWidget): self.replace_text.setVisible(not function_mode) self.functions_container.setVisible(function_mode) - @dynamic_property + @property def mode(self): - def fget(self): - return self.mode_box.mode + return self.mode_box.mode - def fset(self, val): - self.mode_box.mode = val - self.da.setVisible(self.mode in ('regex', 'function')) - return property(fget=fget, fset=fset) + @mode.setter + def mode(self, val): + self.mode_box.mode = val + self.da.setVisible(self.mode in ('regex', 'function')) - @dynamic_property + @property def find(self): - def fget(self): - return unicode_type(self.find_text.text()) + return unicode_type(self.find_text.text()) - def fset(self, val): - self.find_text.setText(val) - return property(fget=fget, fset=fset) + @find.setter + def find(self, val): + self.find_text.setText(val) - @dynamic_property + @property def replace(self): - def fget(self): - if self.mode == 'function': - return self.functions.text() - return unicode_type(self.replace_text.text()) + if self.mode == 'function': + return self.functions.text() + return unicode_type(self.replace_text.text()) - def fset(self, val): - self.replace_text.setText(val) - return property(fget=fget, fset=fset) + @replace.setter + def replace(self, val): + self.replace_text.setText(val) - @dynamic_property + @property def where(self): - def fget(self): - return self.where_box.where + return self.where_box.where - def fset(self, val): - self.where_box.where = val - return property(fget=fget, fset=fset) + @where.setter + def where(self, val): + self.where_box.where = val - @dynamic_property + @property def case_sensitive(self): - def fget(self): - return self.cs.isChecked() + return self.cs.isChecked() - def fset(self, val): - self.cs.setChecked(bool(val)) - return property(fget=fget, fset=fset) + @case_sensitive.setter + def case_sensitive(self, val): + self.cs.setChecked(bool(val)) - @dynamic_property + @property def direction(self): - def fget(self): - return self.direction_box.direction + return self.direction_box.direction - def fset(self, val): - self.direction_box.direction = val - return property(fget=fget, fset=fset) + @direction.setter + def direction(self, val): + self.direction_box.direction = val - @dynamic_property + @property def wrap(self): - def fget(self): - return self.wr.isChecked() + return self.wr.isChecked() - def fset(self, val): - self.wr.setChecked(bool(val)) - return property(fget=fget, fset=fset) + @wrap.setter + def wrap(self, val): + self.wr.setChecked(bool(val)) - @dynamic_property + @property def dot_all(self): - def fget(self): - return self.da.isChecked() + return self.da.isChecked() - def fset(self, val): - self.da.setChecked(bool(val)) - return property(fget=fget, fset=fset) + @dot_all.setter + def dot_all(self, val): + self.da.setChecked(bool(val)) - @dynamic_property + @property def state(self): - def fget(self): - return {x:getattr(self, x) for x in self.DEFAULT_STATE} + return {x:getattr(self, x) for x in self.DEFAULT_STATE} - def fset(self, val): - for x in self.DEFAULT_STATE: - if x in val: - setattr(self, x, val[x]) - return property(fget=fget, fset=fset) + @state.setter + def state(self, val): + for x in self.DEFAULT_STATE: + if x in val: + setattr(self, x, val[x]) def restore_state(self): self.state = tprefs.get('find-widget-state', self.DEFAULT_STATE) @@ -1008,14 +996,13 @@ class SavedSearches(QWidget): self.searches.setFocus(Qt.OtherFocusReason) - @dynamic_property + @property def state(self): - def fget(self): - return {'wrap':self.wrap, 'direction':self.direction, 'where':self.where} + return {'wrap':self.wrap, 'direction':self.direction, 'where':self.where} - def fset(self, val): - self.wrap, self.where, self.direction = val['wrap'], val['where'], val['direction'] - return property(fget=fget, fset=fset) + @state.setter + def state(self, val): + self.wrap, self.where, self.direction = val['wrap'], val['where'], val['direction'] def save_state(self): tprefs['saved_seaches_state'] = self.state @@ -1042,32 +1029,29 @@ class SavedSearches(QWidget): for x in ('eb', 'ab', 'rb', 'upb', 'dnb', 'd2', 'filter_text', 'cft', 'd3', 'ib', 'eb2'): getattr(self, x).setVisible(visible) - @dynamic_property + @property def where(self): - def fget(self): - return self.where_box.where + return self.where_box.where - def fset(self, val): - self.where_box.where = val - return property(fget=fget, fset=fset) + @where.setter + def where(self, val): + self.where_box.where = val - @dynamic_property + @property def direction(self): - def fget(self): - return self.direction_box.direction + return self.direction_box.direction - def fset(self, val): - self.direction_box.direction = val - return property(fget=fget, fset=fset) + @direction.setter + def direction(self, val): + self.direction_box.direction = val - @dynamic_property + @property def wrap(self): - def fget(self): - return self.wr.isChecked() + return self.wr.isChecked() - def fset(self, val): - self.wr.setChecked(bool(val)) - return property(fget=fget, fset=fset) + @wrap.setter + def wrap(self, val): + self.wr.setChecked(bool(val)) def do_filter(self, text): self.model.do_filter(text) diff --git a/src/calibre/gui2/tweak_book/text_search.py b/src/calibre/gui2/tweak_book/text_search.py index 23794a6577..b44967c444 100644 --- a/src/calibre/gui2/tweak_book/text_search.py +++ b/src/calibre/gui2/tweak_book/text_search.py @@ -36,14 +36,13 @@ class ModeBox(QComboBox):
The search expression is interpreted as a regular expression. See the User Manual for more help on using regular expressions.
''')) - @dynamic_property + @property def mode(self): - def fget(self): - return ('normal', 'regex')[self.currentIndex()] + return ('normal', 'regex')[self.currentIndex()] - def fset(self, val): - self.setCurrentIndex({'regex':1}.get(val, 0)) - return property(fget=fget, fset=fset) + @mode.setter + def mode(self, val): + self.setCurrentIndex({'regex':1}.get(val, 0)) class WhereBox(QComboBox): @@ -71,16 +70,15 @@ class WhereBox(QComboBox): f.setBold(True), f.setItalic(True) self.setFont(f) - @dynamic_property + @property def where(self): wm = {0:'current', 1:'text', 2:'selected', 3:'open'} + return wm[self.currentIndex()] - def fget(self): - return wm[self.currentIndex()] - - def fset(self, val): - self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val]) - return property(fget=fget, fset=fset) + @where.setter + def where(self, val): + wm = {0:'current', 1:'text', 2:'selected', 3:'open'} + self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val]) def showPopup(self): # We do it like this so that the popup uses a normal font @@ -137,17 +135,16 @@ class TextSearch(QWidget): state = tprefs.get('text_search_widget_state') self.state = state or {} - @dynamic_property + @property def state(self): - def fget(self): - return {'mode': self.mode.mode, 'where':self.where_box.where, 'case_sensitive':self.cs.isChecked(), 'dot_all':self.da.isChecked()} + return {'mode': self.mode.mode, 'where':self.where_box.where, 'case_sensitive':self.cs.isChecked(), 'dot_all':self.da.isChecked()} - def fset(self, val): - self.mode.mode = val.get('mode', 'normal') - self.where_box.where = val.get('where', 'current') - self.cs.setChecked(bool(val.get('case_sensitive'))) - self.da.setChecked(bool(val.get('dot_all', True))) - return property(fget=fget, fset=fset) + @state.setter + def state(self, val): + self.mode.mode = val.get('mode', 'normal') + self.where_box.where = val.get('where', 'current') + self.cs.setChecked(bool(val.get('case_sensitive'))) + self.da.setChecked(bool(val.get('dot_all', True))) def save_state(self): tprefs['text_search_widget_state'] = self.state diff --git a/src/calibre/gui2/viewer/config.py b/src/calibre/gui2/viewer/config.py index 8f310d53e8..4e1f2f6d00 100644 --- a/src/calibre/gui2/viewer/config.py +++ b/src/calibre/gui2/viewer/config.py @@ -236,17 +236,16 @@ class ConfigDialog(QDialog, Ui_Dialog): from calibre.gui2.viewer.main import dprefs self.word_lookups = dprefs['word_lookups'] - @dynamic_property + @property def word_lookups(self): - def fget(self): - return dict(self.dictionary_list.item(i).data(Qt.UserRole) for i in range(self.dictionary_list.count())) + return dict(self.dictionary_list.item(i).data(Qt.UserRole) for i in range(self.dictionary_list.count())) - def fset(self, wl): - self.dictionary_list.clear() - for langcode, url in sorted(iteritems(wl), key=lambda lc_url:sort_key(calibre_langcode_to_name(lc_url[0]))): - i = QListWidgetItem('%s: %s' % (calibre_langcode_to_name(langcode), url), self.dictionary_list) - i.setData(Qt.UserRole, (langcode, url)) - return property(fget=fget, fset=fset) + @word_lookups.setter + def word_lookups(self, wl): + self.dictionary_list.clear() + for langcode, url in sorted(iteritems(wl), key=lambda lc_url:sort_key(calibre_langcode_to_name(lc_url[0]))): + i = QListWidgetItem('%s: %s' % (calibre_langcode_to_name(langcode), url), self.dictionary_list) + i.setData(Qt.UserRole, (langcode, url)) def add_dictionary_website(self): class AD(QDialog): diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 91232c0cd7..b8c210ef7f 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -430,46 +430,43 @@ class Document(QWebPage): # {{{ def xpos(self): return self.mainFrame().scrollPosition().x() - @dynamic_property + @property def scroll_fraction(self): - def fget(self): - if self.in_paged_mode: - return self.javascript(''' - ans = 0.0; - if (window.paged_display) { - ans = window.paged_display.current_pos(); - } - ans;''', typ='float') - else: - try: - return abs(float(self.ypos)/(self.height-self.window_height)) - except ZeroDivisionError: - return 0. + if self.in_paged_mode: + return self.javascript(''' + ans = 0.0; + if (window.paged_display) { + ans = window.paged_display.current_pos(); + } + ans;''', typ='float') + else: + try: + return abs(float(self.ypos)/(self.height-self.window_height)) + except ZeroDivisionError: + return 0. - def fset(self, val): - if self.in_paged_mode and self.loaded_javascript: - self.javascript('paged_display.scroll_to_pos(%f)'%val) - else: - npos = val * (self.height - self.window_height) - if npos < 0: - npos = 0 - self.scroll_to(x=self.xpos, y=npos) - return property(fget=fget, fset=fset) + @scroll_fraction.setter + def scroll_fraction(self, val): + if self.in_paged_mode and self.loaded_javascript: + self.javascript('paged_display.scroll_to_pos(%f)'%val) + else: + npos = val * (self.height - self.window_height) + if npos < 0: + npos = 0 + self.scroll_to(x=self.xpos, y=npos) - @dynamic_property + @property def page_number(self): ' The page number is the number of the page at the left most edge of the screen (starting from 0) ' + if self.in_paged_mode: + return self.javascript( + 'ans = 0; if (window.paged_display) ans = window.paged_display.column_boundaries()[0]; ans;', typ='int') - def fget(self): - if self.in_paged_mode: - return self.javascript( - 'ans = 0; if (window.paged_display) ans = window.paged_display.column_boundaries()[0]; ans;', typ='int') - - def fset(self, val): - if self.in_paged_mode and self.loaded_javascript: - self.javascript('if (window.paged_display) window.paged_display.scroll_to_column(%d)' % int(val)) - return True - return property(fget=fget, fset=fset) + @page_number.setter + def page_number(self, val): + if self.in_paged_mode and self.loaded_javascript: + self.javascript('if (window.paged_display) window.paged_display.scroll_to_column(%d)' % int(val)) + return True @property def page_dimensions(self): @@ -862,14 +859,13 @@ class DocumentView(QWebView): # {{{ def sizeHint(self): return self._size_hint - @dynamic_property + @property def scroll_fraction(self): - def fget(self): - return self.document.scroll_fraction + return self.document.scroll_fraction - def fset(self, val): - self.document.scroll_fraction = float(val) - return property(fget=fget, fset=fset) + @scroll_fraction.setter + def scroll_fraction(self, val): + self.document.scroll_fraction = float(val) @property def hscroll_fraction(self): @@ -879,14 +875,13 @@ class DocumentView(QWebView): # {{{ def content_size(self): return self.document.width, self.document.height - @dynamic_property + @property def current_language(self): - def fget(self): - return self.document.current_language + return self.document.current_language - def fset(self, val): - self.document.current_language = val - return property(fget=fget, fset=fset) + @current_language.setter + def current_language(self, val): + self.document.current_language = val def search(self, text, backwards=False): flags = self.document.FindBackward if backwards else self.document.FindFlags(0) @@ -1189,19 +1184,18 @@ class DocumentView(QWebView): # {{{ if notify and self.manager is not None and new_pos != old_pos: self.manager.scrolled(self.scroll_fraction) - @dynamic_property + @property def multiplier(self): - def fget(self): - return self.zoomFactor() + return self.zoomFactor() - def fset(self, val): - oval = self.zoomFactor() - self.setZoomFactor(val) - if val != oval: - if self.document.in_paged_mode: - self.document.update_contents_size_for_paged_mode() - self.magnification_changed.emit(val) - return property(fget=fget, fset=fset) + @multiplier.setter + def multiplier(self, val): + oval = self.zoomFactor() + self.setZoomFactor(val) + if val != oval: + if self.document.in_paged_mode: + self.document.update_contents_size_for_paged_mode() + self.magnification_changed.emit(val) def magnify_fonts(self, amount=None): if amount is None: diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index ff02851f8b..9eb9861a18 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -1131,30 +1131,28 @@ class Splitter(QSplitter): print(self.save_name, 'side:', self.side_index_size, 'other:', end=' ') print(list(self.sizes())[self.other_index]) - @dynamic_property + @property def side_index_size(self): - def fget(self): - if self.count() < 2: - return 0 - return self.sizes()[self.side_index] + if self.count() < 2: + return 0 + return self.sizes()[self.side_index] - def fset(self, val): - if self.count() < 2: - return - if val == 0 and not self.is_side_index_hidden: - self.save_state() - sizes = list(self.sizes()) - for i in range(len(sizes)): - sizes[i] = val if i == self.side_index else 10 - self.setSizes(sizes) - total = sum(self.sizes()) - sizes = list(self.sizes()) - for i in range(len(sizes)): - sizes[i] = val if i == self.side_index else total-val - self.setSizes(sizes) - self.initialize() - - return property(fget=fget, fset=fset) + @side_index_size.setter + def side_index_size(self, val): + if self.count() < 2: + return + if val == 0 and not self.is_side_index_hidden: + self.save_state() + sizes = list(self.sizes()) + for i in range(len(sizes)): + sizes[i] = val if i == self.side_index else 10 + self.setSizes(sizes) + total = sum(self.sizes()) + sizes = list(self.sizes()) + for i in range(len(sizes)): + sizes[i] = val if i == self.side_index else total-val + self.setSizes(sizes) + self.initialize() def do_resize(self, *args): orig = self.desired_side_size diff --git a/src/calibre/gui2/widgets2.py b/src/calibre/gui2/widgets2.py index 76db5fc928..135405f044 100644 --- a/src/calibre/gui2/widgets2.py +++ b/src/calibre/gui2/widgets2.py @@ -86,28 +86,27 @@ class ColorButton(QPushButton): self.color = initial_color self.clicked.connect(self.choose_color) - @dynamic_property + @property def color(self): - def fget(self): - return self._color + return self._color - def fset(self, val): - val = unicode_type(val or '') - col = QColor(val) - orig = self._color - if col.isValid(): - self._color = val - self.setText(val) - p = QPixmap(self.iconSize()) - p.fill(col) - self.setIcon(QIcon(p)) - else: - self._color = None - self.setText(self.choose_text) - self.setIcon(QIcon()) - if orig != col: - self.color_changed.emit(self._color) - return property(fget=fget, fset=fset) + @color.setter + def color(self, val): + val = unicode_type(val or '') + col = QColor(val) + orig = self._color + if col.isValid(): + self._color = val + self.setText(val) + p = QPixmap(self.iconSize()) + p.fill(col) + self.setIcon(QIcon(p)) + else: + self._color = None + self.setText(self.choose_text) + self.setIcon(QIcon()) + if orig != col: + self.color_changed.emit(self._color) def choose_color(self): col = QColorDialog.getColor(QColor(self._color or Qt.white), self, _('Choose a color')) diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index c47c932639..7a898d566c 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -827,13 +827,10 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; # _lock_file = None self.conn.close() - @dynamic_property + @property def user_version(self): - doc = 'The user version of this database' - - def fget(self): - return self.conn.get('pragma user_version;', all=False) - return property(doc=doc, fget=fget) + 'The user version of this database' + return self.conn.get('pragma user_version;', all=False) def is_empty(self): return not self.conn.get('SELECT id FROM books LIMIT 1', all=False) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index e73f76bf5b..a5a7ce4b49 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -75,43 +75,36 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): PATH_LIMIT = 40 if 'win32' in sys.platform else 100 WINDOWS_LIBRARY_PATH_LIMIT = 75 - @dynamic_property + @property def user_version(self): - doc = 'The user version of this database' + 'The user version of this database' + return self.conn.get('pragma user_version;', all=False) - def fget(self): - return self.conn.get('pragma user_version;', all=False) + @user_version.setter + def user_version(self, val): + self.conn.execute('pragma user_version=%d'%int(val)) + self.conn.commit() - def fset(self, val): - self.conn.execute('pragma user_version=%d'%int(val)) - self.conn.commit() - - return property(doc=doc, fget=fget, fset=fset) - - @dynamic_property + @property def library_id(self): - doc = ('The UUID for this library. As long as the user only operates' - ' on libraries with calibre, it will be unique') + '''The UUID for this library. As long as the user only operates on libraries with calibre, it will be unique''' + if self._library_id_ is None: + ans = self.conn.get('SELECT uuid FROM library_id', all=False) + if ans is None: + ans = str(uuid.uuid4()) + self.library_id = ans + else: + self._library_id_ = ans + return self._library_id_ - def fget(self): - if self._library_id_ is None: - ans = self.conn.get('SELECT uuid FROM library_id', all=False) - if ans is None: - ans = str(uuid.uuid4()) - self.library_id = ans - else: - self._library_id_ = ans - return self._library_id_ - - def fset(self, val): - self._library_id_ = unicode_type(val) - self.conn.executescript(''' - DELETE FROM library_id; - INSERT INTO library_id (uuid) VALUES ("%s"); - '''%self._library_id_) - self.conn.commit() - - return property(doc=doc, fget=fget, fset=fset) + @library_id.setter + def library_id(self, val): + self._library_id_ = unicode_type(val) + self.conn.executescript(''' + DELETE FROM library_id; + INSERT INTO library_id (uuid) VALUES ("%s"); + '''%self._library_id_) + self.conn.commit() def connect(self): if iswindows and len(self.library_path) + 4*self.PATH_LIMIT + 10 > 259: diff --git a/src/calibre/utils/magick/legacy.py b/src/calibre/utils/magick/legacy.py index 388f7ee9e1..b144e32235 100644 --- a/src/calibre/utils/magick/legacy.py +++ b/src/calibre/utils/magick/legacy.py @@ -57,47 +57,43 @@ class Image(object): def to_qimage(self): return clone_image(self.img) - @dynamic_property + @property def type(self): - def fget(self): - if len(self.img.colorTable()) > 0: - return 'PaletteType' - return 'TrueColorType' + if len(self.img.colorTable()) > 0: + return 'PaletteType' + return 'TrueColorType' - def fset(self, t): - if t == 'GrayscaleType': - self.img = grayscale_image(self.img) - elif t == 'PaletteType': - self.img = quantize_image(self.img) - return property(fget=fget, fset=fset) + @type.setter + def type(self, t): + if t == 'GrayscaleType': + self.img = grayscale_image(self.img) + elif t == 'PaletteType': + self.img = quantize_image(self.img) - @dynamic_property + @property def format(self): - def fget(self): - return self.write_format or self.read_format + return self.write_format or self.read_format - def fset(self, val): - self.write_format = val - return property(fget=fget, fset=fset) + @format.setter + def format(self, val): + self.write_format = val - @dynamic_property + @property def colorspace(self): - def fget(self): - return 'RGBColorspace' + return 'RGBColorspace' - def fset(self, val): - raise NotImplementedError('Changing image colorspace is not supported') - return property(fget=fget, fset=fset) + @colorspace.setter + def colorspace(self, val): + raise NotImplementedError('Changing image colorspace is not supported') - @dynamic_property + @property def size(self): - def fget(self): - return self.img.width(), self.img.height() + return self.img.width(), self.img.height() - def fset(self, val): - w, h = val[:2] - self.img = resize_image(self.img, w, h) - return property(fget=fget, fset=fset) + @size.setter + def size(self, val): + w, h = val[:2] + self.img = resize_image(self.img, w, h) def save(self, path, format=None): if format is None: diff --git a/src/calibre/web/feeds/__init__.py b/src/calibre/web/feeds/__init__.py index 3b42ec7f18..db9e8bc572 100644 --- a/src/calibre/web/feeds/__init__.py +++ b/src/calibre/web/feeds/__init__.py @@ -57,32 +57,29 @@ class Article(object): self.localtime = self.utctime.astimezone(local_tz) self._formatted_date = None - @dynamic_property + @property def formatted_date(self): - def fget(self): - if self._formatted_date is None: - self._formatted_date = strftime(" [%a, %d %b %H:%M]", - t=self.localtime.timetuple()) - return self._formatted_date + if self._formatted_date is None: + self._formatted_date = strftime(" [%a, %d %b %H:%M]", + t=self.localtime.timetuple()) + return self._formatted_date - def fset(self, val): - if isinstance(val, unicode_type): - self._formatted_date = val + @formatted_date.setter + def formatted_date(self, val): + if isinstance(val, unicode_type): + self._formatted_date = val - return property(fget=fget, fset=fset) - - @dynamic_property + @property def title(self): - def fget(self): - t = self._title - if not isinstance(t, unicode_type) and hasattr(t, 'decode'): - t = t.decode('utf-8', 'replace') - return t + t = self._title + if not isinstance(t, unicode_type) and hasattr(t, 'decode'): + t = t.decode('utf-8', 'replace') + return t - def fset(self, val): - self._title = clean_ascii_chars(val) - return property(fget=fget, fset=fset) + @title.setter + def title(self, val): + self._title = clean_ascii_chars(val) def __repr__(self): return \