Get rid of @dynamic_property

This commit is contained in:
Kovid Goyal 2019-05-15 20:39:48 +05:30
parent 846a1a5e9f
commit f51e63718c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
43 changed files with 1269 additions and 1487 deletions

View File

@ -1,6 +1,6 @@
[flake8] [flake8]
max-line-length = 160 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 ignore = E12,E203,E22,E231,E241,E401,E402,E731,W391,E722,E741,W504
[yapf] [yapf]

View File

@ -4,9 +4,8 @@ __copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, os, re, time, random, warnings 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) itervalues, unicode_type, range, filter, hasenv)
builtins.__dict__['dynamic_property'] = lambda func: func(None)
from math import floor from math import floor
from functools import partial from functools import partial

View File

@ -1145,17 +1145,14 @@ class DB(object):
def vacuum(self): def vacuum(self):
self.execute('VACUUM') self.execute('VACUUM')
@dynamic_property @property
def user_version(self): 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): @user_version.setter
return self.conn.get('pragma user_version;', all=False) def user_version(self, val):
self.execute('pragma user_version=%d'%int(val))
def fset(self, val):
self.execute('pragma user_version=%d'%int(val))
return property(doc=doc, fget=fget, fset=fset)
def initialize_database(self): def initialize_database(self):
metadata_sqlite = P('metadata_sqlite.sql', data=True, metadata_sqlite = P('metadata_sqlite.sql', data=True,
@ -1252,29 +1249,26 @@ class DB(object):
def exists_at(cls, path): def exists_at(cls, path):
return path and os.path.exists(os.path.join(path, 'metadata.db')) return path and os.path.exists(os.path.join(path, 'metadata.db'))
@dynamic_property @property
def library_id(self): def library_id(self):
doc = ('The UUID for this library. As long as the user only operates' '''The UUID for this library. As long as the user only operates on libraries with calibre, it will be unique'''
' on libraries with calibre, it will be unique')
def fget(self): if getattr(self, '_library_id_', None) is None:
if getattr(self, '_library_id_', None) is None: ans = self.conn.get('SELECT uuid FROM library_id', all=False)
ans = self.conn.get('SELECT uuid FROM library_id', all=False) if ans is None:
if ans is None: ans = str(uuid.uuid4())
ans = str(uuid.uuid4()) self.library_id = ans
self.library_id = ans else:
else: self._library_id_ = ans
self._library_id_ = ans return self._library_id_
return self._library_id_
def fset(self, val): @library_id.setter
self._library_id_ = unicode_type(val) def library_id(self, val):
self.execute(''' self._library_id_ = unicode_type(val)
DELETE FROM library_id; self.execute('''
INSERT INTO library_id (uuid) VALUES (?); DELETE FROM library_id;
''', (self._library_id_,)) INSERT INTO library_id (uuid) VALUES (?);
''', (self._library_id_,))
return property(doc=doc, fget=fget, fset=fset)
def last_modified(self): def last_modified(self):
''' Return last modified time as a UTC datetime object ''' ''' Return last modified time as a UTC datetime object '''

View File

@ -31,73 +31,55 @@ class FileFormatter(object):
self.name = file.name self.name = file.name
self.path = file.path self.path = file.path
@dynamic_property @property
def mode_string(self): 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): @property
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
def isdir_name(self): 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): @property
name = self.name
if self.is_dir:
name += '/'
return name
return property(doc=doc, fget=fget)
@dynamic_property
def name_in_color(self): 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): @property
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
def human_readable_size(self): 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): @property
return human_readable(self.size)
return property(doc=doc, fget=fget)
@dynamic_property
def modification_time(self): 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): @property
return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime))
return property(doc=doc, fget=fget)
@dynamic_property
def creation_time(self): def creation_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.ctime))
def fget(self):
return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime))
return property(doc=doc, fget=fget)
def info(dev): def info(dev):

View File

@ -46,26 +46,21 @@ class Book(Metadata):
# use lpath because the prefix can change, changing path # use lpath because the prefix can change, changing path
return self.lpath == getattr(other, 'lpath', None) return self.lpath == getattr(other, 'lpath', None)
@dynamic_property @property
def db_id(self): 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])
match = re.search(r'_(\d+)$', self.lpath.rpartition('.')[0]) if match:
if match: return int(match.group(1))
return int(match.group(1)) return None
return None
return property(fget=fget, doc=doc)
@dynamic_property @property
def title_sorter(self): 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): @property
return title_sort(self.title)
return property(doc=doc, fget=fget)
@dynamic_property
def thumbnail(self): def thumbnail(self):
return None return None

View File

@ -169,24 +169,23 @@ class Block(object):
def height(self): def height(self):
return int(ceil(sum(l if isinstance(l, numbers.Number) else l.boundingRect().height() for l in self.layouts))) 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 position(self):
def fget(self): return self._position
return self._position
def fset(self, new_pos): @position.setter
(x, y) = new_pos def position(self, new_pos):
self._position = Point(x, y) (x, y) = new_pos
if self.layouts: self._position = Point(x, y)
self.layouts[0].setPosition(QPointF(x, y)) if self.layouts:
y += self.layouts[0].boundingRect().height() self.layouts[0].setPosition(QPointF(x, y))
for l in self.layouts[1:]: y += self.layouts[0].boundingRect().height()
if isinstance(l, numbers.Number): for l in self.layouts[1:]:
y += l if isinstance(l, numbers.Number):
else: y += l
l.setPosition(QPointF(x, y)) else:
y += l.boundingRect().height() l.setPosition(QPointF(x, y))
return property(fget=fget, fset=fset) y += l.boundingRect().height()
def draw(self, painter): def draw(self, painter):
for l in self.layouts: for l in self.layouts:

View File

@ -209,37 +209,29 @@ class Tag(object):
s += " at %08X, contents: %s" % (self.offset, repr(self.contents)) s += " at %08X, contents: %s" % (self.offset, repr(self.contents))
return s return s
@dynamic_property @property
def byte(self): def byte(self):
def fget(self): if len(self.contents) != 1:
if len(self.contents) != 1: raise LRFParseError("Bad parameter for tag ID: %04X" % self.id)
raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) return struct.unpack("<B", self.contents)[0]
return struct.unpack("<B", self.contents)[0]
return property(fget=fget)
@dynamic_property @property
def word(self): def word(self):
def fget(self): if len(self.contents) != 2:
if len(self.contents) != 2: raise LRFParseError("Bad parameter for tag ID: %04X" % self.id)
raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) return struct.unpack("<H", self.contents)[0]
return struct.unpack("<H", self.contents)[0]
return property(fget=fget)
@dynamic_property @property
def sword(self): def sword(self):
def fget(self): if len(self.contents) != 2:
if len(self.contents) != 2: raise LRFParseError("Bad parameter for tag ID: %04X" % self.id)
raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) return struct.unpack("<h", self.contents)[0]
return struct.unpack("<h", self.contents)[0]
return property(fget=fget)
@dynamic_property @property
def dword(self): def dword(self):
def fget(self): if len(self.contents) != 4:
if len(self.contents) != 4: raise LRFParseError("Bad parameter for tag ID: %04X" % self.id)
raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) return struct.unpack("<I", self.contents)[0]
return struct.unpack("<I", self.contents)[0]
return property(fget=fget)
def dummy_parser(self, stream): def dummy_parser(self, stream):
raise LRFParseError("Unknown tag at %08X" % stream.tell()) raise LRFParseError("Unknown tag at %08X" % stream.tell())

View File

@ -193,14 +193,13 @@ class ManifestItem(Resource): # {{{
res.mime_type = mt res.mime_type = mt
return res return res
@dynamic_property @property
def media_type(self): def media_type(self):
def fget(self): return self.mime_type
return self.mime_type
def fset(self, val): @media_type.setter
self.mime_type = val def media_type(self, val):
return property(fget=fget, fset=fset) self.mime_type = val
def __unicode__representation__(self): def __unicode__representation__(self):
return u'<item id="%s" href="%s" media-type="%s" />'%(self.id, self.href(), self.media_type) return u'<item id="%s" href="%s" media-type="%s" />'%(self.id, self.href(), self.media_type)
@ -804,172 +803,155 @@ class OPF(object): # {{{
for item in self.iterguide(): for item in self.iterguide():
item.set('href', get_href(item)) item.set('href', get_href(item))
@dynamic_property @property
def title(self): def title(self):
# TODO: Add support for EPUB 3 refinements # TODO: Add support for EPUB 3 refinements
def fget(self): for elem in self.title_path(self.metadata):
for elem in self.title_path(self.metadata): title = self.get_text(elem)
title = self.get_text(elem) if title and title.strip():
if title and title.strip(): return re.sub(r'\s+', ' ', title.strip())
return re.sub(r'\s+', ' ', title.strip())
def fset(self, val): @title.setter
val = (val or '').strip() def title(self, val):
titles = self.title_path(self.metadata) val = (val or '').strip()
if self.package_version < 3: titles = self.title_path(self.metadata)
# EPUB 3 allows multiple title elements containing sub-titles, if self.package_version < 3:
# series and other things. We all loooove EPUB 3. # EPUB 3 allows multiple title elements containing sub-titles,
for title in titles: # series and other things. We all loooove EPUB 3.
title.getparent().remove(title) for title in titles:
titles = () title.getparent().remove(title)
if val: titles = ()
title = titles[0] if titles else self.create_metadata_element('title') if val:
title.text = re.sub(r'\s+', ' ', unicode_type(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) @property
@dynamic_property
def authors(self): 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): @authors.setter
ans = [] def authors(self, val):
for elem in self.authors_path(self.metadata): remove = list(self.authors_path(self.metadata))
ans.extend(string_to_authors(self.get_text(elem))) for elem in remove:
return ans elem.getparent().remove(elem)
# Ensure new author element is at the top of the list
# for broken implementations that always use the first
# <dc:creator> 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): @property
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
# <dc:creator> 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
def author_sort(self): 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): @author_sort.setter
matches = self.authors_path(self.metadata) def author_sort(self, val):
if matches: matches = self.authors_path(self.metadata)
for match in matches: if matches:
ans = match.get('{%s}file-as'%self.NAMESPACES['opf'], None) for key in matches[0].attrib:
if not ans: if key.endswith('file-as'):
ans = match.get('file-as', None) matches[0].attrib.pop(key)
if ans: matches[0].set('{%s}file-as'%self.NAMESPACES['opf'], unicode_type(val))
return ans
def fset(self, val): @property
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
def tags(self): 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): @tags.setter
ans = [] def tags(self, val):
for tag in self.tags_path(self.metadata): for tag in list(self.tags_path(self.metadata)):
text = self.get_text(tag) tag.getparent().remove(tag)
if text and text.strip(): for tag in val:
ans.extend([x.strip() for x in text.split(',')]) elem = self.create_metadata_element('subject')
return ans self.set_text(elem, unicode_type(tag))
def fset(self, val): @property
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
def pubdate(self): 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): @pubdate.setter
ans = None def pubdate(self, val):
for match in self.pubdate_path(self.metadata): least_val = least_elem = None
try: for match in self.pubdate_path(self.metadata):
val = parse_date(etree.tostring(match, encoding=unicode_type, try:
method='text', with_tail=False).strip()) cval = parse_date(etree.tostring(match, encoding=unicode_type,
except: method='text', with_tail=False).strip())
continue except:
if ans is None or val < ans: match.getparent().remove(match)
ans = val else:
return ans if not val:
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:
match.getparent().remove(match) match.getparent().remove(match)
else: if least_val is None or cval < least_val:
if not val: least_val, least_elem = cval, match
match.getparent().remove(match)
if least_val is None or cval < least_val:
least_val, least_elem = cval, match
if val: if val:
if least_val is None: if least_val is None:
least_elem = self.create_metadata_element('date') least_elem = self.create_metadata_element('date')
least_elem.attrib.clear() least_elem.attrib.clear()
least_elem.text = isoformat(val) least_elem.text = isoformat(val)
return property(fget=fget, fset=fset) @property
@dynamic_property
def isbn(self): def isbn(self):
for match in self.isbn_path(self.metadata):
return self.get_text(match) or None
def fget(self): @isbn.setter
for match in self.isbn_path(self.metadata): def isbn(self, val):
return self.get_text(match) or None 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): matches = self.isbn_path(self.metadata)
uuid_id = None if not val:
for attr in self.root.attrib: for x in matches:
if attr.endswith('unique-identifier'): xid = x.get('id', None)
uuid_id = self.root.attrib[attr] is_package_identifier = uuid_id is not None and uuid_id == xid
break if is_package_identifier:
self.set_text(x, str(uuid.uuid4()))
matches = self.isbn_path(self.metadata) for attr in x.attrib:
if not val: if attr.endswith('scheme'):
for x in matches: x.attrib[attr] = 'uuid'
xid = x.get('id', None) else:
is_package_identifier = uuid_id is not None and uuid_id == xid x.getparent().remove(x)
if is_package_identifier: return
self.set_text(x, str(uuid.uuid4())) if not matches:
for attr in x.attrib: attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'ISBN'}
if attr.endswith('scheme'): matches = [self.create_metadata_element('identifier',
x.attrib[attr] = 'uuid' attrib=attrib)]
else: self.set_text(matches[0], unicode_type(val))
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)
def get_identifiers(self): def get_identifiers(self):
identifiers = {} identifiers = {}
@ -1024,85 +1006,73 @@ class OPF(object): # {{{
self.set_text(self.create_metadata_element( self.set_text(self.create_metadata_element(
'identifier', attrib=attrib), unicode_type(val)) 'identifier', attrib=attrib), unicode_type(val))
@dynamic_property @property
def application_id(self): def application_id(self):
for match in self.application_id_path(self.metadata):
return self.get_text(match) or None
def fget(self): @application_id.setter
for match in self.application_id_path(self.metadata): def application_id(self, val):
return self.get_text(match) or None 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): uuid_id = None
removed_ids = set() for attr in self.root.attrib:
for x in tuple(self.application_id_path(self.metadata)): if attr.endswith('unique-identifier'):
removed_ids.add(x.get('id', None)) uuid_id = self.root.attrib[attr]
x.getparent().remove(x) 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 @property
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
def uuid(self): def uuid(self):
for match in self.uuid_id_path(self.metadata):
return self.get_text(match) or None
def fget(self): @uuid.setter
for match in self.uuid_id_path(self.metadata): def uuid(self, val):
return self.get_text(match) or None 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): @property
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
def language(self): def language(self):
ans = self.languages
if ans:
return ans[0]
def fget(self): @language.setter
ans = self.languages def language(self, val):
if ans: self.languages = [val]
return ans[0]
def fset(self, val): @property
self.languages = [val]
return property(fget=fget, fset=fset)
@dynamic_property
def languages(self): 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): @languages.setter
ans = [] def languages(self, val):
for match in self.languages_path(self.metadata): matches = self.languages_path(self.metadata)
t = self.get_text(match) for x in matches:
if t and t.strip(): x.getparent().remove(x)
l = canonicalize_lang(t.strip())
if l:
ans.append(l)
return ans
def fset(self, val): for lang in val:
matches = self.languages_path(self.metadata) l = self.create_metadata_element('language')
for x in matches: self.set_text(l, unicode_type(lang))
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)
@property @property
def raw_languages(self): def raw_languages(self):
@ -1111,20 +1081,18 @@ class OPF(object): # {{{
if t and t.strip(): if t and t.strip():
yield t.strip() yield t.strip()
@dynamic_property @property
def book_producer(self): def book_producer(self):
for match in self.bkp_path(self.metadata):
return self.get_text(match) or None
def fget(self): @book_producer.setter
for match in self.bkp_path(self.metadata): def book_producer(self, val):
return self.get_text(match) or None matches = self.bkp_path(self.metadata)
if not matches:
def fset(self, val): matches = [self.create_metadata_element('contributor')]
matches = self.bkp_path(self.metadata) matches[0].set('{%s}role'%self.NAMESPACES['opf'], 'bkp')
if not matches: self.set_text(matches[0], unicode_type(val))
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)
def identifier_iter(self): def identifier_iter(self):
for item in self.identifier_path(self.metadata): for item in self.identifier_path(self.metadata):
@ -1238,42 +1206,39 @@ class OPF(object): # {{{
if path and os.path.exists(path): if path and os.path.exists(path):
return path return path
@dynamic_property @property
def cover(self): 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): @cover.setter
if self.guide is not None: def cover(self, path):
for t in ('cover', 'other.ms-coverimage-standard', 'other.ms-coverimage'): if self.guide is not None:
for item in self.guide: self.guide.set_cover(path)
if item.type and item.type.lower() == t: for item in list(self.iterguide()):
return item.path if 'cover' in item.get('type', ''):
try: item.getparent().remove(item)
if self.try_to_guess_cover:
return self.guess_cover()
except:
pass
def fset(self, path): else:
if self.guide is not None: g = self.create_guide_element()
self.guide.set_cover(path) self.guide = Guide()
for item in list(self.iterguide()): self.guide.set_cover(path)
if 'cover' in item.get('type', ''): etree.SubElement(g, 'opf:reference', nsmap=self.NAMESPACES,
item.getparent().remove(item) attrib={'type':'cover', 'href':self.guide[-1].href()})
id = self.manifest.id_for_path(self.cover)
else: if id is None:
g = self.create_guide_element() for t in ('cover', 'other.ms-coverimage-standard', 'other.ms-coverimage'):
self.guide = Guide() for item in self.guide:
self.guide.set_cover(path) if item.type.lower() == t:
etree.SubElement(g, 'opf:reference', nsmap=self.NAMESPACES, self.create_manifest_item(item.href(), guess_type(path)[0])
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)
def get_metadata_element(self, name): def get_metadata_element(self, name):
matches = self.metadata_elem_path(self.metadata, name=name) matches = self.metadata_elem_path(self.metadata, name=name)

View File

@ -122,19 +122,16 @@ class TOC(list):
for i in obj.flat(): for i in obj.flat():
yield i yield i
@dynamic_property @property
def abspath(self): 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:
if self.href is None: return None
return None path = self.href.replace('/', os.sep)
path = self.href.replace('/', os.sep) if not os.path.isabs(path):
if not os.path.isabs(path): path = os.path.join(self.base_path, path)
path = os.path.join(self.base_path, path) return path
return path
return property(fget=fget, doc=doc)
def read_from_opf(self, opfreader): def read_from_opf(self, opfreader):
toc = opfreader.soup.find('spine', toc=True) toc = opfreader.soup.find('spine', toc=True)

View File

@ -131,14 +131,13 @@ class IndexEntry(object):
' parent_index=%r)')%(self.offset, self.depth, self.length, ' parent_index=%r)')%(self.offset, self.depth, self.length,
self.index, self.parent_index) self.index, self.parent_index)
@dynamic_property @property
def size(self): def size(self):
def fget(self): return self.length
return self.length
def fset(self, val): @size.setter
self.length = val def size(self, val):
return property(fget=fget, fset=fset, doc='Alias for length') self.length = val
@property @property
def next_offset(self): def next_offset(self):

View File

@ -704,20 +704,17 @@ class Metadata(object):
if attr != nsattr: if attr != nsattr:
attrib[nsattr] = attrib.pop(attr) attrib[nsattr] = attrib.pop(attr)
@dynamic_property @property
def name(self): def name(self):
def fget(self): return self.term
return self.term
return property(fget=fget)
@dynamic_property @property
def content(self): def content(self):
def fget(self): return self.value
return self.value
def fset(self, value): @content.setter
self.value = value def content(self, value):
return property(fget=fget, fset=fset) self.value = value
scheme = Attribute(lambda term: 'scheme' if scheme = Attribute(lambda term: 'scheme' if
term == OPF('meta') else OPF('scheme'), term == OPF('meta') else OPF('scheme'),
@ -830,33 +827,27 @@ class Metadata(object):
def __getattr__(self, term): def __getattr__(self, term):
return self.items[term] return self.items[term]
@dynamic_property @property
def _nsmap(self): def _nsmap(self):
def fget(self): nsmap = {}
nsmap = {} for term in self.items:
for term in self.items: for item in self.items[term]:
for item in self.items[term]: nsmap.update(item.nsmap)
nsmap.update(item.nsmap) return nsmap
return nsmap
return property(fget=fget)
@dynamic_property @property
def _opf1_nsmap(self): def _opf1_nsmap(self):
def fget(self): nsmap = self._nsmap
nsmap = self._nsmap for key, value in nsmap.items():
for key, value in nsmap.items(): if value in OPF_NSES or value in DC_NSES:
if value in OPF_NSES or value in DC_NSES: del nsmap[key]
del nsmap[key] return nsmap
return nsmap
return property(fget=fget)
@dynamic_property @property
def _opf2_nsmap(self): def _opf2_nsmap(self):
def fget(self): nsmap = self._nsmap
nsmap = self._nsmap nsmap.update(OPF2_NSMAP)
nsmap.update(OPF2_NSMAP) return nsmap
return nsmap
return property(fget=fget)
def to_opf1(self, parent=None): def to_opf1(self, parent=None):
nsmap = self._opf1_nsmap nsmap = self._opf1_nsmap
@ -1011,9 +1002,9 @@ class Manifest(object):
# }}} # }}}
@dynamic_property @property
def data(self): def data(self):
doc = """Provides MIME type sensitive access to the manifest """Provides MIME type sensitive access to the manifest
entry's associated content. entry's associated content.
- XHTML, HTML, and variant content is parsed as necessary to - 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 - All other content is returned as a :class:`str` object with no
special parsing. 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.setter
data = self._data def data(self, value):
if data is None: self._data = value
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 fset(self, value): @data.deleter
self._data = value def data(self):
self._data = None
def fdel(self):
self._data = None
return property(fget, fset, fdel, doc=doc)
def unload_data_from_memory(self, memory=None): def unload_data_from_memory(self, memory=None):
if isinstance(self._data, bytes): if isinstance(self._data, bytes):
@ -1266,20 +1256,19 @@ class Manifest(object):
element(elem, OPF('item'), attrib=attrib) element(elem, OPF('item'), attrib=attrib)
return elem return elem
@dynamic_property @property
def main_stylesheet(self): def main_stylesheet(self):
def fget(self): ans = getattr(self, '_main_stylesheet', None)
ans = getattr(self, '_main_stylesheet', None) if ans is None:
if ans is None: for item in self:
for item in self: if item.media_type.lower() in OEB_STYLES:
if item.media_type.lower() in OEB_STYLES: ans = item
ans = item break
break return ans
return ans
def fset(self, item): @main_stylesheet.setter
self._main_stylesheet = item def main_stylesheet(self, item):
return property(fget=fget, fset=fset) self._main_stylesheet = item
class Spine(object): class Spine(object):
@ -1422,15 +1411,12 @@ class Guide(object):
return 'Reference(type=%r, title=%r, href=%r)' \ return 'Reference(type=%r, title=%r, href=%r)' \
% (self.type, self.title, self.href) % (self.type, self.title, self.href)
@dynamic_property @property
def item(self): def item(self):
doc = """The manifest item associated with this reference.""" """The manifest item associated with this reference."""
path = urldefrag(self.href)[0]
def fget(self): hrefs = self.oeb.manifest.hrefs
path = urldefrag(self.href)[0] return hrefs.get(path, None)
hrefs = self.oeb.manifest.hrefs
return hrefs.get(path, None)
return property(fget=fget, doc=doc)
def __init__(self, oeb): def __init__(self, oeb):
self.oeb = oeb self.oeb = oeb

View File

@ -1369,14 +1369,13 @@ class EpubContainer(Container):
with self.open(name, 'wb') as f: with self.open(name, 'wb') as f:
f.write(data) f.write(data)
@dynamic_property @property
def path_to_ebook(self): def path_to_ebook(self):
def fget(self): return self.pathtoepub
return self.pathtoepub
def fset(self, val): @path_to_ebook.setter
self.pathtoepub = val def path_to_ebook(self, val):
return property(fget=fget, fset=fset) self.pathtoepub = val
# }}} # }}}
@ -1496,14 +1495,13 @@ class AZW3Container(Container):
outpath = self.pathtoazw3 outpath = self.pathtoazw3
opf_to_azw3(self.name_path_map[self.opf_name], outpath, self) opf_to_azw3(self.name_path_map[self.opf_name], outpath, self)
@dynamic_property @property
def path_to_ebook(self): def path_to_ebook(self):
def fget(self): return self.pathtoazw3
return self.pathtoazw3
def fset(self, val): @path_to_ebook.setter
self.pathtoazw3 = val def path_to_ebook(self, val):
return property(fget=fget, fset=fset) self.pathtoazw3 = val
@property @property
def names_that_must_not_be_changed(self): def names_that_must_not_be_changed(self):

View File

@ -1022,19 +1022,19 @@ class Application(QApplication):
else: else:
return QApplication.event(self, e) return QApplication.event(self, e)
@dynamic_property @property
def current_custom_colors(self): 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()))] (QColorDialog.customColor(i) for i in range(QColorDialog.customCount()))]
def fset(self, colors): @current_custom_colors.setter
num = min(len(colors), QColorDialog.customCount()) def current_custom_colors(self, colors):
for i in range(num): from PyQt5.Qt import QColorDialog, QColor
QColorDialog.setCustomColor(i, QColor(*colors[i])) num = min(len(colors), QColorDialog.customCount())
return property(fget=fget, fset=fset) for i in range(num):
QColorDialog.setCustomColor(i, QColor(*colors[i]))
def read_custom_colors(self): def read_custom_colors(self):
colors = self.color_prefs.get('custom_colors_for_color_dialog', None) colors = self.color_prefs.get('custom_colors_for_color_dialog', None)

View File

@ -51,18 +51,17 @@ class ChooseFormat(QDialog): # {{{
b.setChecked(True) b.setChecked(True)
self.accept() self.accept()
@dynamic_property @property
def formats(self): def formats(self):
def fget(self): for b in self.buttons:
for b in self.buttons: if b.isChecked():
if b.isChecked(): yield unicode_type(b.text())[1:]
yield unicode_type(b.text())[1:]
def fset(self, formats): @formats.setter
formats = {x.upper() for x in formats} def formats(self, formats):
for b in self.buttons: formats = {x.upper() for x in formats}
b.setChecked(b.text()[1:] in formats) for b in self.buttons:
return property(fget=fget, fset=fset) b.setChecked(b.text()[1:] in formats)
# }}} # }}}

View File

@ -336,55 +336,53 @@ class EditorWidget(QWebView, LineEditECM): # {{{
def remove_format_cleanup(self): def remove_format_cleanup(self):
self.html = self.html self.html = self.html
@dynamic_property @property
def html(self): 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 '<img' not in raw.lower():
return ans
def fget(self):
ans = u''
try: try:
if not self.page().mainFrame().documentElement().findFirst('meta[name="calibre-dont-sanitize"]').isNull(): root = html.fromstring(raw)
# Bypass cleanup if special meta tag exists except Exception:
return unicode_type(self.page().mainFrame().toHtml()) root = parse(raw, maybe_xhtml=False, sanitize_names=True)
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 '<img' not in raw.lower():
return ans
try: elems = []
root = html.fromstring(raw) for body in root.xpath('//body'):
except Exception: if body.text:
root = parse(raw, maybe_xhtml=False, sanitize_names=True) elems.append(body.text)
elems += [html.tostring(x, encoding=unicode_type) for x in body if
x.tag not in ('script', 'style')]
elems = [] if len(elems) > 1:
for body in root.xpath('//body'): ans = u'<div>%s</div>'%(u''.join(elems))
if body.text:
elems.append(body.text)
elems += [html.tostring(x, encoding=unicode_type) for x in body if
x.tag not in ('script', 'style')]
if len(elems) > 1:
ans = u'<div>%s</div>'%(u''.join(elems))
else:
ans = u''.join(elems)
if not ans.startswith('<'):
ans = '<p>%s</p>'%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)
else: else:
self.setHtml(val, self.base_url) ans = u''.join(elems)
self.set_font_style() if not ans.startswith('<'):
return property(fget=fget, fset=fset) ans = '<p>%s</p>'%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): def set_base_url(self, qurl):
self.base_url = qurl self.base_url = qurl
@ -763,15 +761,14 @@ class Editor(QWidget): # {{{
def set_minimum_height_for_editor(self, val): def set_minimum_height_for_editor(self, val):
self.editor.setMinimumHeight(val) self.editor.setMinimumHeight(val)
@dynamic_property @property
def html(self): def html(self):
def fset(self, v): self.tabs.setCurrentIndex(0)
self.editor.html = v return self.editor.html
def fget(self): @html.setter
self.tabs.setCurrentIndex(0) def html(self, v):
return self.editor.html self.editor.html = v
return property(fget=fget, fset=fset)
def change_tab(self, index): def change_tab(self, index):
# print 'reloading:', (index and self.wyswyg_dirty) or (not index and # 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.editor.html = unicode_type(self.code_edit.toPlainText())
self.source_dirty = False self.source_dirty = False
@dynamic_property @property
def tab(self): 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): @tab.setter
self.tabs.setCurrentWidget(self.code_edit if val == 'code' else self.wyswyg) def tab(self, val):
return property(fget=fget, fset=fset) self.tabs.setCurrentWidget(self.code_edit if val == 'code' else self.wyswyg)
def wyswyg_dirtied(self, *args): def wyswyg_dirtied(self, *args):
self.wyswyg_dirty = True self.wyswyg_dirty = True
@ -816,14 +812,13 @@ class Editor(QWidget): # {{{
if self.toolbar_prefs_name is not None: if self.toolbar_prefs_name is not None:
gprefs.set(self.toolbar_prefs_name, visible) gprefs.set(self.toolbar_prefs_name, visible)
@dynamic_property @property
def toolbars_visible(self): 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): @toolbars_visible.setter
getattr(self, ('show' if val else 'hide') + '_toolbars')() def toolbars_visible(self, val):
return property(fget=fget, fset=fset) getattr(self, ('show' if val else 'hide') + '_toolbars')()
def set_readonly(self, what): def set_readonly(self, what):
self.editor.set_readonly(what) self.editor.set_readonly(what)

View File

@ -333,23 +333,21 @@ class LineEdit(QLineEdit, LineEditECM):
def set_add_separator(self, what): def set_add_separator(self, what):
self.add_separator = bool(what) self.add_separator = bool(what)
@dynamic_property @property
def all_items(self): def all_items(self):
def fget(self): return self.mcompleter.model().all_items
return self.mcompleter.model().all_items
def fset(self, items): @all_items.setter
self.mcompleter.model().set_items(items) def all_items(self, items):
return property(fget=fget, fset=fset) self.mcompleter.model().set_items(items)
@dynamic_property @property
def disable_popup(self): def disable_popup(self):
def fget(self): return self.mcompleter.disable_popup
return self.mcompleter.disable_popup
def fset(self, val): @disable_popup.setter
self.mcompleter.disable_popup = bool(val) def disable_popup(self, val):
return property(fget=fget, fset=fset) self.mcompleter.disable_popup = bool(val)
# }}} # }}}
def event(self, ev): def event(self, ev):
@ -471,23 +469,21 @@ class EditWithComplete(EnComboBox):
self.setText(what) self.setText(what)
self.lineEdit().selectAll() self.lineEdit().selectAll()
@dynamic_property @property
def all_items(self): def all_items(self):
def fget(self): return self.lineEdit().all_items
return self.lineEdit().all_items
def fset(self, val): @all_items.setter
self.lineEdit().all_items = val def all_items(self, val):
return property(fget=fget, fset=fset) self.lineEdit().all_items = val
@dynamic_property @property
def disable_popup(self): def disable_popup(self):
def fget(self): return self.lineEdit().disable_popup
return self.lineEdit().disable_popup
def fset(self, val): @disable_popup.setter
self.lineEdit().disable_popup = bool(val) def disable_popup(self, val):
return property(fget=fget, fset=fset) self.lineEdit().disable_popup = bool(val)
# }}} # }}}
def text(self): def text(self):

View File

@ -43,14 +43,13 @@ class ColorButton(QToolButton):
self.setIcon(QIcon(self.pix)) self.setIcon(QIcon(self.pix))
self.clicked.connect(self.choose_color) self.clicked.connect(self.choose_color)
@dynamic_property @property
def color(self): def color(self):
def fget(self): return self._color.name(QColor.HexRgb)[1:]
return self._color.name(QColor.HexRgb)[1:]
def fset(self, val): @color.setter
self._color = QColor('#' + val) def color(self, val):
return property(fget=fget, fset=fset) self._color = QColor('#' + val)
def update_display(self): def update_display(self):
self.pix.fill(self._color) self.pix.fill(self._color)

View File

@ -377,14 +377,13 @@ class Comments(Base):
val = None val = None
return val return val
@dynamic_property @property
def tab(self): def tab(self):
def fget(self): return self._tb.tab
return self._tb.tab
def fset(self, val): @tab.setter
self._tb.tab = val def tab(self, val):
return property(fget=fget, fset=fset) self._tb.tab = val
def connect_data_changed(self, slot): def connect_data_changed(self, slot):
self._tb.data_changed.connect(slot) self._tb.data_changed.connect(slot)

View File

@ -1367,29 +1367,26 @@ class DeviceMixin(object): # {{{
memory=[files, remove]) memory=[files, remove])
self.status_bar.show_message(_('Sending catalogs to device.'), 5000) self.status_bar.show_message(_('Sending catalogs to device.'), 5000)
@dynamic_property @property
def news_to_be_synced(self): 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): @news_to_be_synced.setter
ans = [] def news_to_be_synced(self, ids):
try: try:
ans = self.library_view.model().db.prefs.get('news_to_be_synced', self.library_view.model().db.new_api.set_pref('news_to_be_synced',
[]) list(ids))
except: except:
import traceback import traceback
traceback.print_exc() 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)
def sync_news(self, send_ids=None, do_auto_convert=True): def sync_news(self, send_ids=None, do_auto_convert=True):
if self.device_connected: if self.device_connected:

View File

@ -378,32 +378,31 @@ class BasicRecipe(QWidget): # {{{
return False return False
return True return True
@dynamic_property @property
def recipe_source(self): def recipe_source(self):
def fget(self): title = self.title.text().strip()
title = self.title.text().strip() feeds = [self.feeds.item(i).data(Qt.UserRole) for i in range(self.feeds.count())]
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)
return options_to_recipe_source(title, self.oldest_article.value(), self.max_articles.value(), feeds)
def fset(self, src): @recipe_source.setter
self.feeds.clear() def recipe_source(self, src):
self.feed_title.clear() self.feeds.clear()
self.feed_url.clear() self.feed_title.clear()
if src is None: self.feed_url.clear()
self.title.setText(_('My news source')) if src is None:
self.oldest_article.setValue(7) self.title.setText(_('My news source'))
self.max_articles.setValue(100) self.oldest_article.setValue(7)
else: self.max_articles.setValue(100)
recipe = compile_recipe(src) else:
self.title.setText(recipe.title) recipe = compile_recipe(src)
self.oldest_article.setValue(recipe.oldest_article) self.title.setText(recipe.title)
self.max_articles.setValue(recipe.max_articles_per_feed) self.oldest_article.setValue(recipe.oldest_article)
for x in (recipe.feeds or ()): self.max_articles.setValue(recipe.max_articles_per_feed)
title, url = ('', x) if len(x) == 1 else x for x in (recipe.feeds or ()):
QListWidgetItem('%s - %s' % (title, url), self.feeds).setData(Qt.UserRole, (title, url)) 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 False
return True return True
@dynamic_property @property
def recipe_source(self): def recipe_source(self):
return self.editor.toPlainText()
def fget(self): @recipe_source.setter
return self.editor.toPlainText() def recipe_source(self, src):
self.editor.load_text(src, syntax='python', doc_name='<recipe>')
def fset(self, src):
self.editor.load_text(src, syntax='python', doc_name='<recipe>')
return property(fget=fget, fset=fset)
def sizeHint(self): def sizeHint(self):
return QSize(800, 500) return QSize(800, 500)

View File

@ -64,14 +64,13 @@ class ProgressDialog(QDialog):
def set_value(self, val): def set_value(self, val):
self.value = val self.value = val
@dynamic_property @property
def value(self): def value(self):
def fset(self, val): return self.bar.value()
return self.bar.setValue(val)
def fget(self): @value.setter
return self.bar.value() def value(self, val):
return property(fget=fget, fset=fset) self.bar.setValue(val)
def set_min(self, min): def set_min(self, min):
self.min = min self.min = min
@ -79,42 +78,38 @@ class ProgressDialog(QDialog):
def set_max(self, max): def set_max(self, max):
self.max = max self.max = max
@dynamic_property @property
def max(self): def max(self):
def fget(self): return self.bar.maximum()
return self.bar.maximum()
def fset(self, val): @max.setter
self.bar.setMaximum(val) def max(self, val):
return property(fget=fget, fset=fset) self.bar.setMaximum(val)
@dynamic_property @property
def min(self): def min(self):
def fget(self): return self.bar.minimum()
return self.bar.minimum()
def fset(self, val): @min.setter
self.bar.setMinimum(val) def min(self, val):
return property(fget=fget, fset=fset) self.bar.setMinimum(val)
@dynamic_property @property
def title(self): def title(self):
def fget(self): return self.title_label.text()
return self.title_label.text()
def fset(self, val): @title.setter
self.title_label.setText(unicode_type(val or '')) def title(self, val):
return property(fget=fget, fset=fset) self.title_label.setText(unicode_type(val or ''))
@dynamic_property @property
def msg(self): def msg(self):
def fget(self): return self.message.text()
return self.message.text()
def fset(self, val): @msg.setter
val = unicode_type(val or '') def msg(self, val):
self.message.setText(elided_text(val, self.font(), self.message.minimumWidth()-10)) val = unicode_type(val or '')
return property(fget=fget, fset=fset) self.message.setText(elided_text(val, self.font(), self.message.minimumWidth()-10))
def _canceled(self, *args): def _canceled(self, *args):
self.canceled = True self.canceled = True

View File

@ -353,18 +353,17 @@ class FontFamilyChooser(QWidget):
def clear_family(self): def clear_family(self):
self.font_family = None self.font_family = None
@dynamic_property @property
def font_family(self): def font_family(self):
def fget(self): return self._current_family
return self._current_family
def fset(self, val): @font_family.setter
if not val: def font_family(self, val):
val = None if not val:
self._current_family = val val = None
self.button.setText(val or self.default_text) self._current_family = val
self.family_changed.emit(val) self.button.setText(val or self.default_text)
return property(fget=fget, fset=fset) self.family_changed.emit(val)
def show_chooser(self): def show_chooser(self):
d = FontFamilyDialog(self.font_family, self) d = FontFamilyDialog(self.font_family, self)

View File

@ -60,23 +60,20 @@ class LanguagesEdit(EditWithComplete):
parts = [x.strip() for x in raw.split(',')] parts = [x.strip() for x in raw.split(',')]
return [self.comma_rmap.get(x, x) for x in parts] return [self.comma_rmap.get(x, x) for x in parts]
@dynamic_property @property
def lang_codes(self): 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): @lang_codes.setter
vals = self.vals def lang_codes(self, lang_codes):
ans = [] self.set_lang_codes(lang_codes, allow_undo=False)
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)
def set_lang_codes(self, lang_codes, allow_undo=True): def set_lang_codes(self, lang_codes, allow_undo=True):
ans = [] ans = []

View File

@ -186,18 +186,17 @@ class PreserveViewState(object): # {{{
view.horizontalScrollBar().setValue(self.hscroll) view.horizontalScrollBar().setValue(self.hscroll)
self.init_vals() self.init_vals()
@dynamic_property @property
def state(self): def state(self):
def fget(self): self.__enter__()
self.__enter__() return {x:getattr(self, x) for x in ('selected_ids', 'current_id',
return {x:getattr(self, x) for x in ('selected_ids', 'current_id', 'vscroll', 'hscroll')}
'vscroll', 'hscroll')}
def fset(self, state): @state.setter
for k, v in iteritems(state): def state(self, state):
setattr(self, k, v) for k, v in iteritems(state):
self.__exit__() setattr(self, k, v)
return property(fget=fget, fset=fset) self.__exit__()
# }}} # }}}
@ -1161,24 +1160,23 @@ class BooksView(QTableView): # {{{
ans.append(i) ans.append(i)
return ans return ans
@dynamic_property @property
def current_id(self): def current_id(self):
def fget(self): try:
try: return self.model().id(self.currentIndex())
return self.model().id(self.currentIndex()) except:
except: pass
pass return None
return None
def fset(self, val): @current_id.setter
if val is None: def current_id(self, val):
return if val is None:
m = self.model() return
for row in range(m.rowCount(QModelIndex())): m = self.model()
if m.id(row) == val: for row in range(m.rowCount(QModelIndex())):
self.set_current_row(row, select=False) if m.id(row) == val:
break self.set_current_row(row, select=False)
return property(fget=fget, fset=fset) break
@property @property
def next_id(self): def next_id(self):

View File

@ -72,14 +72,13 @@ class BasicMetadataWidget(object):
def commit(self, db, id_): def commit(self, db, id_):
return True return True
@dynamic_property @property
def current_val(self): def current_val(self):
# Present in most but not all basic metadata widgets return None
def fget(self):
return None @current_val.setter
def fset(self, val): def current_val(self, val):
pass pass
return property(fget=fget, fset=fset)
''' '''
@ -225,24 +224,22 @@ class TitleEdit(EnLineEdit, ToMetadataMixin):
# to work even if some of the book files are opened in windows. # to work even if some of the book files are opened in windows.
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False) getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False)
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): title = clean_text(unicode_type(self.text()))
title = clean_text(unicode_type(self.text())) if not title:
if not title: title = self.get_default()
title = self.get_default() return title.strip()
return title.strip()
def fset(self, val): @current_val.setter
if hasattr(val, 'strip'): def current_val(self, val):
val = val.strip() if hasattr(val, 'strip'):
if not val: val = val.strip()
val = self.get_default() if not val:
self.set_text(val) val = self.get_default()
self.setCursorPosition(0) self.set_text(val)
self.setCursorPosition(0)
return property(fget=fget, fset=fset)
def break_cycles(self): def break_cycles(self):
self.dialog = None self.dialog = None
@ -416,22 +413,20 @@ class AuthorsEdit(EditWithComplete, ToMetadataMixin):
self.books_to_refresh |= db.set_authors(id_, authors, notify=False, self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
allow_case_change=True) allow_case_change=True)
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): au = clean_text(unicode_type(self.text()))
au = clean_text(unicode_type(self.text())) if not au:
if not au: au = self.get_default()
au = self.get_default() return string_to_authors(au)
return string_to_authors(au)
def fset(self, val): @current_val.setter
if not val: def current_val(self, val):
val = [self.get_default()] if not val:
self.set_edit_text(' & '.join([x.strip() for x in val])) val = [self.get_default()]
self.lineEdit().setCursorPosition(0) self.set_edit_text(' & '.join([x.strip() for x in val]))
self.lineEdit().setCursorPosition(0)
return property(fget=fget, fset=fset)
def break_cycles(self): def break_cycles(self):
self.db = self.dialog = None self.db = self.dialog = None
@ -485,19 +480,17 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
self.first_time = True self.first_time = True
self.update_state() self.update_state()
@dynamic_property @property
def current_val(self): 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): @current_val.setter
if not val: def current_val(self, val):
val = '' if not val:
self.set_text(val.strip()) val = ''
self.setCursorPosition(0) self.set_text(val.strip())
self.setCursorPosition(0)
return property(fget=fget, fset=fset)
def update_state_and_val(self): def update_state_and_val(self):
# Handle case change if the authors box changed # Handle case change if the authors box changed
@ -613,19 +606,17 @@ class SeriesEdit(EditWithComplete, ToMetadataMixin):
self.books_to_refresh = set([]) self.books_to_refresh = set([])
self.lineEdit().textChanged.connect(self.data_changed) self.lineEdit().textChanged.connect(self.data_changed)
@dynamic_property @property
def current_val(self): 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): @current_val.setter
if not val: def current_val(self, val):
val = '' if not val:
self.set_edit_text(val.strip()) val = ''
self.lineEdit().setCursorPosition(0) self.set_edit_text(val.strip())
self.lineEdit().setCursorPosition(0)
return property(fget=fget, fset=fset)
def initialize(self, db, id_): def initialize(self, db, id_):
self.books_to_refresh = set([]) self.books_to_refresh = set([])
@ -672,19 +663,17 @@ class SeriesIndexEdit(make_undoable(QDoubleSpinBox), ToMetadataMixin):
def enable(self, *args): def enable(self, *args):
self.setEnabled(bool(self.series_edit.current_val)) self.setEnabled(bool(self.series_edit.current_val))
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.value()
return self.value()
def fset(self, val): @current_val.setter
if val is None: def current_val(self, val):
val = 1.0 if val is None:
val = float(val) val = 1.0
self.set_spinbox_value(val) val = float(val)
self.set_spinbox_value(val)
return property(fget=fget, fset=fset)
def initialize(self, db, id_): def initialize(self, db, id_):
self.db = db self.db = db
@ -1255,31 +1244,29 @@ class Cover(ImageView): # {{{
def changed(self): def changed(self):
return self.current_val != self.original_val return self.current_val != self.original_val
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self._cdata
return self._cdata
def fset(self, cdata): @current_val.setter
self._cdata = None def current_val(self, cdata):
self.cdata_before_trim = None self._cdata = None
pm = QPixmap() self.cdata_before_trim = None
if cdata: pm = QPixmap()
pm.loadFromData(cdata) if cdata:
if pm.isNull(): pm.loadFromData(cdata)
pm = QPixmap(I('default_cover.png')) if pm.isNull():
else: pm = QPixmap(I('default_cover.png'))
self._cdata = cdata else:
pm.setDevicePixelRatio(getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) self._cdata = cdata
self.setPixmap(pm) pm.setDevicePixelRatio(getattr(self, 'devicePixelRatioF', self.devicePixelRatio)())
tt = _('This book has no cover') self.setPixmap(pm)
if self._cdata: tt = _('This book has no cover')
tt = _('Cover size: %(width)d x %(height)d pixels') % \ if self._cdata:
dict(width=pm.width(), height=pm.height()) tt = _('Cover size: %(width)d x %(height)d pixels') % \
self.setToolTip(tt) dict(width=pm.width(), height=pm.height())
self.data_changed.emit() self.setToolTip(tt)
self.data_changed.emit()
return property(fget=fget, fset=fset)
def commit(self, db, id_): def commit(self, db, id_):
if self.changed: if self.changed:
@ -1309,20 +1296,19 @@ class CommentsEdit(Editor, ToMetadataMixin): # {{{
FIELD_NAME = 'comments' FIELD_NAME = 'comments'
toolbar_prefs_name = 'metadata-comments-editor-widget-hidden-toolbars' toolbar_prefs_name = 'metadata-comments-editor-widget-hidden-toolbars'
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.html
return self.html
def fset(self, val): @current_val.setter
if not val or not val.strip(): def current_val(self, val):
val = '' if not val or not val.strip():
else: val = ''
val = comments_to_html(val) else:
self.set_html(val, self.allow_undo) val = comments_to_html(val)
self.wyswyg_dirtied() self.set_html(val, self.allow_undo)
self.data_changed.emit() self.wyswyg_dirtied()
return property(fget=fget, fset=fset) self.data_changed.emit()
def initialize(self, db, id_): def initialize(self, db, id_):
path = db.abspath(id_, index_is_id=True) path = db.abspath(id_, index_is_id=True)
@ -1350,14 +1336,13 @@ class RatingEdit(RatingEditor, ToMetadataMixin): # {{{
self.setWhatsThis(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP)
self.currentTextChanged.connect(self.data_changed) self.currentTextChanged.connect(self.data_changed)
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.rating_value
return self.rating_value
def fset(self, val): @current_val.setter
self.rating_value = val def current_val(self, val):
return property(fget=fget, fset=fset) self.rating_value = val
def initialize(self, db, id_): def initialize(self, db, id_):
val = db.rating(id_, index_is_id=True) val = db.rating(id_, index_is_id=True)
@ -1390,17 +1375,16 @@ class TagsEdit(EditWithComplete, ToMetadataMixin): # {{{
self.setToolTip(self.TOOLTIP) self.setToolTip(self.TOOLTIP)
self.setWhatsThis(self.TOOLTIP) self.setWhatsThis(self.TOOLTIP)
@dynamic_property @property
def current_val(self): 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): @current_val.setter
if not val: def current_val(self, val):
val = [] if not val:
self.set_edit_text(', '.join([x.strip() for x in val])) val = []
self.setCursorPosition(0) self.set_edit_text(', '.join([x.strip() for x in val]))
return property(fget=fget, fset=fset) self.setCursorPosition(0)
def initialize(self, db, id_): def initialize(self, db, id_):
self.books_to_refresh = set([]) self.books_to_refresh = set([])
@ -1454,14 +1438,13 @@ class LanguagesEdit(LE, ToMetadataMixin): # {{{
self.textChanged.connect(self.data_changed) self.textChanged.connect(self.data_changed)
self.setToolTip(self.TOOLTIP) self.setToolTip(self.TOOLTIP)
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.lang_codes
return self.lang_codes
def fset(self, val): @current_val.setter
self.set_lang_codes(val, self.allow_undo) def current_val(self, val):
return property(fget=fget, fset=fset) self.set_lang_codes(val, self.allow_undo)
def initialize(self, db, id_): def initialize(self, db, id_):
self.init_langs(db) self.init_langs(db)
@ -1562,44 +1545,43 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
if d.exec_() == d.Accepted: if d.exec_() == d.Accepted:
self.current_val = d.get_identifiers() self.current_val = d.get_identifiers()
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): raw = unicode_type(self.text()).strip()
raw = unicode_type(self.text()).strip() parts = [clean_text(x) for x in raw.split(',')]
parts = [clean_text(x) for x in raw.split(',')] ans = {}
ans = {} for x in parts:
for x in parts: c = x.split(':')
c = x.split(':') if len(c) > 1:
if len(c) > 1: itype = c[0].lower()
itype = c[0].lower() c = ':'.join(c[1:])
c = ':'.join(c[1:]) if itype == 'isbn':
if itype == 'isbn': v = check_isbn(c)
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)
if v is not None: if v is not None:
val[k] = v c = v
ids = sorted(iteritems(val), key=keygen) ans[itype] = c
txt = ', '.join(['%s:%s'%(k.lower(), vl) for k, vl in ids]) return ans
# Use selectAll + insert instead of setText so that undo works
self.selectAll(), self.insert(txt.strip()) @current_val.setter
self.setCursorPosition(0) def current_val(self, val):
return property(fget=fget, fset=fset) 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_): def initialize(self, db, id_):
self.original_val = db.get_identifiers(id_, index_is_id=True) 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.setToolTip(_('Clear publisher'))
self.clear_button.clicked.connect(self.clearEditText) self.clear_button.clicked.connect(self.clearEditText)
@dynamic_property @property
def current_val(self): 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): @current_val.setter
if not val: def current_val(self, val):
val = '' if not val:
self.set_edit_text(val.strip()) val = ''
self.lineEdit().setCursorPosition(0) self.set_edit_text(val.strip())
self.lineEdit().setCursorPosition(0)
return property(fget=fget, fset=fset)
def initialize(self, db, id_): def initialize(self, db, id_):
self.books_to_refresh = set([]) self.books_to_refresh = set([])
@ -1857,18 +1837,17 @@ class DateEdit(make_undoable(QDateTimeEdit), ToMetadataMixin):
def reset_date(self, *args): def reset_date(self, *args):
self.current_val = None self.current_val = None
@dynamic_property @property
def current_val(self): 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): @current_val.setter
if val is None or is_date_undefined(val): def current_val(self, val):
val = UNDEFINED_DATE if val is None or is_date_undefined(val):
else: val = UNDEFINED_DATE
val = as_local_time(val) else:
self.set_spinbox_value(val) val = as_local_time(val)
return property(fget=fget, fset=fset) self.set_spinbox_value(val)
def initialize(self, db, id_): def initialize(self, db, id_):
self.current_val = getattr(db, self.ATTR)(id_, index_is_id=True) self.current_val = getattr(db, self.ATTR)(id_, index_is_id=True)

View File

@ -49,29 +49,28 @@ class LineEdit(EditWithComplete):
self.set_separator(sep) self.set_separator(sep)
self.textChanged.connect(self.changed) self.textChanged.connect(self.changed)
@dynamic_property @property
def value(self): def value(self):
def fget(self): val = unicode_type(self.text()).strip()
val = unicode_type(self.text()).strip() ism = self.metadata['is_multiple']
ism = self.metadata['is_multiple'] if ism:
if ism: if not val:
if not val: val = []
val = [] else:
else: val = val.strip(ism['list_to_ui'].strip())
val = val.strip(ism['list_to_ui'].strip()) val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()]
val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()] return val
return val
def fset(self, val): @value.setter
ism = self.metadata['is_multiple'] def value(self, val):
if ism: ism = self.metadata['is_multiple']
if not val: if ism:
val = '' if not val:
else: val = ''
val = ism['list_to_ui'].join(val) else:
self.setText(val) val = ism['list_to_ui'].join(val)
self.setCursorPosition(0) self.setText(val)
return property(fget=fget, fset=fset) self.setCursorPosition(0)
def from_mi(self, mi): def from_mi(self, mi):
val = mi.get(self.field, default='') or '' val = mi.get(self.field, default='') or ''
@ -85,15 +84,14 @@ class LineEdit(EditWithComplete):
elif self.field == 'authors': elif self.field == 'authors':
mi.set('author_sort', authors_to_sort_string(val)) mi.set('author_sort', authors_to_sort_string(val))
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return unicode_type(self.text())
return unicode_type(self.text())
def fset(self, val): @current_val.setter
self.setText(val) def current_val(self, val):
self.setCursorPosition(0) self.setText(val)
return property(fget=fget, fset=fset) self.setCursorPosition(0)
@property @property
def is_blank(self): def is_blank(self):
@ -119,14 +117,13 @@ class LanguagesEdit(LE):
if not is_new: if not is_new:
self.lineEdit().setReadOnly(True) self.lineEdit().setReadOnly(True)
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.lang_codes
return self.lang_codes
def fset(self, val): @current_val.setter
self.lang_codes = val def current_val(self, val):
return property(fget=fget, fset=fset) self.lang_codes = val
def from_mi(self, mi): def from_mi(self, mi):
self.lang_codes = mi.languages self.lang_codes = mi.languages
@ -241,17 +238,16 @@ class IdentifiersEdit(LineEdit):
def to_mi(self, mi): def to_mi(self, mi):
mi.set_identifiers(self.as_dict) mi.set_identifiers(self.as_dict)
@dynamic_property @property
def as_dict(self): def as_dict(self):
def fget(self): parts = (x.strip() for x in self.current_val.split(',') if x.strip())
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}
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): @as_dict.setter
val = ('%s:%s' % (k, v) for k, v in iteritems(val)) def as_dict(self, val):
self.setText(', '.join(val)) val = ('%s:%s' % (k, v) for k, v in iteritems(val))
self.setCursorPosition(0) self.setText(', '.join(val))
return property(fget=fget, fset=fset) self.setCursorPosition(0)
class CommentsEdit(Editor): class CommentsEdit(Editor):
@ -269,15 +265,14 @@ class CommentsEdit(Editor):
self.hide_toolbars() self.hide_toolbars()
self.set_readonly(True) self.set_readonly(True)
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.html
return self.html
def fset(self, val): @current_val.setter
self.html = val or '' def current_val(self, val):
self.changed.emit() self.html = val or ''
return property(fget=fget, fset=fset) self.changed.emit()
def from_mi(self, mi): def from_mi(self, mi):
val = mi.get(self.field, default='') val = mi.get(self.field, default='')
@ -315,16 +310,15 @@ class CoverView(QWidget):
def is_blank(self): def is_blank(self):
return self.pixmap is None return self.pixmap is None
@dynamic_property @property
def current_val(self): def current_val(self):
def fget(self): return self.pixmap
return self.pixmap
def fset(self, val): @current_val.setter
self.pixmap = val def current_val(self, val):
self.changed.emit() self.pixmap = val
self.update() self.changed.emit()
return property(fget=fget, fset=fset) self.update()
def from_mi(self, mi): def from_mi(self, mi):
p = getattr(mi, 'cover', None) p = getattr(mi, 'cover', None)

View File

@ -138,35 +138,33 @@ class ConditionEditor(QWidget): # {{{
b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
b.setMinimumContentsLength(20) b.setMinimumContentsLength(20)
@dynamic_property @property
def current_col(self): def current_col(self):
def fget(self): idx = self.column_box.currentIndex()
idx = self.column_box.currentIndex() return unicode_type(self.column_box.itemData(idx) or '')
return unicode_type(self.column_box.itemData(idx) or '')
def fset(self, val): @current_col.setter
for idx in range(self.column_box.count()): def current_col(self, val):
c = unicode_type(self.column_box.itemData(idx) or '') for idx in range(self.column_box.count()):
if c == val: c = unicode_type(self.column_box.itemData(idx) or '')
self.column_box.setCurrentIndex(idx) if c == val:
return self.column_box.setCurrentIndex(idx)
raise ValueError('Column %r not found'%val) return
return property(fget=fget, fset=fset) raise ValueError('Column %r not found'%val)
@dynamic_property @property
def current_action(self): def current_action(self):
def fget(self): idx = self.action_box.currentIndex()
idx = self.action_box.currentIndex() return unicode_type(self.action_box.itemData(idx) or '')
return unicode_type(self.action_box.itemData(idx) or '')
def fset(self, val): @current_action.setter
for idx in range(self.action_box.count()): def current_action(self, val):
c = unicode_type(self.action_box.itemData(idx) or '') for idx in range(self.action_box.count()):
if c == val: c = unicode_type(self.action_box.itemData(idx) or '')
self.action_box.setCurrentIndex(idx) if c == val:
return self.action_box.setCurrentIndex(idx)
raise ValueError('Action %r not valid for current column'%val) return
return property(fget=fget, fset=fset) raise ValueError('Action %r not valid for current column'%val)
@property @property
def current_val(self): def current_val(self):
@ -176,26 +174,24 @@ class ConditionEditor(QWidget): # {{{
ans = rmap.get(lower(ans), ans) ans = rmap.get(lower(ans), ans)
return ans return ans
@dynamic_property @property
def condition(self): def condition(self):
def fget(self): c, a, v = (self.current_col, self.current_action,
c, a, v = (self.current_col, self.current_action, self.current_val)
self.current_val) if not c or not a:
if not c or not a: return None
return None return (c, a, v)
return (c, a, v)
def fset(self, condition): @condition.setter
c, a, v = condition def condition(self, condition):
if not v: c, a, v = condition
v = '' if not v:
v = v.strip() v = ''
self.current_col = c v = v.strip()
self.current_action = a self.current_col = c
self.value_box.setText(v) self.current_action = a
self.value_box.setText(v)
return property(fget=fget, fset=fset)
def init_action_box(self): def init_action_box(self):
self.action_box.blockSignals(True) self.action_box.blockSignals(True)

View File

@ -62,23 +62,21 @@ class ResizeDialog(QDialog): # {{{
other.setValue(oval) other.setValue(oval)
other.blockSignals(False) other.blockSignals(False)
@dynamic_property @property
def width(self): def width(self):
def fget(self): return self._width.value()
return self._width.value()
def fset(self, val): @width.setter
self._width.setValue(val) def width(self, val):
return property(fget=fget, fset=fset) self._width.setValue(val)
@dynamic_property @property
def height(self): def height(self):
def fget(self): return self._height.value()
return self._height.value()
def fset(self, val): @height.setter
self._height.setValue(val) def height(self, val):
return property(fget=fget, fset=fset) 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.undo_redo_state_changed.connect(self.undo_redo_state_changed)
self.canvas.selection_state_changed.connect(self.update_clipboard_actions) self.canvas.selection_state_changed.connect(self.update_clipboard_actions)
@dynamic_property @property
def is_modified(self): def is_modified(self):
def fget(self): return self._is_modified
return self._is_modified
def fset(self, val): @is_modified.setter
self._is_modified = val def is_modified(self, val):
self.modification_state_changed.emit(val) self._is_modified = val
return property(fget=fget, fset=fset) self.modification_state_changed.emit(val)
@dynamic_property @property
def current_editing_state(self): def current_editing_state(self):
def fget(self): return {}
return {}
def fset(self, val): @current_editing_state.setter
pass def current_editing_state(self, val):
return property(fget=fget, fset=fset) pass
@property @property
def undo_available(self): def undo_available(self):
@ -138,14 +134,13 @@ class Editor(QMainWindow):
def redo_available(self): def redo_available(self):
return self.canvas.redo_action.isEnabled() return self.canvas.redo_action.isEnabled()
@dynamic_property @property
def current_line(self): def current_line(self):
def fget(self): return 0
return 0
def fset(self, val): @current_line.setter
pass def current_line(self, val):
return property(fget=fget, fset=fset) pass
@property @property
def number_of_lines(self): def number_of_lines(self):
@ -160,15 +155,14 @@ class Editor(QMainWindow):
def get_raw_data(self): def get_raw_data(self):
return self.canvas.get_image_data(quality=self.quality) return self.canvas.get_image_data(quality=self.quality)
@dynamic_property @property
def data(self): def data(self):
def fget(self): return self.get_raw_data()
return self.get_raw_data()
def fset(self, val): @data.setter
self.canvas.load_image(val) def data(self, val):
self._is_modified = False # The image_changed signal will have been triggered causing this editor to be incorrectly marked as modified self.canvas.load_image(val)
return property(fget=fget, fset=fset) 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): def replace_data(self, raw, only_if_different=True):
# We ignore only_if_different as it is useless in our case, and # We ignore only_if_different as it is useless in our case, and

View File

@ -221,26 +221,25 @@ class EditorTabStop(object):
with m: with m:
m.text = text m.text = text
@dynamic_property @property
def text(self): def text(self):
def fget(self): editor = self.editor()
editor = self.editor() if editor is None or self.is_deleted:
if editor is None or self.is_deleted: return ''
return '' c = editor.textCursor()
c = editor.textCursor() c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor)
c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor) return editor.selected_text_from_cursor(c)
return editor.selected_text_from_cursor(c)
def fset(self, text): @text.setter
editor = self.editor() def text(self, text):
if editor is None or self.is_deleted: editor = self.editor()
return if editor is None or self.is_deleted:
c = editor.textCursor() return
c.joinPreviousEditBlock() if self.join_previous_edit else c.beginEditBlock() c = editor.textCursor()
c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor) c.joinPreviousEditBlock() if self.join_previous_edit else c.beginEditBlock()
c.insertText(text) c.setPosition(self.left), c.setPosition(self.right, c.KeepAnchor)
c.endEditBlock() c.insertText(text)
return property(fget=fget, fset=fset) c.endEditBlock()
def set_editor_cursor(self, editor): def set_editor_cursor(self, editor):
if not self.is_deleted: if not self.is_deleted:
@ -537,20 +536,18 @@ class EditSnippet(QWidget):
self.types.item(0).setCheckState(Qt.Checked) self.types.item(0).setCheckState(Qt.Checked)
(self.name if self.creating_snippet else self.template).setFocus(Qt.OtherFocusReason) (self.name if self.creating_snippet else self.template).setFocus(Qt.OtherFocusReason)
@dynamic_property @property
def snip(self): def snip(self):
def fset(self, snip): ftypes = []
self.apply_snip(snip) 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): @snip.setter
ftypes = [] def snip(self, snip):
for i in range(self.types.count()): self.apply_snip(snip)
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)
def validate(self): def validate(self):
snip = self.snip snip = self.snip

View File

@ -206,17 +206,15 @@ class TextEdit(PlainTextEdit):
insert_text(md.html()) insert_text(md.html())
return return
@dynamic_property @property
def is_modified(self): def is_modified(self):
''' True if the document has been modified since it was loaded or since ''' True if the document has been modified since it was loaded or since
the last time is_modified was set to False. ''' the last time is_modified was set to False. '''
return self.document().isModified()
def fget(self): @is_modified.setter
return self.document().isModified() def is_modified(self, val):
self.document().setModified(bool(val))
def fset(self, val):
self.document().setModified(bool(val))
return property(fget=fget, fset=fset)
def sizeHint(self): def sizeHint(self):
return self.size_hint return self.size_hint

View File

@ -159,28 +159,26 @@ class Editor(QMainWindow):
self.editor.link_clicked.connect(self.link_clicked) self.editor.link_clicked.connect(self.link_clicked)
self.editor.smart_highlighting_updated.connect(self.smart_highlighting_updated) self.editor.smart_highlighting_updated.connect(self.smart_highlighting_updated)
@dynamic_property @property
def current_line(self): def current_line(self):
def fget(self): return self.editor.textCursor().blockNumber()
return self.editor.textCursor().blockNumber()
def fset(self, val): @current_line.setter
self.editor.go_to_line(val) def current_line(self, val):
return property(fget=fget, fset=fset) self.editor.go_to_line(val)
@dynamic_property @property
def current_editing_state(self): def current_editing_state(self):
def fget(self): c = self.editor.textCursor()
c = self.editor.textCursor() return {'cursor':(c.anchor(), c.position())}
return {'cursor':(c.anchor(), c.position())}
def fset(self, val): @current_editing_state.setter
anchor, position = val.get('cursor', (None, None)) def current_editing_state(self, val):
if anchor is not None and position is not None: anchor, position = val.get('cursor', (None, None))
c = self.editor.textCursor() if anchor is not None and position is not None:
c.setPosition(anchor), c.setPosition(position, c.KeepAnchor) c = self.editor.textCursor()
self.editor.setTextCursor(c) c.setPosition(anchor), c.setPosition(position, c.KeepAnchor)
return property(fget=fget, fset=fset) self.editor.setTextCursor(c)
def current_tag(self, for_position_sync=True): def current_tag(self, for_position_sync=True):
return self.editor.current_tag(for_position_sync=for_position_sync) return self.editor.current_tag(for_position_sync=for_position_sync)
@ -189,18 +187,17 @@ class Editor(QMainWindow):
def number_of_lines(self): def number_of_lines(self):
return self.editor.blockCount() return self.editor.blockCount()
@dynamic_property @property
def data(self): def data(self):
def fget(self): ans = self.get_raw_data()
ans = self.get_raw_data() ans, changed = replace_encoding_declarations(ans, enc='utf-8', limit=4*1024)
ans, changed = replace_encoding_declarations(ans, enc='utf-8', limit=4*1024) if changed:
if changed: self.data = ans
self.data = ans return ans.encode('utf-8')
return ans.encode('utf-8')
def fset(self, val): @data.setter
self.editor.load_text(val, syntax=self.syntax, doc_name=editor_name(self)) def data(self, val):
return property(fget=fget, fset=fset) self.editor.load_text(val, syntax=self.syntax, doc_name=editor_name(self))
def init_from_template(self, template): def init_from_template(self, template):
self.editor.load_text(template, syntax=self.syntax, process_template=True, doc_name=editor_name(self)) 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): def has_marked_text(self):
return self.editor.current_search_mark is not None return self.editor.current_search_mark is not None
@dynamic_property @property
def is_modified(self): def is_modified(self):
def fget(self): return self.editor.is_modified
return self.editor.is_modified
def fset(self, val): @is_modified.setter
self.editor.is_modified = val def is_modified(self, val):
return property(fget=fget, fset=fset) self.editor.is_modified = val
def create_toolbars(self): def create_toolbars(self):
self.action_bar = b = self.addToolBar(_('Edit actions tool bar')) self.action_bar = b = self.addToolBar(_('Edit actions tool bar'))

View File

@ -370,17 +370,16 @@ class WebView(QWebView):
def refresh(self): def refresh(self):
self.pageAction(self.page().Reload).trigger() self.pageAction(self.page().Reload).trigger()
@dynamic_property @property
def scroll_pos(self): def scroll_pos(self):
def fget(self): mf = self.page().mainFrame()
mf = self.page().mainFrame() return (mf.scrollBarValue(Qt.Horizontal), mf.scrollBarValue(Qt.Vertical))
return (mf.scrollBarValue(Qt.Horizontal), mf.scrollBarValue(Qt.Vertical))
def fset(self, val): @scroll_pos.setter
mf = self.page().mainFrame() def scroll_pos(self, val):
mf.setScrollBarValue(Qt.Horizontal, val[0]) mf = self.page().mainFrame()
mf.setScrollBarValue(Qt.Vertical, val[1]) mf.setScrollBarValue(Qt.Horizontal, val[0])
return property(fget=fget, fset=fset) mf.setScrollBarValue(Qt.Vertical, val[1])
def clear(self): def clear(self):
self.setHtml(_( self.setHtml(_(

View File

@ -1061,14 +1061,13 @@ class CSSWidget(QWidget):
self.summary = la = QLabel('\xa0') self.summary = la = QLabel('\xa0')
h.addWidget(la) h.addWidget(la)
@dynamic_property @property
def sort_order(self): 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): @sort_order.setter
self._sort_order.setCurrentIndex({Qt.AscendingOrder:0}.get(val, 1)) def sort_order(self, val):
return property(fget=fget, fset=fset) self._sort_order.setCurrentIndex({Qt.AscendingOrder:0}.get(val, 1))
def update_summary(self): def update_summary(self):
self.summary.setText(_('{0} rules, {1} unused').format(self.model.rowCount(), self.model.num_unused)) self.summary.setText(_('{0} rules, {1} unused').format(self.model.rowCount(), self.model.num_unused))

View File

@ -152,16 +152,15 @@ class WhereBox(QComboBox):
f.setBold(True), f.setItalic(True) f.setBold(True), f.setItalic(True)
self.setFont(f) self.setFont(f)
@dynamic_property @property
def where(self): def where(self):
wm = {0:'current', 1:'text', 2:'styles', 3:'selected', 4:'open', 5:'selected-text'} wm = {0:'current', 1:'text', 2:'styles', 3:'selected', 4:'open', 5:'selected-text'}
return wm[self.currentIndex()]
def fget(self): @where.setter
return wm[self.currentIndex()] def where(self, val):
wm = {0:'current', 1:'text', 2:'styles', 3:'selected', 4:'open', 5:'selected-text'}
def fset(self, val): self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val])
self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val])
return property(fget=fget, fset=fset)
def showPopup(self): def showPopup(self):
# We do it like this so that the popup uses a normal font # We do it like this so that the popup uses a normal font
@ -190,14 +189,13 @@ class DirectionBox(QComboBox):
<dd>Search for the previous match from your current position</dd> <dd>Search for the previous match from your current position</dd>
</dl>''')) </dl>'''))
@dynamic_property @property
def direction(self): 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): @direction.setter
self.setCurrentIndex(1 if val == 'up' else 0) def direction(self, val):
return property(fget=fget, fset=fset) self.setCurrentIndex(1 if val == 'up' else 0)
class ModeBox(QComboBox): class ModeBox(QComboBox):
@ -216,14 +214,13 @@ class ModeBox(QComboBox):
<dd>The search expression is interpreted as a regular expression. The replace expression is an arbitrarily powerful Python function.</dd> <dd>The search expression is interpreted as a regular expression. The replace expression is an arbitrarily powerful Python function.</dd>
</dl>''')) </dl>'''))
@dynamic_property @property
def mode(self): def mode(self):
def fget(self): return ('normal', 'regex', 'function')[self.currentIndex()]
return ('normal', 'regex', 'function')[self.currentIndex()]
def fset(self, val): @mode.setter
self.setCurrentIndex({'regex':1, 'function':2}.get(val, 0)) def mode(self, val):
return property(fget=fget, fset=fset) self.setCurrentIndex({'regex':1, 'function':2}.get(val, 0))
class SearchWidget(QWidget): class SearchWidget(QWidget):
@ -353,91 +350,82 @@ class SearchWidget(QWidget):
self.replace_text.setVisible(not function_mode) self.replace_text.setVisible(not function_mode)
self.functions_container.setVisible(function_mode) self.functions_container.setVisible(function_mode)
@dynamic_property @property
def mode(self): def mode(self):
def fget(self): return self.mode_box.mode
return self.mode_box.mode
def fset(self, val): @mode.setter
self.mode_box.mode = val def mode(self, val):
self.da.setVisible(self.mode in ('regex', 'function')) self.mode_box.mode = val
return property(fget=fget, fset=fset) self.da.setVisible(self.mode in ('regex', 'function'))
@dynamic_property @property
def find(self): def find(self):
def fget(self): return unicode_type(self.find_text.text())
return unicode_type(self.find_text.text())
def fset(self, val): @find.setter
self.find_text.setText(val) def find(self, val):
return property(fget=fget, fset=fset) self.find_text.setText(val)
@dynamic_property @property
def replace(self): def replace(self):
def fget(self): if self.mode == 'function':
if self.mode == 'function': return self.functions.text()
return self.functions.text() return unicode_type(self.replace_text.text())
return unicode_type(self.replace_text.text())
def fset(self, val): @replace.setter
self.replace_text.setText(val) def replace(self, val):
return property(fget=fget, fset=fset) self.replace_text.setText(val)
@dynamic_property @property
def where(self): def where(self):
def fget(self): return self.where_box.where
return self.where_box.where
def fset(self, val): @where.setter
self.where_box.where = val def where(self, val):
return property(fget=fget, fset=fset) self.where_box.where = val
@dynamic_property @property
def case_sensitive(self): def case_sensitive(self):
def fget(self): return self.cs.isChecked()
return self.cs.isChecked()
def fset(self, val): @case_sensitive.setter
self.cs.setChecked(bool(val)) def case_sensitive(self, val):
return property(fget=fget, fset=fset) self.cs.setChecked(bool(val))
@dynamic_property @property
def direction(self): def direction(self):
def fget(self): return self.direction_box.direction
return self.direction_box.direction
def fset(self, val): @direction.setter
self.direction_box.direction = val def direction(self, val):
return property(fget=fget, fset=fset) self.direction_box.direction = val
@dynamic_property @property
def wrap(self): def wrap(self):
def fget(self): return self.wr.isChecked()
return self.wr.isChecked()
def fset(self, val): @wrap.setter
self.wr.setChecked(bool(val)) def wrap(self, val):
return property(fget=fget, fset=fset) self.wr.setChecked(bool(val))
@dynamic_property @property
def dot_all(self): def dot_all(self):
def fget(self): return self.da.isChecked()
return self.da.isChecked()
def fset(self, val): @dot_all.setter
self.da.setChecked(bool(val)) def dot_all(self, val):
return property(fget=fget, fset=fset) self.da.setChecked(bool(val))
@dynamic_property @property
def state(self): 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): @state.setter
for x in self.DEFAULT_STATE: def state(self, val):
if x in val: for x in self.DEFAULT_STATE:
setattr(self, x, val[x]) if x in val:
return property(fget=fget, fset=fset) setattr(self, x, val[x])
def restore_state(self): def restore_state(self):
self.state = tprefs.get('find-widget-state', self.DEFAULT_STATE) self.state = tprefs.get('find-widget-state', self.DEFAULT_STATE)
@ -1008,14 +996,13 @@ class SavedSearches(QWidget):
self.searches.setFocus(Qt.OtherFocusReason) self.searches.setFocus(Qt.OtherFocusReason)
@dynamic_property @property
def state(self): 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): @state.setter
self.wrap, self.where, self.direction = val['wrap'], val['where'], val['direction'] def state(self, val):
return property(fget=fget, fset=fset) self.wrap, self.where, self.direction = val['wrap'], val['where'], val['direction']
def save_state(self): def save_state(self):
tprefs['saved_seaches_state'] = self.state 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'): for x in ('eb', 'ab', 'rb', 'upb', 'dnb', 'd2', 'filter_text', 'cft', 'd3', 'ib', 'eb2'):
getattr(self, x).setVisible(visible) getattr(self, x).setVisible(visible)
@dynamic_property @property
def where(self): def where(self):
def fget(self): return self.where_box.where
return self.where_box.where
def fset(self, val): @where.setter
self.where_box.where = val def where(self, val):
return property(fget=fget, fset=fset) self.where_box.where = val
@dynamic_property @property
def direction(self): def direction(self):
def fget(self): return self.direction_box.direction
return self.direction_box.direction
def fset(self, val): @direction.setter
self.direction_box.direction = val def direction(self, val):
return property(fget=fget, fset=fset) self.direction_box.direction = val
@dynamic_property @property
def wrap(self): def wrap(self):
def fget(self): return self.wr.isChecked()
return self.wr.isChecked()
def fset(self, val): @wrap.setter
self.wr.setChecked(bool(val)) def wrap(self, val):
return property(fget=fget, fset=fset) self.wr.setChecked(bool(val))
def do_filter(self, text): def do_filter(self, text):
self.model.do_filter(text) self.model.do_filter(text)

View File

@ -36,14 +36,13 @@ class ModeBox(QComboBox):
<dd>The search expression is interpreted as a regular expression. See the User Manual for more help on using regular expressions.</dd> <dd>The search expression is interpreted as a regular expression. See the User Manual for more help on using regular expressions.</dd>
</dl>''')) </dl>'''))
@dynamic_property @property
def mode(self): def mode(self):
def fget(self): return ('normal', 'regex')[self.currentIndex()]
return ('normal', 'regex')[self.currentIndex()]
def fset(self, val): @mode.setter
self.setCurrentIndex({'regex':1}.get(val, 0)) def mode(self, val):
return property(fget=fget, fset=fset) self.setCurrentIndex({'regex':1}.get(val, 0))
class WhereBox(QComboBox): class WhereBox(QComboBox):
@ -71,16 +70,15 @@ class WhereBox(QComboBox):
f.setBold(True), f.setItalic(True) f.setBold(True), f.setItalic(True)
self.setFont(f) self.setFont(f)
@dynamic_property @property
def where(self): def where(self):
wm = {0:'current', 1:'text', 2:'selected', 3:'open'} wm = {0:'current', 1:'text', 2:'selected', 3:'open'}
return wm[self.currentIndex()]
def fget(self): @where.setter
return wm[self.currentIndex()] def where(self, val):
wm = {0:'current', 1:'text', 2:'selected', 3:'open'}
def fset(self, val): self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val])
self.setCurrentIndex({v:k for k, v in iteritems(wm)}[val])
return property(fget=fget, fset=fset)
def showPopup(self): def showPopup(self):
# We do it like this so that the popup uses a normal font # 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') state = tprefs.get('text_search_widget_state')
self.state = state or {} self.state = state or {}
@dynamic_property @property
def state(self): 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): @state.setter
self.mode.mode = val.get('mode', 'normal') def state(self, val):
self.where_box.where = val.get('where', 'current') self.mode.mode = val.get('mode', 'normal')
self.cs.setChecked(bool(val.get('case_sensitive'))) self.where_box.where = val.get('where', 'current')
self.da.setChecked(bool(val.get('dot_all', True))) self.cs.setChecked(bool(val.get('case_sensitive')))
return property(fget=fget, fset=fset) self.da.setChecked(bool(val.get('dot_all', True)))
def save_state(self): def save_state(self):
tprefs['text_search_widget_state'] = self.state tprefs['text_search_widget_state'] = self.state

View File

@ -236,17 +236,16 @@ class ConfigDialog(QDialog, Ui_Dialog):
from calibre.gui2.viewer.main import dprefs from calibre.gui2.viewer.main import dprefs
self.word_lookups = dprefs['word_lookups'] self.word_lookups = dprefs['word_lookups']
@dynamic_property @property
def word_lookups(self): 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): @word_lookups.setter
self.dictionary_list.clear() def word_lookups(self, wl):
for langcode, url in sorted(iteritems(wl), key=lambda lc_url:sort_key(calibre_langcode_to_name(lc_url[0]))): self.dictionary_list.clear()
i = QListWidgetItem('%s: %s' % (calibre_langcode_to_name(langcode), url), self.dictionary_list) for langcode, url in sorted(iteritems(wl), key=lambda lc_url:sort_key(calibre_langcode_to_name(lc_url[0]))):
i.setData(Qt.UserRole, (langcode, url)) i = QListWidgetItem('%s: %s' % (calibre_langcode_to_name(langcode), url), self.dictionary_list)
return property(fget=fget, fset=fset) i.setData(Qt.UserRole, (langcode, url))
def add_dictionary_website(self): def add_dictionary_website(self):
class AD(QDialog): class AD(QDialog):

View File

@ -430,46 +430,43 @@ class Document(QWebPage): # {{{
def xpos(self): def xpos(self):
return self.mainFrame().scrollPosition().x() return self.mainFrame().scrollPosition().x()
@dynamic_property @property
def scroll_fraction(self): def scroll_fraction(self):
def fget(self): if self.in_paged_mode:
if self.in_paged_mode: return self.javascript('''
return self.javascript(''' ans = 0.0;
ans = 0.0; if (window.paged_display) {
if (window.paged_display) { ans = window.paged_display.current_pos();
ans = window.paged_display.current_pos(); }
} ans;''', typ='float')
ans;''', typ='float') else:
else: try:
try: return abs(float(self.ypos)/(self.height-self.window_height))
return abs(float(self.ypos)/(self.height-self.window_height)) except ZeroDivisionError:
except ZeroDivisionError: return 0.
return 0.
def fset(self, val): @scroll_fraction.setter
if self.in_paged_mode and self.loaded_javascript: def scroll_fraction(self, val):
self.javascript('paged_display.scroll_to_pos(%f)'%val) if self.in_paged_mode and self.loaded_javascript:
else: self.javascript('paged_display.scroll_to_pos(%f)'%val)
npos = val * (self.height - self.window_height) else:
if npos < 0: npos = val * (self.height - self.window_height)
npos = 0 if npos < 0:
self.scroll_to(x=self.xpos, y=npos) npos = 0
return property(fget=fget, fset=fset) self.scroll_to(x=self.xpos, y=npos)
@dynamic_property @property
def page_number(self): def page_number(self):
' The page number is the number of the page at the left most edge of the screen (starting from 0) ' ' 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): @page_number.setter
if self.in_paged_mode: def page_number(self, val):
return self.javascript( if self.in_paged_mode and self.loaded_javascript:
'ans = 0; if (window.paged_display) ans = window.paged_display.column_boundaries()[0]; ans;', typ='int') self.javascript('if (window.paged_display) window.paged_display.scroll_to_column(%d)' % int(val))
return True
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)
@property @property
def page_dimensions(self): def page_dimensions(self):
@ -862,14 +859,13 @@ class DocumentView(QWebView): # {{{
def sizeHint(self): def sizeHint(self):
return self._size_hint return self._size_hint
@dynamic_property @property
def scroll_fraction(self): def scroll_fraction(self):
def fget(self): return self.document.scroll_fraction
return self.document.scroll_fraction
def fset(self, val): @scroll_fraction.setter
self.document.scroll_fraction = float(val) def scroll_fraction(self, val):
return property(fget=fget, fset=fset) self.document.scroll_fraction = float(val)
@property @property
def hscroll_fraction(self): def hscroll_fraction(self):
@ -879,14 +875,13 @@ class DocumentView(QWebView): # {{{
def content_size(self): def content_size(self):
return self.document.width, self.document.height return self.document.width, self.document.height
@dynamic_property @property
def current_language(self): def current_language(self):
def fget(self): return self.document.current_language
return self.document.current_language
def fset(self, val): @current_language.setter
self.document.current_language = val def current_language(self, val):
return property(fget=fget, fset=fset) self.document.current_language = val
def search(self, text, backwards=False): def search(self, text, backwards=False):
flags = self.document.FindBackward if backwards else self.document.FindFlags(0) 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: if notify and self.manager is not None and new_pos != old_pos:
self.manager.scrolled(self.scroll_fraction) self.manager.scrolled(self.scroll_fraction)
@dynamic_property @property
def multiplier(self): def multiplier(self):
def fget(self): return self.zoomFactor()
return self.zoomFactor()
def fset(self, val): @multiplier.setter
oval = self.zoomFactor() def multiplier(self, val):
self.setZoomFactor(val) oval = self.zoomFactor()
if val != oval: self.setZoomFactor(val)
if self.document.in_paged_mode: if val != oval:
self.document.update_contents_size_for_paged_mode() if self.document.in_paged_mode:
self.magnification_changed.emit(val) self.document.update_contents_size_for_paged_mode()
return property(fget=fget, fset=fset) self.magnification_changed.emit(val)
def magnify_fonts(self, amount=None): def magnify_fonts(self, amount=None):
if amount is None: if amount is None:

View File

@ -1131,30 +1131,28 @@ class Splitter(QSplitter):
print(self.save_name, 'side:', self.side_index_size, 'other:', end=' ') print(self.save_name, 'side:', self.side_index_size, 'other:', end=' ')
print(list(self.sizes())[self.other_index]) print(list(self.sizes())[self.other_index])
@dynamic_property @property
def side_index_size(self): def side_index_size(self):
def fget(self): if self.count() < 2:
if self.count() < 2: return 0
return 0 return self.sizes()[self.side_index]
return self.sizes()[self.side_index]
def fset(self, val): @side_index_size.setter
if self.count() < 2: def side_index_size(self, val):
return if self.count() < 2:
if val == 0 and not self.is_side_index_hidden: return
self.save_state() if val == 0 and not self.is_side_index_hidden:
sizes = list(self.sizes()) self.save_state()
for i in range(len(sizes)): sizes = list(self.sizes())
sizes[i] = val if i == self.side_index else 10 for i in range(len(sizes)):
self.setSizes(sizes) sizes[i] = val if i == self.side_index else 10
total = sum(self.sizes()) self.setSizes(sizes)
sizes = list(self.sizes()) total = sum(self.sizes())
for i in range(len(sizes)): sizes = list(self.sizes())
sizes[i] = val if i == self.side_index else total-val for i in range(len(sizes)):
self.setSizes(sizes) sizes[i] = val if i == self.side_index else total-val
self.initialize() self.setSizes(sizes)
self.initialize()
return property(fget=fget, fset=fset)
def do_resize(self, *args): def do_resize(self, *args):
orig = self.desired_side_size orig = self.desired_side_size

View File

@ -86,28 +86,27 @@ class ColorButton(QPushButton):
self.color = initial_color self.color = initial_color
self.clicked.connect(self.choose_color) self.clicked.connect(self.choose_color)
@dynamic_property @property
def color(self): def color(self):
def fget(self): return self._color
return self._color
def fset(self, val): @color.setter
val = unicode_type(val or '') def color(self, val):
col = QColor(val) val = unicode_type(val or '')
orig = self._color col = QColor(val)
if col.isValid(): orig = self._color
self._color = val if col.isValid():
self.setText(val) self._color = val
p = QPixmap(self.iconSize()) self.setText(val)
p.fill(col) p = QPixmap(self.iconSize())
self.setIcon(QIcon(p)) p.fill(col)
else: self.setIcon(QIcon(p))
self._color = None else:
self.setText(self.choose_text) self._color = None
self.setIcon(QIcon()) self.setText(self.choose_text)
if orig != col: self.setIcon(QIcon())
self.color_changed.emit(self._color) if orig != col:
return property(fget=fget, fset=fset) self.color_changed.emit(self._color)
def choose_color(self): def choose_color(self):
col = QColorDialog.getColor(QColor(self._color or Qt.white), self, _('Choose a color')) col = QColorDialog.getColor(QColor(self._color or Qt.white), self, _('Choose a color'))

View File

@ -827,13 +827,10 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
# _lock_file = None # _lock_file = None
self.conn.close() self.conn.close()
@dynamic_property @property
def user_version(self): 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)
return property(doc=doc, fget=fget)
def is_empty(self): def is_empty(self):
return not self.conn.get('SELECT id FROM books LIMIT 1', all=False) return not self.conn.get('SELECT id FROM books LIMIT 1', all=False)

View File

@ -75,43 +75,36 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
PATH_LIMIT = 40 if 'win32' in sys.platform else 100 PATH_LIMIT = 40 if 'win32' in sys.platform else 100
WINDOWS_LIBRARY_PATH_LIMIT = 75 WINDOWS_LIBRARY_PATH_LIMIT = 75
@dynamic_property @property
def user_version(self): 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): @user_version.setter
return self.conn.get('pragma user_version;', all=False) def user_version(self, val):
self.conn.execute('pragma user_version=%d'%int(val))
self.conn.commit()
def fset(self, val): @property
self.conn.execute('pragma user_version=%d'%int(val))
self.conn.commit()
return property(doc=doc, fget=fget, fset=fset)
@dynamic_property
def library_id(self): def library_id(self):
doc = ('The UUID for this library. As long as the user only operates' '''The UUID for this library. As long as the user only operates on libraries with calibre, it will be unique'''
' 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): @library_id.setter
if self._library_id_ is None: def library_id(self, val):
ans = self.conn.get('SELECT uuid FROM library_id', all=False) self._library_id_ = unicode_type(val)
if ans is None: self.conn.executescript('''
ans = str(uuid.uuid4()) DELETE FROM library_id;
self.library_id = ans INSERT INTO library_id (uuid) VALUES ("%s");
else: '''%self._library_id_)
self._library_id_ = ans self.conn.commit()
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)
def connect(self): def connect(self):
if iswindows and len(self.library_path) + 4*self.PATH_LIMIT + 10 > 259: if iswindows and len(self.library_path) + 4*self.PATH_LIMIT + 10 > 259:

View File

@ -57,47 +57,43 @@ class Image(object):
def to_qimage(self): def to_qimage(self):
return clone_image(self.img) return clone_image(self.img)
@dynamic_property @property
def type(self): def type(self):
def fget(self): if len(self.img.colorTable()) > 0:
if len(self.img.colorTable()) > 0: return 'PaletteType'
return 'PaletteType' return 'TrueColorType'
return 'TrueColorType'
def fset(self, t): @type.setter
if t == 'GrayscaleType': def type(self, t):
self.img = grayscale_image(self.img) if t == 'GrayscaleType':
elif t == 'PaletteType': self.img = grayscale_image(self.img)
self.img = quantize_image(self.img) elif t == 'PaletteType':
return property(fget=fget, fset=fset) self.img = quantize_image(self.img)
@dynamic_property @property
def format(self): 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): @format.setter
self.write_format = val def format(self, val):
return property(fget=fget, fset=fset) self.write_format = val
@dynamic_property @property
def colorspace(self): def colorspace(self):
def fget(self): return 'RGBColorspace'
return 'RGBColorspace'
def fset(self, val): @colorspace.setter
raise NotImplementedError('Changing image colorspace is not supported') def colorspace(self, val):
return property(fget=fget, fset=fset) raise NotImplementedError('Changing image colorspace is not supported')
@dynamic_property @property
def size(self): def size(self):
def fget(self): return self.img.width(), self.img.height()
return self.img.width(), self.img.height()
def fset(self, val): @size.setter
w, h = val[:2] def size(self, val):
self.img = resize_image(self.img, w, h) w, h = val[:2]
return property(fget=fget, fset=fset) self.img = resize_image(self.img, w, h)
def save(self, path, format=None): def save(self, path, format=None):
if format is None: if format is None:

View File

@ -57,32 +57,29 @@ class Article(object):
self.localtime = self.utctime.astimezone(local_tz) self.localtime = self.utctime.astimezone(local_tz)
self._formatted_date = None self._formatted_date = None
@dynamic_property @property
def formatted_date(self): def formatted_date(self):
def fget(self): if self._formatted_date is None:
if self._formatted_date is None: self._formatted_date = strftime(" [%a, %d %b %H:%M]",
self._formatted_date = strftime(" [%a, %d %b %H:%M]", t=self.localtime.timetuple())
t=self.localtime.timetuple()) return self._formatted_date
return self._formatted_date
def fset(self, val): @formatted_date.setter
if isinstance(val, unicode_type): def formatted_date(self, val):
self._formatted_date = val if isinstance(val, unicode_type):
self._formatted_date = val
return property(fget=fget, fset=fset) @property
@dynamic_property
def title(self): def title(self):
def fget(self): t = self._title
t = self._title if not isinstance(t, unicode_type) and hasattr(t, 'decode'):
if not isinstance(t, unicode_type) and hasattr(t, 'decode'): t = t.decode('utf-8', 'replace')
t = t.decode('utf-8', 'replace') return t
return t
def fset(self, val): @title.setter
self._title = clean_ascii_chars(val) def title(self, val):
return property(fget=fget, fset=fset) self._title = clean_ascii_chars(val)
def __repr__(self): def __repr__(self):
return \ return \