class NewsCategory(NewsTreeItem): def __init__(self, category, builtin, custom, scheduler_config, parent): NewsTreeItem.__init__(self, builtin, custom, scheduler_config, parent) self.category = category self.cdata = get_language(self.category) self.bold_font = QFont() self.bold_font.setBold(True) self.bold_font = (self.bold_font) def data(self, role): if role == Qt.DisplayRole: return (self.cdata + ' [%d]'%len(self.children)) elif role == Qt.FontRole: return self.bold_font elif role == Qt.ForegroundRole and self.category == _('Scheduled'): return (QColor(0, 255, 0)) return None def flags(self): return Qt.ItemIsEnabled def __cmp__(self, other): def decorate(x): if x == _('Scheduled'): x = '0' + x elif x == _('Custom'): x = '1' + x else: x = '2' + x return x return cmp(decorate(self.cdata), decorate(getattr(other, 'cdata', '')))
def font(self, text_style): device_font = text_style.fontfacename in LIBERATION_FONT_MAP try: if device_font: face = self.font_map[text_style.fontfacename] else: face = self.face_map[text_style.fontfacename] except KeyError: # Bad fontfacename field in LRF face = self.font_map['Dutch801 Rm BT Roman'] sz = text_style.fontsize wt = text_style.fontweight style = text_style.fontstyle font = (face, wt, style, sz,) if font in self.cache: rfont = self.cache[font] else: italic = font[2] == QFont.StyleItalic rfont = QFont(font[0], font[3], font[1], italic) rfont.setPixelSize(font[3]) rfont.setBold(wt>=69) self.cache[font] = rfont qfont = rfont if text_style.emplinetype != 'none': qfont = QFont(rfont) qfont.setOverline(text_style.emplineposition == 'before') qfont.setUnderline(text_style.emplineposition == 'after') return qfont
def paint_line_numbers(self, ev): painter = QPainter(self.line_number_area) painter.fillRect(ev.rect(), self.line_number_palette.color(QPalette.Base)) block = self.firstVisibleBlock() num = block.blockNumber() top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top()) bottom = top + int(self.blockBoundingRect(block).height()) current = self.textCursor().block().blockNumber() painter.setPen(self.line_number_palette.color(QPalette.Text)) while block.isValid() and top <= ev.rect().bottom(): if block.isVisible() and bottom >= ev.rect().top(): if current == num: painter.save() painter.setPen(self.line_number_palette.color(QPalette.BrightText)) f = QFont(self.font()) f.setBold(True) painter.setFont(f) self.last_current_lnum = (top, bottom - top) painter.drawText( 0, top, self.line_number_area.width() - 5, self.fontMetrics().height(), Qt.AlignRight, str(num + 1) ) if current == num: painter.restore() block = block.next() top = bottom bottom = top + int(self.blockBoundingRect(block).height()) num += 1
class NewsCategory(NewsTreeItem): def __init__(self, category, builtin, custom, scheduler_config, parent): NewsTreeItem.__init__(self, builtin, custom, scheduler_config, parent) self.category = category self.cdata = get_language(self.category) if self.category == _('Scheduled'): self.sortq = 0, '' elif self.category == _('Custom'): self.sortq = 1, '' else: self.sortq = 2, self.cdata self.bold_font = QFont() self.bold_font.setBold(True) self.bold_font = (self.bold_font) def data(self, role): if role == Qt.DisplayRole: return (self.cdata + ' [%d]'%len(self.children)) elif role == Qt.FontRole: return self.bold_font elif role == Qt.ForegroundRole and self.category == _('Scheduled'): return (QColor(0, 255, 0)) return None def flags(self): return Qt.ItemIsEnabled def __eq__(self, other): return self.cdata == other.cdata def __lt__(self, other): return self.sortq < getattr(other, 'sortq', (3, ''))
class NewsCategory(NewsTreeItem): def __init__(self, category, builtin, custom, scheduler_config, parent): NewsTreeItem.__init__(self, builtin, custom, scheduler_config, parent) self.category = category self.cdata = get_language(self.category) self.bold_font = QFont() self.bold_font.setBold(True) self.bold_font = self.bold_font def data(self, role): if role == Qt.DisplayRole: return self.cdata + " [%d]" % len(self.children) elif role == Qt.FontRole: return self.bold_font elif role == Qt.ForegroundRole and self.category == _("Scheduled"): return QColor(0, 255, 0) return None def flags(self): return Qt.ItemIsEnabled def __cmp__(self, other): def decorate(x): if x == _("Scheduled"): x = "0" + x elif x == _("Custom"): x = "1" + x else: x = "2" + x return x return cmp(decorate(self.cdata), decorate(getattr(other, "cdata", "")))
def capture_clicked(self, which=1): self.capture = which button = getattr(self, "button%d" % which) button.setText(_("Press a key...")) button.setFocus(Qt.OtherFocusReason) font = QFont() font.setBold(True) button.setFont(font)
def capture_clicked(self, which=1): self.capture = which for w in 1, 2: self.clear_button(w) button = getattr(self, 'button%d'%which) button.setText(_('Press a key...')) button.setFocus(Qt.OtherFocusReason) font = QFont() font.setBold(True) button.setFont(font)
def set_window_title(self): db = self.current_db restrictions = [x for x in (db.data.get_base_restriction_name(), db.data.get_search_restriction_name()) if x] restrictions = " :: ".join(restrictions) font = QFont() if restrictions: restrictions = " :: " + restrictions font.setBold(True) font.setItalic(True) self.virtual_library.setFont(font) title = u"{0} - || {1}{2} ||".format(__appname__, self.iactions["Choose Library"].library_name(), restrictions) self.setWindowTitle(title)
class HeaderView(QHeaderView): # {{{ def __init__(self, *args): QHeaderView.__init__(self, *args) self.hover = -1 self.current_font = QFont(self.font()) self.current_font.setBold(True) self.current_font.setItalic(True) def event(self, e): if e.type() in (e.HoverMove, e.HoverEnter): self.hover = self.logicalIndexAt(e.pos()) elif e.type() in (e.Leave, e.HoverLeave): self.hover = -1 return QHeaderView.event(self, e) def paintSection(self, painter, rect, logical_index): opt = QStyleOptionHeader() self.initStyleOption(opt) opt.rect = rect opt.section = logical_index opt.orientation = self.orientation() opt.textAlignment = Qt.AlignHCenter | Qt.AlignVCenter model = self.parent().model() opt.text = unicode(model.headerData(logical_index, opt.orientation, Qt.DisplayRole) or '') if self.isSortIndicatorShown() and self.sortIndicatorSection() == logical_index: opt.sortIndicator = QStyleOptionHeader.SortDown if self.sortIndicatorOrder() == Qt.AscendingOrder else QStyleOptionHeader.SortUp opt.text = opt.fontMetrics.elidedText(opt.text, Qt.ElideRight, rect.width() - 4) if self.isEnabled(): opt.state |= QStyle.State_Enabled if self.window().isActiveWindow(): opt.state |= QStyle.State_Active if self.hover == logical_index: opt.state |= QStyle.State_MouseOver sm = self.selectionModel() if opt.orientation == Qt.Vertical: try: opt.icon = model.headerData(logical_index, opt.orientation, Qt.DecorationRole) opt.iconAlignment = Qt.AlignVCenter except (IndexError, ValueError, TypeError): pass if sm.isRowSelected(logical_index, QModelIndex()): opt.state |= QStyle.State_Sunken painter.save() if ( (opt.orientation == Qt.Horizontal and sm.currentIndex().column() == logical_index) or (opt.orientation == Qt.Vertical and sm.currentIndex().row() == logical_index)): painter.setFont(self.current_font) self.style().drawControl(QStyle.CE_Header, opt, painter, self) painter.restore()
class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): QStatusBar.__init__(self, parent) self.default_message = __appname__ + ' ' + _('version') + ' ' + \ __version__ + ' ' + _('created by Kovid Goyal') self.device_string = '' self._font = QFont() self._font.setBold(True) self.setFont(self._font) self.w = QLabel(self.default_message) self.w.setFont(self._font) self.addWidget(self.w)
def data(self, index, role): try: widget = self.widgets[index.row()] except: return None if role == Qt.DisplayRole: return (widget.config_title()) if role == Qt.DecorationRole: return (widget.config_icon()) if role == Qt.FontRole: f = QFont() f.setBold(True) return (f) return None
class Category(QWidget): # {{{ plugin_activated = pyqtSignal(object) def __init__(self, name, plugins, gui_name, parent=None): QWidget.__init__(self, parent) self._layout = QVBoxLayout() self.setLayout(self._layout) self.label = QLabel(gui_name) self.sep = QFrame(self) self.bf = QFont() self.bf.setBold(True) self.label.setFont(self.bf) self.sep.setFrameShape(QFrame.HLine) self._layout.addWidget(self.label) self._layout.addWidget(self.sep) self.plugins = plugins self.bar = QToolBar(self) self.bar.setStyleSheet( 'QToolBar { border: none; background: none }') lh = QApplication.instance().line_height self.bar.setIconSize(QSize(2*lh, 2*lh)) self.bar.setMovable(False) self.bar.setFloatable(False) self.bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self._layout.addWidget(self.bar) self.actions = [] for p in plugins: target = partial(self.triggered, p) ac = self.bar.addAction(QIcon(p.icon), p.gui_name.replace('&', '&&'), target) ac.setToolTip(textwrap.fill(p.description)) ac.setWhatsThis(textwrap.fill(p.description)) ac.setStatusTip(p.description) self.actions.append(ac) w = self.bar.widgetForAction(ac) w.setCursor(Qt.PointingHandCursor) if hasattr(w, 'setAutoRaise'): w.setAutoRaise(True) w.setMinimumWidth(100) def triggered(self, plugin, *args): self.plugin_activated.emit(plugin)
def process_duplicates(self, db, duplicates): ta = _('%(title)s by %(author)s [%(formats)s]') bf = QFont(self.dup_list.font()) bf.setBold(True) itf = QFont(self.dup_list.font()) itf.setItalic(True) for mi, cover, formats in duplicates: # formats is a list of file paths # Grab just the extension and display to the user # Based only off the file name, no file type tests are done. incoming_formats = ', '.join(os.path.splitext(path)[-1].replace('.', '').upper() for path in formats) item = QTreeWidgetItem([ta%dict( title=mi.title, author=mi.format_field('authors')[1], formats=incoming_formats)] , 0) item.setCheckState(0, Qt.Checked) item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) item.setData(0, Qt.FontRole, bf) item.setData(0, Qt.UserRole, (mi, cover, formats)) matching_books = db.books_with_same_title(mi) def add_child(text): c = QTreeWidgetItem([text], 1) c.setFlags(Qt.ItemIsEnabled) item.addChild(c) return c add_child(_('Already in calibre:')).setData(0, Qt.FontRole, itf) for book_id in matching_books: aut = [a.replace('|', ',') for a in (db.authors(book_id, index_is_id=True) or '').split(',')] add_child(ta%dict( title=db.title(book_id, index_is_id=True), author=authors_to_string(aut), formats=db.formats(book_id, index_is_id=True, verify_formats=False))) add_child('') yield item
def data(self, index, role): row = index.row() try: tweak = self.tweaks[row] except: return None if role == Qt.DisplayRole: return textwrap.fill(tweak.name, 40) if role == Qt.FontRole and tweak.is_customized: ans = QFont() ans.setBold(True) return ans if role == Qt.ToolTipRole: tt = _('This tweak has it default value') if tweak.is_customized: tt = '<p>'+_('This tweak has been customized') tt += '<pre>' for varn, val in tweak.custom_values.iteritems(): tt += '%s = %r\n\n'%(varn, val) return textwrap.fill(tt) if role == Qt.UserRole: return tweak return None
def paint_line_numbers(self, ev): painter = QPainter(self.line_number_area) painter.fillRect(ev.rect(), self.line_number_palette.color(QPalette.Base)) block = self.firstVisibleBlock() num = block.blockNumber() top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top()) bottom = top + int(self.blockBoundingRect(block).height()) painter.setPen(self.line_number_palette.color(QPalette.Text)) change_starts = {x[0] for x in self.changes} while block.isValid() and top <= ev.rect().bottom(): r = ev.rect() if block.isVisible() and bottom >= r.top(): text = unicode(self.line_number_map.get(num, '')) is_start = text != '-' and num in change_starts if is_start: painter.save() f = QFont(self.font()) f.setBold(True) painter.setFont(f) painter.setPen(self.line_number_palette.color(QPalette.BrightText)) if text == '-': painter.drawLine(r.left() + 2, (top + bottom)//2, r.right() - 2, (top + bottom)//2) else: if self.right: painter.drawText(r.left() + 3, top, r.right(), self.fontMetrics().height(), Qt.AlignLeft, text) else: painter.drawText(r.left() + 2, top, r.right() - 5, self.fontMetrics().height(), Qt.AlignRight, text) if is_start: painter.restore() block = block.next() top = bottom bottom = top + int(self.blockBoundingRect(block).height()) num += 1
def generate_masthead(title, output_path=None, width=600, height=60, as_qimage=False, font_family=None): init_environment() font_family = font_family or cprefs['title_font_family'] or 'Liberation Serif' img = QImage(width, height, QImage.Format_ARGB32) img.fill(Qt.white) p = QPainter(img) f = QFont(font_family) f.setPixelSize((height * 3) // 4), f.setBold(True) p.setFont(f) p.drawText(img.rect(), Qt.AlignLeft | Qt.AlignVCenter, sanitize(title)) p.end() if as_qimage: return img data = pixmap_to_data(img) if output_path is None: return data with open(output_path, 'wb') as f: f.write(data)
def mark_item_as_current(self, item): font = QFont(self.font()) font.setItalic(True) font.setBold(True) item.setData(0, Qt.FontRole, font)
class TOCItem(QStandardItem): def __init__(self, spine, toc, depth, all_items, parent=None): text = toc.text if text: text = re.sub(r'\s', ' ', text) self.title = text self.parent = parent QStandardItem.__init__(self, text if text else '') self.abspath = toc.abspath if toc.href else None self.fragment = toc.fragment all_items.append(self) self.emphasis_font = QFont(self.font()) self.emphasis_font.setBold(True), self.emphasis_font.setItalic(True) self.normal_font = self.font() for t in toc: self.appendRow(TOCItem(spine, t, depth+1, all_items, parent=self)) self.setFlags(Qt.ItemIsEnabled) self.is_current_search_result = False spos = 0 for i, si in enumerate(spine): if si == self.abspath: spos = i break am = {} if self.abspath is not None: try: am = getattr(spine[i], 'anchor_map', {}) except UnboundLocalError: # Spine was empty? pass frag = self.fragment if (self.fragment and self.fragment in am) else None self.starts_at = spos self.start_anchor = frag self.start_src_offset = am.get(frag, 0) self.depth = depth self.is_being_viewed = False @property def ancestors(self): parent = self.parent while parent is not None: yield parent parent = parent.parent @classmethod def type(cls): return QStandardItem.UserType+10 def update_indexing_state(self, spine_index, viewport_rect, anchor_map, in_paged_mode): if in_paged_mode: self.update_indexing_state_paged(spine_index, viewport_rect, anchor_map) else: self.update_indexing_state_unpaged(spine_index, viewport_rect, anchor_map) def update_indexing_state_unpaged(self, spine_index, viewport_rect, anchor_map): is_being_viewed = False top, bottom = viewport_rect[1], viewport_rect[3] # We use bottom-25 in the checks below to account for the case where # the next entry has some invisible margin that just overlaps with the # bottom of the screen. In this case it will appear to the user that # the entry is not visible on the screen. Of course, the margin could # be larger than 25, but that's a decent compromise. Also we dont want # to count a partial line as being visible. # We only care about y position anchor_map = {k:v[1] for k, v in anchor_map.iteritems()} if spine_index >= self.starts_at and spine_index <= self.ends_at: # The position at which this anchor is present in the document start_pos = anchor_map.get(self.start_anchor, 0) psp = [] if self.ends_at == spine_index: # Anchors that could possibly indicate the start of the next # section and therefore the end of this section. # self.possible_end_anchors is a set of anchors belonging to # toc entries with depth <= self.depth that are also not # ancestors of this entry. psp = [anchor_map.get(x, 0) for x in self.possible_end_anchors] psp = [x for x in psp if x >= start_pos] # The end position. The first anchor whose pos is >= start_pos # or if the end is not in this spine item, we set it to the bottom # of the window +1 end_pos = min(psp) if psp else (bottom+1 if self.ends_at >= spine_index else 0) if spine_index > self.starts_at and spine_index < self.ends_at: # The entire spine item is contained in this entry is_being_viewed = True elif (spine_index == self.starts_at and bottom-25 >= start_pos and # This spine item contains the start # The start position is before the end of the viewport (spine_index != self.ends_at or top < end_pos)): # The end position is after the start of the viewport is_being_viewed = True elif (spine_index == self.ends_at and top < end_pos and # This spine item contains the end # The end position is after the start of the viewport (spine_index != self.starts_at or bottom-25 >= start_pos)): # The start position is before the end of the viewport is_being_viewed = True changed = is_being_viewed != self.is_being_viewed self.is_being_viewed = is_being_viewed if changed: self.setFont(self.emphasis_font if is_being_viewed else self.normal_font) def update_indexing_state_paged(self, spine_index, viewport_rect, anchor_map): is_being_viewed = False left, right = viewport_rect[0], viewport_rect[2] left, right = (left, 0), (right, -1) if spine_index >= self.starts_at and spine_index <= self.ends_at: # The position at which this anchor is present in the document start_pos = anchor_map.get(self.start_anchor, (0, 0)) psp = [] if self.ends_at == spine_index: # Anchors that could possibly indicate the start of the next # section and therefore the end of this section. # self.possible_end_anchors is a set of anchors belonging to # toc entries with depth <= self.depth that are also not # ancestors of this entry. psp = [anchor_map.get(x, (0, 0)) for x in self.possible_end_anchors] psp = [x for x in psp if x >= start_pos] # The end position. The first anchor whose pos is >= start_pos # or if the end is not in this spine item, we set it to the column # after the right edge of the viewport end_pos = min(psp) if psp else (right if self.ends_at >= spine_index else (0, 0)) if spine_index > self.starts_at and spine_index < self.ends_at: # The entire spine item is contained in this entry is_being_viewed = True elif (spine_index == self.starts_at and right > start_pos and # This spine item contains the start # The start position is before the end of the viewport (spine_index != self.ends_at or left < end_pos)): # The end position is after the start of the viewport is_being_viewed = True elif (spine_index == self.ends_at and left < end_pos and # This spine item contains the end # The end position is after the start of the viewport (spine_index != self.starts_at or right > start_pos)): # The start position is before the end of the viewport is_being_viewed = True changed = is_being_viewed != self.is_being_viewed self.is_being_viewed = is_being_viewed if changed: self.setFont(self.emphasis_font if is_being_viewed else self.normal_font) def set_current_search_result(self, yes): if yes and not self.is_current_search_result: self.setText(self.text() + ' ◄') self.is_current_search_result = True elif not yes and self.is_current_search_result: self.setText(self.text()[:-2]) self.is_current_search_result = False def __repr__(self): return 'TOC Item: %s %s#%s'%(self.title, self.abspath, self.fragment) def __str__(self): return repr(self)
def __init__(self, gui, icon, guidb, plugin_path, ui_exit, action_type): parent = gui unique_pref_name = 'library_codes:gui_parameters_dialog' SizePersistedDialog.__init__(self, parent, unique_pref_name) #----------------------------------------------------- self.gui = gui self.guidb = guidb #----------------------------------------------------- self.icon = icon #----------------------------------------------------- self.plugin_path = plugin_path #----------------------------------------------------- self.ui_exit = ui_exit #----------------------------------------------------- self.action_type = action_type #----------------------------------------------------- self.myparentprefs = collections.OrderedDict([]) prefsdefaults = deepcopy(prefs.defaults) tmp_list = [] #~ for k,v in prefs.iteritems(): for k, v in iteritems(prefs): tmp_list.append(k) #END FOR #~ for k,v in prefsdefaults.iteritems(): for k, v in iteritems(prefsdefaults): tmp_list.append(k) #END FOR tmp_set = set(tmp_list) tmp_list = list(tmp_set) #no duplicates del tmp_set tmp_list.sort() for k in tmp_list: self.myparentprefs[k] = " " # ordered by key #END FOR del tmp_list #~ for k,v in prefs.iteritems(): for k, v in iteritems(prefs): self.myparentprefs[k] = v #END FOR #~ for k,v in prefsdefaults.iteritems(): for k, v in iteritems(prefsdefaults): if not k in prefs: prefs[k] = v else: if not prefs[k] > " ": prefs[k] = v if not k in self.myparentprefs: self.myparentprefs[k] = v else: if not self.myparentprefs[k] > " ": self.myparentprefs[k] = v #END FOR #~ for k,v in self.myparentprefs.iteritems(): for k, v in iteritems(self.myparentprefs): prefs[k] = v #END FOR prefs #prefs now synched #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.param_dict = collections.OrderedDict([]) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.init_tooltips_for_parent() self.setToolTip(self.parent_tooltip) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- # Tab 0: LibraryCodesTab #----------------------------------------------------- #----------------------------------------------------- from calibre_plugins.library_codes.library_codes_dialog import LibraryCodesTab self.LibraryCodesTab = LibraryCodesTab(self.gui, self.guidb, self.myparentprefs, self.param_dict, self.ui_exit, self.save_dialog_geometry) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- # Parent LibraryCodesDialog #----------------------------------------------------- font = QFont() font.setBold(False) font.setPointSize(10) tablabel_font = QFont() tablabel_font.setBold(False) tablabel_font.setPointSize(10) #----------------------------------------------------- self.setWindowTitle('Library Codes') self.setWindowIcon(icon) #----------------------------------------------------- self.layout_frame = QVBoxLayout() self.layout_frame.setAlignment(Qt.AlignLeft) self.setLayout(self.layout_frame) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- n_width = 600 self.LCtabWidget = QTabWidget() self.LCtabWidget.setMaximumWidth(n_width) self.LCtabWidget.setFont(tablabel_font) self.LCtabWidget.addTab( self.LibraryCodesTab, "Derivation from ISBN or ISSN or Author/Title") self.LibraryCodesTab.setToolTip( "<p style='white-space:wrap'>Derive Library Codes DDC and/or LCC and/or OCLC-OWI from ISBN or ISSN or Author/Title. Visit: http://classify.oclc.org/classify2/ " ) self.LibraryCodesTab.setMaximumWidth(n_width) #----------------------------------------------------- self.layout_frame.addWidget(self.LCtabWidget) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.resize_dialog() # inherited from SizePersistedDialog #----------------------------------------------------- self.LCtabWidget.setCurrentIndex(0)
class EmailAccounts(QAbstractTableModel): # {{{ def __init__(self, accounts, subjects, aliases={}, tags={}): QAbstractTableModel.__init__(self) self.accounts = accounts self.subjects = subjects self.aliases = aliases self.tags = tags self.sorted_on = (0, True) self.account_order = self.accounts.keys() self.do_sort() self.headers = map(unicode, [_('Email'), _('Formats'), _('Subject'), _('Auto send'), _('Alias'), _('Auto send only tags')]) self.default_font = QFont() self.default_font.setBold(True) self.default_font = (self.default_font) self.tooltips =[None] + list(map(unicode, map(textwrap.fill, [_('Formats to email. The first matching format will be sent.'), _('Subject of the email to use when sending. When left blank ' 'the title will be used for the subject. Also, the same ' 'templates used for "Save to disk" such as {title} and ' '{author_sort} can be used here.'), '<p>'+_('If checked, downloaded news will be automatically ' 'mailed to this email address ' '(provided it is in one of the listed formats and has not been filtered by tags).'), _('Friendly name to use for this email address'), _('If specified, only news with one of these tags will be sent to' ' this email address. All news downloads have their title as a' ' tag, so you can use this to easily control which news downloads' ' are sent to this email address.') ]))) def do_sort(self): col = self.sorted_on[0] if col == 0: def key(account_key): return numeric_sort_key(account_key) elif col == 1: def key(account_key): return numeric_sort_key(self.accounts[account_key][0] or '') elif col == 2: def key(account_key): return numeric_sort_key(self.subjects.get(account_key) or '') elif col == 3: def key(account_key): return numeric_sort_key(type(u'')(self.accounts[account_key][0]) or '') elif col == 4: def key(account_key): return numeric_sort_key(self.aliases.get(account_key) or '') elif col == 5: def key(account_key): return numeric_sort_key(self.tags.get(account_key) or '') self.account_order.sort(key=key, reverse=not self.sorted_on[1]) def sort(self, column, order=Qt.AscendingOrder): nsort = (column, order == Qt.AscendingOrder) if nsort != self.sorted_on: self.sorted_on = nsort self.beginResetModel() try: self.do_sort() finally: self.endResetModel() def rowCount(self, *args): return len(self.account_order) def columnCount(self, *args): return len(self.headers) def headerData(self, section, orientation, role): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.headers[section] return None def data(self, index, role): row, col = index.row(), index.column() if row < 0 or row >= self.rowCount(): return None account = self.account_order[row] if account not in self.accounts: return None if role == Qt.UserRole: return (account, self.accounts[account]) if role == Qt.ToolTipRole: return self.tooltips[col] if role in [Qt.DisplayRole, Qt.EditRole]: if col == 0: return (account) if col == 1: return ', '.join(x.strip() for x in (self.accounts[account][0] or '').split(',')) if col == 2: return (self.subjects.get(account, '')) if col == 4: return (self.aliases.get(account, '')) if col == 5: return (self.tags.get(account, '')) if role == Qt.FontRole and self.accounts[account][2]: return self.default_font if role == Qt.CheckStateRole and col == 3: return (Qt.Checked if self.accounts[account][1] else Qt.Unchecked) return None def flags(self, index): if index.column() == 3: return QAbstractTableModel.flags(self, index)|Qt.ItemIsUserCheckable else: return QAbstractTableModel.flags(self, index)|Qt.ItemIsEditable def setData(self, index, value, role): if not index.isValid(): return False row, col = index.row(), index.column() account = self.account_order[row] if col == 3: self.accounts[account][1] ^= True elif col == 2: self.subjects[account] = unicode(value or '') elif col == 4: self.aliases.pop(account, None) aval = unicode(value or '').strip() if aval: self.aliases[account] = aval elif col == 5: self.tags.pop(account, None) aval = unicode(value or '').strip() if aval: self.tags[account] = aval elif col == 1: self.accounts[account][0] = re.sub(',+', ',', re.sub(r'\s+', ',', unicode(value or '').upper())) elif col == 0: na = unicode(value or '') from email.utils import parseaddr addr = parseaddr(na)[-1] if not addr: return False self.accounts[na] = self.accounts.pop(account) self.account_order[row] = na if '@kindle.com' in addr: self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1' self.dataChanged.emit( self.index(index.row(), 0), self.index(index.row(), 3)) return True def make_default(self, index): if index.isValid(): self.beginResetModel() row = index.row() for x in self.accounts.values(): x[2] = False self.accounts[self.account_order[row]][2] = True self.endResetModel() def add(self): x = _('new email address') y = x c = 0 while y in self.accounts: c += 1 y = x + str(c) auto_send = len(self.accounts) < 1 self.beginResetModel() self.accounts[y] = ['MOBI, EPUB', auto_send, len(self.account_order) == 0] self.account_order = self.accounts.keys() self.do_sort() self.endResetModel() return self.index(self.account_order.index(y), 0) def remove(self, index): if index.isValid(): row = index.row() account = self.account_order[row] self.accounts.pop(account) self.account_order = sorted(self.accounts.keys()) has_default = False for account in self.account_order: if self.accounts[account][2]: has_default = True break if not has_default and self.account_order: self.accounts[self.account_order[0]][2] = True self.beginResetModel() self.endResetModel()
class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): QStatusBar.__init__(self, parent) self.version = get_version() self.base_msg = '%s %s' % (__appname__, self.version) self.device_string = '' self.update_label = UpdateLabel('') self.total = self.current = self.selected = self.library_total = 0 self.addPermanentWidget(self.update_label) self.update_label.setVisible(False) self._font = QFont() self._font.setBold(True) self.setFont(self._font) self.defmsg = QLabel('') self.defmsg.setFont(self._font) self.addWidget(self.defmsg) self.set_label() def initialize(self, systray=None): self.systray = systray self.notifier = get_notifier(systray) def device_connected(self, devname): self.device_string = _('Connected ') + devname self.set_label() def update_state(self, library_total, total, current, selected): self.library_total = library_total self.total, self.current, self.selected = total, current, selected self.set_label() def set_label(self): try: self._set_label() except: import traceback traceback.print_exc() def _set_label(self): msg = self.base_msg if self.device_string: msg += ' ..::.. ' + self.device_string else: msg += _(' %(created)s %(name)s') % dict(created=_('created by'), name='Kovid Goyal') if self.total != self.current: base = _('%(num)d of %(total)d books') % dict(num=self.current, total=self.total) else: base = _('%d books') % self.total if self.selected > 0: base = _('%(num)s, %(sel)d selected') % dict(num=base, sel=self.selected) if self.library_total != self.total: base = _('{0}, {1} total').format(base, self.library_total) self.defmsg.setText(u'%s\xa0\xa0\xa0\xa0[%s]' % (msg, base)) self.clearMessage() def device_disconnected(self): self.device_string = '' self.set_label() def show_message(self, msg, timeout=0, show_notification=True): self.showMessage(msg, timeout) if self.notifier is not None and not config['disable_tray_notification'] and show_notification: if isosx and isinstance(msg, unicode): try: msg = msg.encode(preferred_encoding) except UnicodeEncodeError: msg = msg.encode('utf-8') self.notifier(msg) def clear_message(self): self.clearMessage()
class TOCItem(QStandardItem): def __init__(self, spine, toc, depth, all_items, parent=None): text = toc.text if text: text = re.sub(r'\s', ' ', text) self.title = text self.parent = parent QStandardItem.__init__(self, text if text else '') self.abspath = toc.abspath if toc.href else None self.fragment = toc.fragment all_items.append(self) self.emphasis_font = QFont(self.font()) self.emphasis_font.setBold(True), self.emphasis_font.setItalic(True) self.normal_font = self.font() for t in toc: self.appendRow(TOCItem(spine, t, depth+1, all_items, parent=self)) self.setFlags(Qt.ItemIsEnabled) self.is_current_search_result = False spos = 0 for i, si in enumerate(spine): if si == self.abspath: spos = i break am = {} if self.abspath is not None: try: am = getattr(spine[i], 'anchor_map', {}) except UnboundLocalError: # Spine was empty? pass frag = self.fragment if (self.fragment and self.fragment in am) else None self.starts_at = spos self.start_anchor = frag self.start_src_offset = am.get(frag, 0) self.depth = depth self.is_being_viewed = False @property def ancestors(self): parent = self.parent while parent is not None: yield parent parent = parent.parent @classmethod def type(cls): return QStandardItem.UserType+10 def update_indexing_state(self, spine_index, viewport_rect, anchor_map, in_paged_mode): if in_paged_mode: self.update_indexing_state_paged(spine_index, viewport_rect, anchor_map) else: self.update_indexing_state_unpaged(spine_index, viewport_rect, anchor_map) def update_indexing_state_unpaged(self, spine_index, viewport_rect, anchor_map): is_being_viewed = False top, bottom = viewport_rect[1], viewport_rect[3] # We use bottom-25 in the checks below to account for the case where # the next entry has some invisible margin that just overlaps with the # bottom of the screen. In this case it will appear to the user that # the entry is not visible on the screen. Of course, the margin could # be larger than 25, but that's a decent compromise. Also we dont want # to count a partial line as being visible. # We only care about y position anchor_map = {k:v[1] for k, v in anchor_map.items()} if spine_index >= self.starts_at and spine_index <= self.ends_at: # The position at which this anchor is present in the document start_pos = anchor_map.get(self.start_anchor, 0) psp = [] if self.ends_at == spine_index: # Anchors that could possibly indicate the start of the next # section and therefore the end of this section. # self.possible_end_anchors is a set of anchors belonging to # toc entries with depth <= self.depth that are also not # ancestors of this entry. psp = [anchor_map.get(x, 0) for x in self.possible_end_anchors] psp = [x for x in psp if x >= start_pos] # The end position. The first anchor whose pos is >= start_pos # or if the end is not in this spine item, we set it to the bottom # of the window +1 end_pos = min(psp) if psp else (bottom+1 if self.ends_at >= spine_index else 0) if spine_index > self.starts_at and spine_index < self.ends_at: # The entire spine item is contained in this entry is_being_viewed = True elif (spine_index == self.starts_at and bottom-25 >= start_pos and # This spine item contains the start # The start position is before the end of the viewport (spine_index != self.ends_at or top < end_pos)): # The end position is after the start of the viewport is_being_viewed = True elif (spine_index == self.ends_at and top < end_pos and # This spine item contains the end # The end position is after the start of the viewport (spine_index != self.starts_at or bottom-25 >= start_pos)): # The start position is before the end of the viewport is_being_viewed = True changed = is_being_viewed != self.is_being_viewed self.is_being_viewed = is_being_viewed if changed: self.setFont(self.emphasis_font if is_being_viewed else self.normal_font) def update_indexing_state_paged(self, spine_index, viewport_rect, anchor_map): is_being_viewed = False left, right = viewport_rect[0], viewport_rect[2] left, right = (left, 0), (right, -1) if spine_index >= self.starts_at and spine_index <= self.ends_at: # The position at which this anchor is present in the document start_pos = anchor_map.get(self.start_anchor, (0, 0)) psp = [] if self.ends_at == spine_index: # Anchors that could possibly indicate the start of the next # section and therefore the end of this section. # self.possible_end_anchors is a set of anchors belonging to # toc entries with depth <= self.depth that are also not # ancestors of this entry. psp = [anchor_map.get(x, (0, 0)) for x in self.possible_end_anchors] psp = [x for x in psp if x >= start_pos] # The end position. The first anchor whose pos is >= start_pos # or if the end is not in this spine item, we set it to the column # after the right edge of the viewport end_pos = min(psp) if psp else (right if self.ends_at >= spine_index else (0, 0)) if spine_index > self.starts_at and spine_index < self.ends_at: # The entire spine item is contained in this entry is_being_viewed = True elif (spine_index == self.starts_at and right > start_pos and # This spine item contains the start # The start position is before the end of the viewport (spine_index != self.ends_at or left < end_pos)): # The end position is after the start of the viewport is_being_viewed = True elif (spine_index == self.ends_at and left < end_pos and # This spine item contains the end # The end position is after the start of the viewport (spine_index != self.starts_at or right > start_pos)): # The start position is before the end of the viewport is_being_viewed = True changed = is_being_viewed != self.is_being_viewed self.is_being_viewed = is_being_viewed if changed: self.setFont(self.emphasis_font if is_being_viewed else self.normal_font) def set_current_search_result(self, yes): if yes and not self.is_current_search_result: self.setText(self.text() + ' ◄') self.is_current_search_result = True elif not yes and self.is_current_search_result: self.setText(self.text()[:-2]) self.is_current_search_result = False def __repr__(self): return 'TOC Item: %s %s#%s'%(self.title, self.abspath, self.fragment) def __str__(self): return repr(self)
class EmailAccounts(QAbstractTableModel): # {{{ def __init__(self, accounts, subjects, aliases={}, tags={}): QAbstractTableModel.__init__(self) self.accounts = accounts self.subjects = subjects self.aliases = aliases self.tags = tags self.sorted_on = (0, True) self.account_order = list(self.accounts) self.do_sort() self.headers = [ _('Email'), _('Formats'), _('Subject'), _('Auto send'), _('Alias'), _('Auto send only tags') ] self.default_font = QFont() self.default_font.setBold(True) self.default_font = (self.default_font) self.tooltips = [None] + list( map(textwrap.fill, [ _('Formats to email. The first matching format will be sent.'), _('Subject of the email to use when sending. When left blank ' 'the title will be used for the subject. Also, the same ' 'templates used for "Save to disk" such as {title} and ' '{author_sort} can be used here.'), '<p>' + _('If checked, downloaded news will be automatically ' 'mailed to this email address ' '(provided it is in one of the listed formats and has not been filtered by tags).' ), _('Friendly name to use for this email address'), _('If specified, only news with one of these tags will be sent to' ' this email address. All news downloads have their title as a' ' tag, so you can use this to easily control which news downloads' ' are sent to this email address.') ])) def do_sort(self): col = self.sorted_on[0] if col == 0: def key(account_key): return numeric_sort_key(account_key) elif col == 1: def key(account_key): return numeric_sort_key(self.accounts[account_key][0] or '') elif col == 2: def key(account_key): return numeric_sort_key(self.subjects.get(account_key) or '') elif col == 3: def key(account_key): return numeric_sort_key( as_unicode(self.accounts[account_key][0]) or '') elif col == 4: def key(account_key): return numeric_sort_key(self.aliases.get(account_key) or '') elif col == 5: def key(account_key): return numeric_sort_key(self.tags.get(account_key) or '') self.account_order.sort(key=key, reverse=not self.sorted_on[1]) def sort(self, column, order=Qt.SortOrder.AscendingOrder): nsort = (column, order == Qt.SortOrder.AscendingOrder) if nsort != self.sorted_on: self.sorted_on = nsort self.beginResetModel() try: self.do_sort() finally: self.endResetModel() def rowCount(self, *args): return len(self.account_order) def columnCount(self, *args): return len(self.headers) def headerData(self, section, orientation, role): if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal: return self.headers[section] return None def data(self, index, role): row, col = index.row(), index.column() if row < 0 or row >= self.rowCount(): return None account = self.account_order[row] if account not in self.accounts: return None if role == Qt.ItemDataRole.UserRole: return (account, self.accounts[account]) if role == Qt.ItemDataRole.ToolTipRole: return self.tooltips[col] if role in [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.EditRole]: if col == 0: return (account) if col == 1: return ', '.join( x.strip() for x in (self.accounts[account][0] or '').split(',')) if col == 2: return (self.subjects.get(account, '')) if col == 4: return (self.aliases.get(account, '')) if col == 5: return (self.tags.get(account, '')) if role == Qt.ItemDataRole.FontRole and self.accounts[account][2]: return self.default_font if role == Qt.ItemDataRole.CheckStateRole and col == 3: return (Qt.CheckState.Checked if self.accounts[account][1] else Qt.CheckState.Unchecked) return None def flags(self, index): if index.column() == 3: return QAbstractTableModel.flags( self, index) | Qt.ItemFlag.ItemIsUserCheckable else: return QAbstractTableModel.flags( self, index) | Qt.ItemFlag.ItemIsEditable def setData(self, index, value, role): if not index.isValid(): return False row, col = index.row(), index.column() account = self.account_order[row] if col == 3: self.accounts[account][1] ^= True elif col == 2: self.subjects[account] = as_unicode(value or '') elif col == 4: self.aliases.pop(account, None) aval = as_unicode(value or '').strip() if aval: self.aliases[account] = aval elif col == 5: self.tags.pop(account, None) aval = as_unicode(value or '').strip() if aval: self.tags[account] = aval elif col == 1: self.accounts[account][0] = re.sub( ',+', ',', re.sub(r'\s+', ',', as_unicode(value or '').upper())) elif col == 0: na = as_unicode(value or '') from email.utils import parseaddr addr = parseaddr(na)[-1] if not addr: return False self.accounts[na] = self.accounts.pop(account) self.account_order[row] = na if '@kindle.com' in addr: self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1' self.dataChanged.emit(self.index(index.row(), 0), self.index(index.row(), 3)) return True def make_default(self, index): if index.isValid(): self.beginResetModel() row = index.row() for x in self.accounts.values(): x[2] = False self.accounts[self.account_order[row]][2] = True self.endResetModel() def add(self): x = _('new email address') y = x c = 0 while y in self.accounts: c += 1 y = x + unicode_type(c) auto_send = len(self.accounts) < 1 self.beginResetModel() self.accounts[y] = [ 'MOBI, EPUB', auto_send, len(self.account_order) == 0 ] self.account_order = list(self.accounts) self.do_sort() self.endResetModel() return self.index(self.account_order.index(y), 0) def remove(self, index): if index.isValid(): row = index.row() account = self.account_order[row] self.accounts.pop(account) self.account_order = sorted(self.accounts) has_default = False for account in self.account_order: if self.accounts[account][2]: has_default = True break if not has_default and self.account_order: self.accounts[self.account_order[0]][2] = True self.beginResetModel() self.endResetModel()
class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): QStatusBar.__init__(self, parent) self.version = get_version() self.base_msg = '%s %s' % (__appname__, self.version) self.device_string = '' self.update_label = UpdateLabel('') self.total = self.current = self.selected = self.library_total = 0 self.addPermanentWidget(self.update_label) self.update_label.setVisible(False) self._font = QFont() self._font.setBold(True) self.setFont(self._font) self.defmsg = VersionLabel(self) self.defmsg.setFont(self._font) self.addWidget(self.defmsg) self.set_label() def initialize(self, systray=None): self.systray = systray self.notifier = get_notifier(systray) def device_connected(self, devname): self.device_string = _('Connected ') + devname self.set_label() def update_state(self, library_total, total, current, selected): self.library_total = library_total self.total, self.current, self.selected = total, current, selected self.set_label() def set_label(self): try: self._set_label() except: import traceback traceback.print_exc() def _set_label(self): msg = self.base_msg if self.device_string: msg += ' ..::.. ' + self.device_string else: msg += _(' %(created)s %(name)s') % dict(created=_('created by'), name='Kovid Goyal') if self.total != self.current: base = _('%(num)d of %(total)d books') % dict(num=self.current, total=self.total) else: base = ngettext('one book', '{} books', self.total).format(self.total) if self.selected > 0: base = ngettext('%(num)s, %(sel)d selected', '%(num)s, %(sel)d selected', self.selected) % dict( num=base, sel=self.selected) if self.library_total != self.total: base = _('{0}, {1} total').format(base, self.library_total) self.defmsg.setText(u'\xa0%s\xa0\xa0\xa0\xa0[%s]' % (msg, base)) self.clearMessage() def device_disconnected(self): self.device_string = '' self.set_label() def show_message(self, msg, timeout=0, show_notification=True): self.showMessage(msg, timeout) if self.notifier is not None and not config[ 'disable_tray_notification'] and show_notification: if isosx and isinstance(msg, unicode): try: msg = msg.encode(preferred_encoding) except UnicodeEncodeError: msg = msg.encode('utf-8') self.notifier(msg) def clear_message(self): self.clearMessage()
def getFont(self): font = QFont() font.setBold(True) font.setUnderline(True) return font
class WheelScene(QGraphicsScene): def __init__(self, radius=200, parent=None, categories=[], loglevel=logging.DEBUG): super(WheelScene, self).__init__(parent) self.logger = logs.build_logger(__name__, loglevel) self.loglevel = loglevel #super(TestWheelScene, self).__init__(parent) families = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] if len(categories) == 0: sector_names = [ "freespin", "bankruptcy", "dogs", "cats", "PC", "OP", "loseturn", "colors", "numbers", "states", "countries", "people" ] else: sector_names = categories self.radius = radius total = 0 set_angle = 0 count1 = 0 colours = [] total = sum(families) for count in range(len(families)): number = [] for count in range(3): number.append(random.randrange(0, 255)) colours.append(QColor(number[0], number[1], number[2])) for i, family in enumerate(families): # Max span is 5760, so we have to calculate corresponding span angle angle = round(float(family * 5760) / total) subangle = round(float((family * 5760) / total / 2)) subangle += 120 # ellipse = QGraphicsEllipseItem(0,0,400,400) ellipse = QGraphicsEllipseItem(0, 0, self.radius * 2, self.radius * 2) #ellipse.setPos(QPointF(0, 0)) # ellipse.setPos(ellipse.boundingRect().center()) #print(ellipse.rect().center()) #ellipse.setTransformOriginPoint(200, 200) # ellipse.setRect(-200,200,400,400) ellipse.setStartAngle(set_angle) ellipse.setSpanAngle(angle) ellipse.setBrush(colours[count1]) # https://stackoverflow.com/questions/3312016/text-in-a-qgraphicsscene text = QGraphicsTextItem() #text.setFont(QFont("Helvetica", 65)) #text.setTextWidth(20) # print("angle=%s, set_angle=%s, sector_name=%s" % (angle, set_angle, sector_names[i])) document = QtGui.QTextDocument() document.setDocumentMargin(0) text.setDocument(document) self.font = QFont() self.default_font_size = 14 self.font.setPointSize(self.default_font_size) self.font.setBold(True) self.font.setFamily("Helvetica") self.font.setCapitalization(True) text.setPlainText(textwrap.fill(sector_names[i], width=1)) text.setFont(self.font) # print("render_piece label=%s, textangle=%s sub_angle=%s, set_angle=%s, angle=%s" % # (sector_names[i], # ((set_angle+subangle)/5760)*360, # subangle, # set_angle, # angle)) #print("textpos=%s" % ellipse.rect().center()) reduction_factor = 0 while text.boundingRect().height() > self.radius - (self.radius * .01): # print("category=%s, real_height=%s, radius=%s" % (sector_names[i], # text.boundingRect().height(), # self.radius)) # print("trying changing reduction factor from %s to %s" % (reduction_factor, # reduction_factor + 1)) reduction_factor += 1 self.font.setPointSize(self.default_font_size - reduction_factor) text.deleteLater() text = None text = QGraphicsTextItem() text.setDocument(document) #text.setFont(QFont("Helvetica", 65- reduction_factor)) text.setPlainText(textwrap.fill(sector_names[i], width=1)) text.setFont(self.font) text.setZValue(2) if sector_names[i] == False: #scrap this part for now until we can figure out how to safely offset titles. # print("ellipse center=%s" % ellipse.rect().center()) hypotenuse = self.radius * .01 degree_subangle = (((set_angle + subangle) / 5760) * 360) degree_subangle += 90 degree_subangle = degree_subangle % 360 doTranslate = True if doTranslate: #math.cos(degree_subangle) = adjacent/hypotenuse x = math.cos(degree_subangle) * hypotenuse y = math.sin(degree_subangle) * hypotenuse extra = True if extra: if degree_subangle > 0. and degree_subangle < 90.: pass elif degree_subangle > 90 and degree_subangle < 180: pass elif degree_subangle > 180 and degree_subangle < 270: pass #y = -y elif degree_subangle > 270: pass target = ellipse.rect().center() target.setX(x + target.x()) target.setY(y + target.y()) # print("target=%s" % target) print( "do_move_text_offset cat=%s, offset=%s degree_subangle=%s, x=%s, y=%s" % (sector_names[i], target, degree_subangle, x, y)) text.setPos(target) else: text.setPos(ellipse.rect().center()) text.setPos(ellipse.rect().center()) self.logger.debug("ellipse rect: %s" % ellipse.rect()) text.setRotation((((set_angle + subangle) / 5760) * 360)) #text.setRotation(30) # set_angle+=1 set_angle += angle count1 += 1 self.addItem(ellipse) self.addItem(text) self.setSceneRect(0, 0, self.radius * 2, self.radius * 2) self.logger.debug("scenesize= %s" % self.sceneRect())
class HeaderView(QHeaderView): # {{{ def __init__(self, *args): QHeaderView.__init__(self, *args) self.hover = -1 self.current_font = QFont(self.font()) self.current_font.setBold(True) self.current_font.setItalic(True) self.fm = QFontMetrics(self.current_font) def event(self, e): if e.type() in (e.HoverMove, e.HoverEnter): self.hover = self.logicalIndexAt(e.pos()) elif e.type() in (e.Leave, e.HoverLeave): self.hover = -1 return QHeaderView.event(self, e) def sectionSizeFromContents(self, logical_index): self.ensurePolished() opt = QStyleOptionHeader() self.initStyleOption(opt) opt.section = logical_index opt.orientation = self.orientation() opt.fontMetrics = self.fm model = self.parent().model() opt.text = unicode(model.headerData(logical_index, opt.orientation, Qt.DisplayRole) or '') if opt.orientation == Qt.Vertical: try: val = model.headerData(logical_index, opt.orientation, Qt.DecorationRole) if val is not None: opt.icon = val opt.iconAlignment = Qt.AlignVCenter except (IndexError, ValueError, TypeError): pass if self.isSortIndicatorShown(): opt.sortIndicator = QStyleOptionHeader.SortDown return self.style().sizeFromContents(QStyle.CT_HeaderSection, opt, QSize(), self) def paintSection(self, painter, rect, logical_index): opt = QStyleOptionHeader() self.initStyleOption(opt) opt.rect = rect opt.section = logical_index opt.orientation = self.orientation() opt.textAlignment = Qt.AlignHCenter | Qt.AlignVCenter opt.fontMetrics = self.fm model = self.parent().model() style = self.style() margin = 2 * style.pixelMetric(style.PM_HeaderMargin, None, self) if self.isSortIndicatorShown() and self.sortIndicatorSection() == logical_index: opt.sortIndicator = QStyleOptionHeader.SortDown if self.sortIndicatorOrder() == Qt.AscendingOrder else QStyleOptionHeader.SortUp margin += style.pixelMetric(style.PM_HeaderMarkSize, None, self) opt.text = unicode(model.headerData(logical_index, opt.orientation, Qt.DisplayRole) or '') if self.textElideMode() != Qt.ElideNone: opt.text = opt.fontMetrics.elidedText(opt.text, Qt.ElideRight, rect.width() - margin) if self.isEnabled(): opt.state |= QStyle.State_Enabled if self.window().isActiveWindow(): opt.state |= QStyle.State_Active if self.hover == logical_index: opt.state |= QStyle.State_MouseOver sm = self.selectionModel() if opt.orientation == Qt.Vertical: try: val = model.headerData(logical_index, opt.orientation, Qt.DecorationRole) if val is not None: opt.icon = val opt.iconAlignment = Qt.AlignVCenter except (IndexError, ValueError, TypeError): pass if sm.isRowSelected(logical_index, QModelIndex()): opt.state |= QStyle.State_Sunken painter.save() if ( (opt.orientation == Qt.Horizontal and sm.currentIndex().column() == logical_index) or (opt.orientation == Qt.Vertical and sm.currentIndex().row() == logical_index)): painter.setFont(self.current_font) self.style().drawControl(QStyle.CE_Header, opt, painter, self) painter.restore()
def drawGridX(self, painter): rect = self.getRectPlotter() if not rect.isValid(): return painter.save() painter.setFont(self._fontGrid) painter.setPen(QPen(Qt.black)) painter.setBrush(Qt.NoBrush) # Axis ticks dxmsec = rect.width() / self._plotterScale.Scale.X.span() fontMetricsF = QFontMetricsF(self._fontGrid) index = findIndex(fontMetricsF.width(' 00:00 ') / dxmsec, BasePlotter._mSecs_for_grid) msec0 = self._plotterScale.Scale.X.Min offset = findOffset(msec0, BasePlotter._mSecs_for_grid[index]) dx = dxmsec * BasePlotter._mSecs_for_grid[index] step = BasePlotter._mSecs_for_grid[index] for x in numpy.arange(rect.left() - dxmsec * offset, rect.right(), dx): if x < rect.left(): continue painter.save() heightTick = 3 datetimelabel = QDateTime.fromMSecsSinceEpoch(msec0, Qt.UTC) if step >= 60000: # больше минуты if step >= 86400000: # больше суток label = datetimelabel.toString('dd.MM.yy') if datetimelabel.time().minute() == 0: heightTick = 8 painter.setPen(Qt.SolidLine) else: t0 = QDateTime.fromMSecsSinceEpoch(self._plotterScale.Scale.X.Min, Qt.UTC) t1 = QDateTime.fromMSecsSinceEpoch(self._plotterScale.Scale.X.Max, Qt.UTC) if (datetimelabel.time().hour() == 0 and datetimelabel.time().minute() == 0) and ( t1.date() > t0.date()): # Метка даты. Появляется,если листаем график между днями font = QFont(painter.font().family(), painter.font().pointSize()) font.setBold(True) painter.setFont(font) label = datetimelabel.toString('dd.MM.yy') else: label = datetimelabel.toString('hh:mm') if datetimelabel.time().minute() == 0: heightTick = 8 painter.setPen(Qt.SolidLine) if datetimelabel.time().minute() == 30: heightTick = 6 painter.setPen(Qt.DashLine) if datetimelabel.time().minute() == 15 or datetimelabel.time().minute() == 45: heightTick = 4 painter.setPen(Qt.DashLine) else: label = datetimelabel.time().toString('hh:mm:ss') painter.drawLine(x, rect.bottom(), x, rect.top()) rect_label = QRectF(x - fontMetricsF.width(label) / 2, rect.bottom() + heightTick, fontMetricsF.width(label), 2 * fontMetricsF.height()) painter.drawLine(x, rect.bottom() - heightTick, x, rect.bottom() + heightTick) painter.drawText(rect_label, label) msec0 += step painter.restore() painter.restore()
class CompareSingle(QWidget): def __init__( self, field_metadata, parent=None, revert_tooltip=None, datetime_fmt='MMMM yyyy', blank_as_equal=True, fields=('title', 'authors', 'series', 'tags', 'rating', 'publisher', 'pubdate', 'identifiers', 'languages', 'comments', 'cover'), db=None): QWidget.__init__(self, parent) self.l = l = QGridLayout() l.setContentsMargins(0, 0, 0, 0) self.setLayout(l) revert_tooltip = revert_tooltip or _('Revert %s') self.current_mi = None self.changed_font = QFont(QApplication.font()) self.changed_font.setBold(True) self.changed_font.setItalic(True) self.blank_as_equal = blank_as_equal self.widgets = OrderedDict() row = 0 for field in fields: m = field_metadata[field] dt = m['datatype'] extra = None if 'series' in {field, dt}: cls = SeriesEdit elif field == 'identifiers': cls = IdentifiersEdit elif field == 'languages': cls = LanguagesEdit elif 'comments' in {field, dt}: cls = CommentsEdit elif 'rating' in {field, dt}: cls = RatingsEdit elif dt == 'datetime': extra = datetime_fmt cls = DateEdit elif field == 'cover': cls = CoverView elif dt in {'text', 'enum'}: cls = LineEdit else: continue neww = cls(field, True, self, m, extra) neww.changed.connect(partial(self.changed, field)) if isinstance(neww, EditWithComplete): try: neww.update_items_cache(db.new_api.all_field_names(field)) except ValueError: pass # A one-one field like title if isinstance(neww, SeriesEdit): neww.set_db(db.new_api) oldw = cls(field, False, self, m, extra) newl = QLabel('&%s:' % m['name']) newl.setBuddy(neww) button = RightClickButton(self) button.setIcon(QIcon(I('back.png'))) button.clicked.connect(partial(self.revert, field)) button.setToolTip(revert_tooltip % m['name']) if field == 'identifiers': button.m = m = QMenu(button) button.setMenu(m) button.setPopupMode(QToolButton.DelayedPopup) m.addAction(button.toolTip()).triggered.connect(button.click) m.actions()[0].setIcon(button.icon()) m.addAction(_('Merge identifiers')).triggered.connect(self.merge_identifiers) m.actions()[1].setIcon(QIcon(I('merge.png'))) elif field == 'tags': button.m = m = QMenu(button) button.setMenu(m) button.setPopupMode(QToolButton.DelayedPopup) m.addAction(button.toolTip()).triggered.connect(button.click) m.actions()[0].setIcon(button.icon()) m.addAction(_('Merge tags')).triggered.connect(self.merge_tags) m.actions()[1].setIcon(QIcon(I('merge.png'))) self.widgets[field] = Widgets(neww, oldw, newl, button) for i, w in enumerate((newl, neww, button, oldw)): c = i if i < 2 else i + 1 if w is oldw: c += 1 l.addWidget(w, row, c) row += 1 self.sep = f = QFrame(self) f.setFrameShape(f.VLine) l.addWidget(f, 0, 2, row, 1) self.sep2 = f = QFrame(self) f.setFrameShape(f.VLine) l.addWidget(f, 0, 4, row, 1) if 'comments' in self.widgets and not gprefs.get('diff_widget_show_comments_controls', True): self.widgets['comments'].new.hide_toolbars() def save_comments_controls_state(self): if 'comments' in self.widgets: vis = self.widgets['comments'].new.toolbars_visible if vis != gprefs.get('diff_widget_show_comments_controls', True): gprefs.set('diff_widget_show_comments_controls', vis) def changed(self, field): w = self.widgets[field] if not w.new.same_as(w.old) and (not self.blank_as_equal or not w.new.is_blank): w.label.setFont(self.changed_font) else: w.label.setFont(QApplication.font()) def revert(self, field): widgets = self.widgets[field] neww, oldw = widgets[:2] neww.current_val = oldw.current_val def merge_identifiers(self): widgets = self.widgets['identifiers'] neww, oldw = widgets[:2] val = neww.as_dict val.update(oldw.as_dict) neww.as_dict = val def merge_tags(self): widgets = self.widgets['tags'] neww, oldw = widgets[:2] val = oldw.value lval = {icu_lower(x) for x in val} extra = [x for x in neww.value if icu_lower(x) not in lval] if extra: neww.value = val + extra def __call__(self, oldmi, newmi): self.current_mi = newmi self.initial_vals = {} for field, widgets in self.widgets.iteritems(): widgets.old.from_mi(oldmi) widgets.new.from_mi(newmi) self.initial_vals[field] = widgets.new.current_val def apply_changes(self): changed = False for field, widgets in self.widgets.iteritems(): val = widgets.new.current_val if val != self.initial_vals[field]: widgets.new.to_mi(self.current_mi) changed = True return changed
def mark_item_as_current(self, item): font = QFont(self.font()) font.setItalic(True) font.setBold(True) item.setData(0, Qt.FontRole, font)
class EmailAccounts(QAbstractTableModel): # {{{ def __init__(self, accounts, subjects, aliases={}): QAbstractTableModel.__init__(self) self.accounts = accounts self.subjects = subjects self.aliases = aliases self.account_order = sorted(self.accounts.keys()) self.headers = map(unicode, [_("Email"), _("Formats"), _("Subject"), _("Auto send"), _("Alias")]) self.default_font = QFont() self.default_font.setBold(True) self.default_font = self.default_font self.tooltips = [None] + list( map( unicode, map( textwrap.fill, [ _("Formats to email. The first matching format will be sent."), _( "Subject of the email to use when sending. When left blank " "the title will be used for the subject. Also, the same " 'templates used for "Save to disk" such as {title} and ' "{author_sort} can be used here." ), "<p>" + _( "If checked, downloaded news will be automatically " "mailed <br>to this email address " "(provided it is in one of the listed formats)." ), _("Friendly name to use for this email address"), ], ), ) ) def rowCount(self, *args): return len(self.account_order) def columnCount(self, *args): return len(self.headers) def headerData(self, section, orientation, role): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.headers[section] return None def data(self, index, role): row, col = index.row(), index.column() if row < 0 or row >= self.rowCount(): return None account = self.account_order[row] if account not in self.accounts: return None if role == Qt.UserRole: return (account, self.accounts[account]) if role == Qt.ToolTipRole: return self.tooltips[col] if role in [Qt.DisplayRole, Qt.EditRole]: if col == 0: return account if col == 1: return self.accounts[account][0] if col == 2: return self.subjects.get(account, "") if col == 4: return self.aliases.get(account, "") if role == Qt.FontRole and self.accounts[account][2]: return self.default_font if role == Qt.CheckStateRole and col == 3: return Qt.Checked if self.accounts[account][1] else Qt.Unchecked return None def flags(self, index): if index.column() == 3: return QAbstractTableModel.flags(self, index) | Qt.ItemIsUserCheckable else: return QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable def setData(self, index, value, role): if not index.isValid(): return False row, col = index.row(), index.column() account = self.account_order[row] if col == 3: self.accounts[account][1] ^= True elif col == 2: self.subjects[account] = unicode(value or "") elif col == 4: self.aliases.pop(account, None) aval = unicode(value or "").strip() if aval: self.aliases[account] = aval elif col == 1: self.accounts[account][0] = unicode(value or "").upper() elif col == 0: na = unicode(value or "") from email.utils import parseaddr addr = parseaddr(na)[-1] if not addr: return False self.accounts[na] = self.accounts.pop(account) self.account_order[row] = na if "@kindle.com" in addr: self.accounts[na][0] = "AZW, MOBI, TPZ, PRC, AZW1" self.dataChanged.emit(self.index(index.row(), 0), self.index(index.row(), 3)) return True def make_default(self, index): if index.isValid(): self.beginResetModel() row = index.row() for x in self.accounts.values(): x[2] = False self.accounts[self.account_order[row]][2] = True self.endResetModel() def add(self): x = _("new email address") y = x c = 0 while y in self.accounts: c += 1 y = x + str(c) auto_send = len(self.accounts) < 1 self.beginResetModel() self.accounts[y] = ["MOBI, EPUB", auto_send, len(self.account_order) == 0] self.account_order = sorted(self.accounts.keys()) self.endResetModel() return self.index(self.account_order.index(y), 0) def remove(self, index): if index.isValid(): row = index.row() account = self.account_order[row] self.accounts.pop(account) self.account_order = sorted(self.accounts.keys()) has_default = False for account in self.account_order: if self.accounts[account][2]: has_default = True break if not has_default and self.account_order: self.accounts[self.account_order[0]][2] = True self.beginResetModel() self.endResetModel()
def __init__(self, mygui, myguidb, mymainprefs, myparam_dict, myuiexit, mysavedialoggeometry): super(LibraryCodesTab, self).__init__() #----------------------------------------------------- #----------------------------------------------------- self.gui = mygui #----------------------------------------------------- #----------------------------------------------------- self.guidb = myguidb #----------------------------------------------------- #----------------------------------------------------- self.lib_path = self.gui.library_view.model().db.library_path #----------------------------------------------------- #----------------------------------------------------- self.mytabprefs = mymainprefs #----------------------------------------------------- #----------------------------------------------------- self.param_dict = myparam_dict #----------------------------------------------------- #----------------------------------------------------- self.ui_exit = myuiexit #----------------------------------------------------- #----------------------------------------------------- self.save_dialog_geometry = mysavedialoggeometry #----------------------------------------------------- #----------------------------------------------------- font = QFont() font.setBold(False) font.setPointSize(10) #----------------------------------------------------- self.layout_top = QVBoxLayout() self.layout_top.setSpacing(0) self.layout_top.setAlignment(Qt.AlignLeft) self.setLayout(self.layout_top) #----------------------------------------------------- self.scroll_area_frame = QScrollArea() self.scroll_area_frame.setAlignment(Qt.AlignLeft) self.scroll_area_frame.setWidgetResizable(True) self.scroll_area_frame.ensureVisible(400, 400) self.layout_top.addWidget( self.scroll_area_frame ) # the scroll area is now the child of the parent of self.layout_top # NOTE: the self.scroll_area_frame.setWidget(self.scroll_widget) is at the end of the init() AFTER all children have been created and assigned to a layout... #----------------------------------------------------- self.scroll_widget = QWidget() self.layout_top.addWidget( self.scroll_widget ) # causes automatic reparenting of QWidget to the parent of self.layout_top, which is: self . #----------------------------------------------------- self.layout_frame = QVBoxLayout() self.layout_frame.setSpacing(0) self.layout_frame.setAlignment(Qt.AlignLeft) self.scroll_widget.setLayout( self.layout_frame ) # causes automatic reparenting of any widget later added to self.layout_frame to the parent of self.layout_frame, which is: QWidget . #----------------------------------------------------- self.lc_groupbox = QGroupBox('Settings:') self.lc_groupbox.setMaximumWidth(400) self.lc_groupbox.setToolTip( "<p style='white-space:wrap'>The settings that control 'Library Codes'. Using only ISBN or ISSN or Author/Title, Library Codes for selected books will be derived using the Current Settings." ) self.layout_frame.addWidget(self.lc_groupbox) self.lc_layout = QGridLayout() self.lc_groupbox.setLayout(self.lc_layout) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.spacing0 = QLabel() self.layout_frame.addWidget(self.spacing0) self.spacing0.setMaximumHeight(20) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.button_box = QDialogButtonBox() self.button_box.setOrientation(Qt.Horizontal) self.button_box.setCenterButtons(True) self.layout_frame.addWidget(self.button_box) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.push_button_save_only = QPushButton("Save") self.push_button_save_only.clicked.connect(self.save_settings) self.push_button_save_only.setDefault(True) self.push_button_save_only.setFont(font) self.push_button_save_only.setToolTip( "<p style='white-space:wrap'>Save all user settings.") self.button_box.addButton(self.push_button_save_only, 0) self.push_button_exit_only = QPushButton("Exit") self.push_button_exit_only.clicked.connect(self.exit_only) self.push_button_exit_only.setDefault(False) self.push_button_exit_only.setFont(font) self.push_button_exit_only.setToolTip( "<p style='white-space:wrap'>Exit immediately without saving anything." ) self.button_box.addButton(self.push_button_exit_only, 0) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- r = 4 self.ddc_labelname = QLineEdit(self) self.ddc_labelname.setText(self.mytabprefs['DDC']) self.ddc_labelname.setFont(font) self.ddc_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for DDC.<br><br>See: https://www.oclc.org/dewey/features/summaries.en.html" ) self.ddc_labelname.setMaximumWidth(100) self.lc_layout.addWidget(self.ddc_labelname, r, 0) self.ddc_activate_checkbox = QCheckBox( "Activate 'Dewey Decimal Code' Classification?") self.ddc_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive DDC?") r = r + 1 self.lc_layout.addWidget(self.ddc_activate_checkbox, r, 0) if prefs['DDC_IS_ACTIVE'] == unicode_type(S_TRUE): self.ddc_activate_checkbox.setChecked(True) else: self.ddc_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing1 = QLabel() r = r + 1 self.lc_layout.addWidget(self.spacing1, r, 0) self.spacing1.setMaximumHeight(10) #----------------------------------------------------- self.lcc_labelname = QLineEdit(self) self.lcc_labelname.setText(self.mytabprefs['LCC']) self.lcc_labelname.setFont(font) self.lcc_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for LCC.<br><br>See: http://www.loc.gov/catdir/cpso/lcco/ " ) self.lcc_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.lcc_labelname, r, 0) self.lcc_activate_checkbox = QCheckBox( "Activate 'Library of Congress Code' Classification?") self.lcc_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive LCC?") r = r + 1 self.lc_layout.addWidget(self.lcc_activate_checkbox, r, 0) if prefs['LCC_IS_ACTIVE'] == unicode_type(S_TRUE): self.lcc_activate_checkbox.setChecked(True) else: self.lcc_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing2 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing2, r, 0) self.spacing2.setMaximumHeight(10) #----------------------------------------------------- self.fast_labelname = QLineEdit(self) self.fast_labelname.setText(self.mytabprefs['FAST']) self.fast_labelname.setFont(font) self.fast_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for FAST Tag Values. " ) self.fast_labelname.setMinimumWidth(100) self.fast_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.fast_labelname, r, 0) self.fast_activate_checkbox = QCheckBox("Activate 'FAST' Tags?") self.fast_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive FAST Tags?\ <br><br>Text. Behaves like Tags. Not Names.<br><br>" ) r = r + 1 self.lc_layout.addWidget(self.fast_activate_checkbox, r, 0) if prefs['FAST_IS_ACTIVE'] == unicode_type(S_TRUE): self.fast_activate_checkbox.setChecked(True) else: self.fast_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing6 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing6, r, 0) self.spacing6.setMaximumHeight(10) #----------------------------------------------------- self.oclc_labelname = QLineEdit(self) self.oclc_labelname.setText(self.mytabprefs['OCLC']) self.oclc_labelname.setFont(font) self.oclc_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for OCLC-OWI.<br><br>See: #http://classify.oclc.org/classify2/ " ) self.oclc_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.oclc_labelname, r, 0) self.oclc_activate_checkbox = QCheckBox( "Activate 'Online Computer Library Center' Work ID Code?") self.oclc_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive OCLC-OWI?") r = r + 1 self.lc_layout.addWidget(self.oclc_activate_checkbox, r, 0) if self.mytabprefs['OCLC_IS_ACTIVE'] == unicode_type(S_TRUE): self.oclc_activate_checkbox.setChecked(True) else: self.oclc_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing5 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing5, r, 0) self.spacing5.setMaximumHeight(10) #----------------------------------------------------- self.lc_author_details_labelname = QLineEdit(self) self.lc_author_details_labelname.setText( self.mytabprefs['EXTRA_AUTHOR_DETAILS']) self.lc_author_details_labelname.setFont(font) self.lc_author_details_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for 'LC Extra Author Details'.\ <br><br>Text. Behaves like Tags. Not Names.<br><br>" ) self.lc_author_details_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.lc_author_details_labelname, r, 0) self.lc_author_details_checkbox = QCheckBox( "Activate 'Library Codes Extra Author Details'?") self.lc_author_details_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to add (never delete or replace) any available Tag-like values to this Custom Column if they are associated with the OCLC-OWI Identifier?" ) r = r + 1 self.lc_layout.addWidget(self.lc_author_details_checkbox, r, 0) if self.mytabprefs['EXTRA_AUTHOR_DETAILS_IS_ACTIVE'] == unicode_type( S_TRUE): self.lc_author_details_checkbox.setChecked(True) else: self.lc_author_details_checkbox.setChecked(False) #----------------------------------------------------- #----------------------------------------------------- self.spacing4 = QLabel() r = r + 1 self.lc_layout.addWidget(self.spacing4, r, 0) self.spacing4.setMaximumHeight(10) #----------------------------------------------------- font.setBold(False) font.setPointSize(7) #----------------------------------------------------- self.push_button_autoadd_custom_columns = QPushButton( "Automatically Add Activated Custom Columns?") self.push_button_autoadd_custom_columns.clicked.connect( self.autoadd_custom_columns) self.push_button_autoadd_custom_columns.setDefault(False) self.push_button_autoadd_custom_columns.setFont(font) self.push_button_autoadd_custom_columns.setToolTip( "<p style='white-space:wrap'>Do you want to automatically add the Custom Columns selected above?<br><br>If you have any issues, please add them manually." ) r = r + 4 self.lc_layout.addWidget(self.push_button_autoadd_custom_columns, r, 0) self.push_button_autoadd_custom_columns.setMaximumWidth(250) #----------------------------------------------------- self.lc_custom_columns_generation_label = QLabel() r = r + 1 self.lc_layout.addWidget(self.lc_custom_columns_generation_label, r, 0) self.lc_custom_columns_generation_label.setText( " ") self.lc_custom_columns_generation_label.setMaximumHeight(10) self.lc_custom_columns_generation_label.setFont(font) self.oclc_identifier_only_checkbox = QCheckBox( "Always Create OCLC-OWI as an 'Identifier' (à la ISBN)?") self.oclc_identifier_only_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to update Calibre's Identifiers for an Identifier of 'OCLC-OWI',\ regardless of whether you want its own Custom Column updated?\ <br><br>REQUIRED to derive DDC/LCC using Author/Title." ) r = r + 2 self.lc_layout.addWidget(self.oclc_identifier_only_checkbox, r, 0) if prefs['OCLC_IDENTIFIER'] == unicode_type(S_TRUE): self.oclc_identifier_only_checkbox.setChecked(True) else: self.oclc_identifier_only_checkbox.setChecked(False) #----------------------------------------------------- self.spacing3 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing3, r, 0) self.spacing3.setMaximumHeight(10) #----------------------------------------------------- font.setBold(False) font.setPointSize(10) #----------------------------------------------------- self.lc_genre_labelname = QLineEdit(self) self.lc_genre_labelname.setText(self.mytabprefs['GENRE']) self.lc_genre_labelname.setFont(font) self.lc_genre_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for 'Genre'.\ <br><br>Text. Behaves like Tags.<br><br>" ) self.lc_genre_labelname.setMaximumWidth(100) r = r + 1 self.lc_layout.addWidget(self.lc_genre_labelname, r, 0) self.lc_checkbox_buttongroup = QButtonGroup() self.lc_checkbox_buttongroup.setExclusive(True) self.lc_genre_ddc_checkbox = QCheckBox( "Update 'Genre' using DDC-to-Genre Mappings?") self.lc_genre_ddc_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want LC to update 'Genre' using the DDC-to-Genre mapping in Table _lc_genre_mapping?" ) r = r + 1 self.lc_layout.addWidget(self.lc_genre_ddc_checkbox, r, 0) self.lc_genre_lcc_checkbox = QCheckBox( "Update 'Genre' using LCC-to-Genre Mappings?") self.lc_genre_lcc_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want LC to update 'Genre' using the LCC-to-Genre mapping in Table _lc_genre_mapping?" ) r = r + 1 self.lc_layout.addWidget(self.lc_genre_lcc_checkbox, r, 0) self.lc_genre_inactive_checkbox = QCheckBox( "Do not update 'Genre' at all") self.lc_genre_inactive_checkbox.setToolTip( "<p style='white-space:wrap'>Do no 'Genre' processing at all?") r = r + 1 self.lc_layout.addWidget(self.lc_genre_inactive_checkbox, r, 0) self.lc_checkbox_buttongroup.addButton(self.lc_genre_ddc_checkbox) self.lc_checkbox_buttongroup.addButton(self.lc_genre_lcc_checkbox) self.lc_checkbox_buttongroup.addButton(self.lc_genre_inactive_checkbox) if self.mytabprefs['GENRE_DDC_IS_ACTIVE'] == unicode_type(S_TRUE): self.lc_genre_ddc_checkbox.setChecked(True) elif self.mytabprefs['GENRE_LCC_IS_ACTIVE'] == unicode_type(S_TRUE): self.lc_genre_lcc_checkbox.setChecked(True) elif self.mytabprefs['GENRE_IS_INACTIVE'] == unicode_type(S_TRUE): self.lc_genre_inactive_checkbox.setChecked(True) self.lc_exact_match_checkbox = QCheckBox( "DDC: Require an 'Exact Match', not a 'Best Match'?") self.lc_exact_match_checkbox.setToolTip( "<p style='white-space:wrap'>Check this checkbox if you want an exact DDC match to be required in Table _lc_genre_mapping. Otherwise, a 'best match' will be used via progressive shortening from right to left, but not past any decimal point. If there is no decimal point in a book's DDC, then no progressive shortening will be performed at all." ) r = r + 1 self.lc_layout.addWidget(self.lc_exact_match_checkbox, r, 0) if self.mytabprefs['GENRE_EXACT_MATCH'] == unicode_type(S_TRUE): self.lc_exact_match_checkbox.setChecked(True) self.spin_lcc = QSpinBox(self) self.spin_lcc.setMinimum(1) self.spin_lcc.setMaximum(50) self.spin_lcc.setProperty('value', prefs['GENRE_LCC_MATCH_LENGTH']) self.spin_lcc.setMaximumWidth(250) self.spin_lcc.setSuffix(" LCC: Maximum Length to Match") self.spin_lcc.setToolTip( "<p style='white-space:nowrap'>Maximum number of characters in the LCC that should be used to map to the 'Genre', starting from the left. A maximum of 1 guarantees a (broad) match.\ <br><br>LCCs are structured with either 1 or 2 beginning letters, so 2-character LCCs have special matching logic.\ <br><br>Example: Assume maximum = 2 for a LCC of 'Q1': Q1 would be attempted. If it failed, because the 2nd digit is a number, 'Q' would be attempted.\ <br><br>Example: Assume maximum = 2 for a LCC of 'PN1969.C65': PN would be attempted. If it failed, nothing else would be attempted.\ <br><br>Example: Assume maximum = 4 for a LCC of 'PN1969.C65': PN19 would be attempted. If it failed, nothing else would be attempted.\ <br><br>Example: Assume maximum = 4 for a LCC of 'Q1': Q1 would be attempted. If it failed, because the 2nd digit is a number, 'Q' would be attempted.\ <br><br>Example: Assume maximum = 4 for a LCC of 'Q389': Q389 would be attempted. If it failed, nothing else would be attempted." ) r = r + 2 self.lc_layout.addWidget(self.spin_lcc, r, 0) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.scroll_widget.resize(self.sizeHint()) #----------------------------------------------------- #----------------------------------------------------- self.scroll_area_frame.setWidget( self.scroll_widget ) # now that all widgets have been created and assigned to a layout... #----------------------------------------------------- #----------------------------------------------------- self.scroll_area_frame.resize(self.sizeHint()) #----------------------------------------------------- #----------------------------------------------------- self.resize(self.sizeHint())
class CompareSingle(QWidget): def __init__( self, field_metadata, parent=None, revert_tooltip=None, datetime_fmt='MMMM yyyy', blank_as_equal=True, fields=('title', 'authors', 'series', 'tags', 'rating', 'publisher', 'pubdate', 'identifiers', 'languages', 'comments', 'cover')): QWidget.__init__(self, parent) self.l = l = QGridLayout() l.setContentsMargins(0, 0, 0, 0) self.setLayout(l) revert_tooltip = revert_tooltip or _('Revert %s') self.current_mi = None self.changed_font = QFont(QApplication.font()) self.changed_font.setBold(True) self.changed_font.setItalic(True) self.blank_as_equal = blank_as_equal self.widgets = OrderedDict() row = 0 for field in fields: m = field_metadata[field] dt = m['datatype'] extra = None if 'series' in {field, dt}: cls = SeriesEdit elif field == 'identifiers': cls = IdentifiersEdit elif field == 'languages': cls = LanguagesEdit elif 'comments' in {field, dt}: cls = CommentsEdit elif 'rating' in {field, dt}: cls = RatingsEdit elif dt == 'datetime': extra = datetime_fmt cls = DateEdit elif field == 'cover': cls = CoverView elif dt in {'text', 'enum'}: cls = LineEdit else: continue neww = cls(field, True, self, m, extra) neww.changed.connect(partial(self.changed, field)) oldw = cls(field, False, self, m, extra) newl = QLabel('&%s:' % m['name']) newl.setBuddy(neww) button = QToolButton(self) button.setIcon(QIcon(I('back.png'))) button.clicked.connect(partial(self.revert, field)) button.setToolTip(revert_tooltip % m['name']) self.widgets[field] = Widgets(neww, oldw, newl, button) for i, w in enumerate((newl, neww, button, oldw)): c = i if i < 2 else i + 1 if w is oldw: c += 1 l.addWidget(w, row, c) row += 1 self.sep = f = QFrame(self) f.setFrameShape(f.VLine) l.addWidget(f, 0, 2, row, 1) self.sep2 = f = QFrame(self) f.setFrameShape(f.VLine) l.addWidget(f, 0, 4, row, 1) if 'comments' in self.widgets and not gprefs.get('diff_widget_show_comments_controls', True): self.widgets['comments'].new.hide_toolbars() def save_comments_controls_state(self): if 'comments' in self.widgets: vis = self.widgets['comments'].new.toolbars_visible if vis != gprefs.get('diff_widget_show_comments_controls', True): gprefs.set('diff_widget_show_comments_controls', vis) def changed(self, field): w = self.widgets[field] if not w.new.same_as(w.old) and (not self.blank_as_equal or not w.new.is_blank): w.label.setFont(self.changed_font) else: w.label.setFont(QApplication.font()) def revert(self, field): widgets = self.widgets[field] neww, oldw = widgets[:2] neww.current_val = oldw.current_val def __call__(self, oldmi, newmi): self.current_mi = newmi self.initial_vals = {} for field, widgets in self.widgets.iteritems(): widgets.old.from_mi(oldmi) widgets.new.from_mi(newmi) self.initial_vals[field] = widgets.new.current_val def apply_changes(self): changed = False for field, widgets in self.widgets.iteritems(): val = widgets.new.current_val if val != self.initial_vals[field]: widgets.new.to_mi(self.current_mi) changed = True return changed
class EmailAccounts(QAbstractTableModel): # {{{ def __init__(self, accounts, subjects, aliases={}): QAbstractTableModel.__init__(self) self.accounts = accounts self.subjects = subjects self.aliases = aliases self.account_order = sorted(self.accounts.keys()) self.headers = map(unicode, [ _('Email'), _('Formats'), _('Subject'), _('Auto send'), _('Alias') ]) self.default_font = QFont() self.default_font.setBold(True) self.default_font = (self.default_font) self.tooltips = [None] + list( map( unicode, map(textwrap.fill, [ _('Formats to email. The first matching format will be sent.' ), _('Subject of the email to use when sending. When left blank ' 'the title will be used for the subject. Also, the same ' 'templates used for "Save to disk" such as {title} and ' '{author_sort} can be used here.'), '<p>' + _('If checked, downloaded news will be automatically ' 'mailed <br>to this email address ' '(provided it is in one of the listed formats).'), _('Friendly name to use for this email address') ]))) def rowCount(self, *args): return len(self.account_order) def columnCount(self, *args): return len(self.headers) def headerData(self, section, orientation, role): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.headers[section] return None def data(self, index, role): row, col = index.row(), index.column() if row < 0 or row >= self.rowCount(): return None account = self.account_order[row] if account not in self.accounts: return None if role == Qt.UserRole: return (account, self.accounts[account]) if role == Qt.ToolTipRole: return self.tooltips[col] if role in [Qt.DisplayRole, Qt.EditRole]: if col == 0: return (account) if col == 1: return (self.accounts[account][0]) if col == 2: return (self.subjects.get(account, '')) if col == 4: return (self.aliases.get(account, '')) if role == Qt.FontRole and self.accounts[account][2]: return self.default_font if role == Qt.CheckStateRole and col == 3: return (Qt.Checked if self.accounts[account][1] else Qt.Unchecked) return None def flags(self, index): if index.column() == 3: return QAbstractTableModel.flags(self, index) | Qt.ItemIsUserCheckable else: return QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable def setData(self, index, value, role): if not index.isValid(): return False row, col = index.row(), index.column() account = self.account_order[row] if col == 3: self.accounts[account][1] ^= True elif col == 2: self.subjects[account] = unicode(value or '') elif col == 4: self.aliases.pop(account, None) aval = unicode(value or '').strip() if aval: self.aliases[account] = aval elif col == 1: self.accounts[account][0] = unicode(value or '').upper() elif col == 0: na = unicode(value or '') from email.utils import parseaddr addr = parseaddr(na)[-1] if not addr: return False self.accounts[na] = self.accounts.pop(account) self.account_order[row] = na if '@kindle.com' in addr: self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1' self.dataChanged.emit(self.index(index.row(), 0), self.index(index.row(), 3)) return True def make_default(self, index): if index.isValid(): self.beginResetModel() row = index.row() for x in self.accounts.values(): x[2] = False self.accounts[self.account_order[row]][2] = True self.endResetModel() def add(self): x = _('new email address') y = x c = 0 while y in self.accounts: c += 1 y = x + str(c) auto_send = len(self.accounts) < 1 self.beginResetModel() self.accounts[y] = [ 'MOBI, EPUB', auto_send, len(self.account_order) == 0 ] self.account_order = sorted(self.accounts.keys()) self.endResetModel() return self.index(self.account_order.index(y), 0) def remove(self, index): if index.isValid(): row = index.row() account = self.account_order[row] self.accounts.pop(account) self.account_order = sorted(self.accounts.keys()) has_default = False for account in self.account_order: if self.accounts[account][2]: has_default = True break if not has_default and self.account_order: self.accounts[self.account_order[0]][2] = True self.beginResetModel() self.endResetModel()
class CompareSingle(QWidget): def __init__(self, field_metadata, parent=None, revert_tooltip=None, datetime_fmt='MMMM yyyy', blank_as_equal=True, fields=('title', 'authors', 'series', 'tags', 'rating', 'publisher', 'pubdate', 'identifiers', 'languages', 'comments', 'cover'), db=None): QWidget.__init__(self, parent) self.l = l = QGridLayout() l.setContentsMargins(0, 0, 0, 0) self.setLayout(l) revert_tooltip = revert_tooltip or _('Revert %s') self.current_mi = None self.changed_font = QFont(QApplication.font()) self.changed_font.setBold(True) self.changed_font.setItalic(True) self.blank_as_equal = blank_as_equal self.widgets = OrderedDict() row = 0 for field in fields: m = field_metadata[field] dt = m['datatype'] extra = None if 'series' in {field, dt}: cls = SeriesEdit elif field == 'identifiers': cls = IdentifiersEdit elif field == 'languages': cls = LanguagesEdit elif 'comments' in {field, dt}: cls = CommentsEdit elif 'rating' in {field, dt}: cls = RatingsEdit elif dt == 'datetime': extra = datetime_fmt cls = DateEdit elif field == 'cover': cls = CoverView elif dt in {'text', 'enum'}: cls = LineEdit else: continue neww = cls(field, True, self, m, extra) neww.changed.connect(partial(self.changed, field)) if isinstance(neww, EditWithComplete): try: neww.update_items_cache(db.new_api.all_field_names(field)) except ValueError: pass # A one-one field like title if isinstance(neww, SeriesEdit): neww.set_db(db.new_api) oldw = cls(field, False, self, m, extra) newl = QLabel('&%s:' % m['name']) newl.setBuddy(neww) button = RightClickButton(self) button.setIcon(QIcon(I('back.png'))) button.clicked.connect(partial(self.revert, field)) button.setToolTip(revert_tooltip % m['name']) if field == 'identifiers': button.m = m = QMenu(button) button.setMenu(m) button.setPopupMode(QToolButton.DelayedPopup) m.addAction(button.toolTip()).triggered.connect(button.click) m.actions()[0].setIcon(button.icon()) m.addAction(_('Merge identifiers')).triggered.connect( self.merge_identifiers) m.actions()[1].setIcon(QIcon(I('merge.png'))) elif field == 'tags': button.m = m = QMenu(button) button.setMenu(m) button.setPopupMode(QToolButton.DelayedPopup) m.addAction(button.toolTip()).triggered.connect(button.click) m.actions()[0].setIcon(button.icon()) m.addAction(_('Merge tags')).triggered.connect(self.merge_tags) m.actions()[1].setIcon(QIcon(I('merge.png'))) self.widgets[field] = Widgets(neww, oldw, newl, button) for i, w in enumerate((newl, neww, button, oldw)): c = i if i < 2 else i + 1 if w is oldw: c += 1 l.addWidget(w, row, c) row += 1 self.sep = f = QFrame(self) f.setFrameShape(f.VLine) l.addWidget(f, 0, 2, row, 1) self.sep2 = f = QFrame(self) f.setFrameShape(f.VLine) l.addWidget(f, 0, 4, row, 1) if 'comments' in self.widgets and not gprefs.get( 'diff_widget_show_comments_controls', True): self.widgets['comments'].new.hide_toolbars() def save_comments_controls_state(self): if 'comments' in self.widgets: vis = self.widgets['comments'].new.toolbars_visible if vis != gprefs.get('diff_widget_show_comments_controls', True): gprefs.set('diff_widget_show_comments_controls', vis) def changed(self, field): w = self.widgets[field] if not w.new.same_as(w.old) and (not self.blank_as_equal or not w.new.is_blank): w.label.setFont(self.changed_font) else: w.label.setFont(QApplication.font()) def revert(self, field): widgets = self.widgets[field] neww, oldw = widgets[:2] neww.current_val = oldw.current_val def merge_identifiers(self): widgets = self.widgets['identifiers'] neww, oldw = widgets[:2] val = neww.as_dict val.update(oldw.as_dict) neww.as_dict = val def merge_tags(self): widgets = self.widgets['tags'] neww, oldw = widgets[:2] val = oldw.value lval = {icu_lower(x) for x in val} extra = [x for x in neww.value if icu_lower(x) not in lval] if extra: neww.value = val + extra def __call__(self, oldmi, newmi): self.current_mi = newmi self.initial_vals = {} for field, widgets in self.widgets.iteritems(): widgets.old.from_mi(oldmi) widgets.new.from_mi(newmi) self.initial_vals[field] = widgets.new.current_val def apply_changes(self): changed = False for field, widgets in self.widgets.iteritems(): val = widgets.new.current_val if val != self.initial_vals[field]: widgets.new.to_mi(self.current_mi) changed = True return changed