class BlockingBusy(QDialog): def __init__(self, msg, parent=None, window_title=_('Working')): QDialog.__init__(self, parent) self._layout = QVBoxLayout() self.setLayout(self._layout) self.msg = QLabel(msg) # self.msg.setWordWrap(True) self.font = QFont() self.font.setPointSize(self.font.pointSize() + 8) self.msg.setFont(self.font) self.pi = ProgressIndicator(self) self.pi.setDisplaySize(100) self._layout.addWidget(self.pi, 0, Qt.AlignHCenter) self._layout.addSpacing(15) self._layout.addWidget(self.msg, 0, Qt.AlignHCenter) self.start() self.setWindowTitle(window_title) self.resize(self.sizeHint()) def start(self): self.pi.startAnimation() def stop(self): self.pi.stopAnimation() def accept(self): self.stop() return QDialog.accept(self) def reject(self): pass # Cannot cancel this dialog
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
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
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', '')))
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, ''))
def __init__(self, log, icon): ''' :param log: The text to show in the dialog :param icon: The window icon ''' QDialog.__init__(self) self.setWindowTitle(_('Prince log')) self.setWindowIcon(icon) self.l = QVBoxLayout() self.setLayout(self.l) monofont = QFont('') monofont.setStyleHint(QFont.TypeWriter) self.box = QPlainTextEdit() self.box.setPlainText(log) self.box.setStyleSheet('* { font-family: monospace }') self.box.setMinimumWidth(500) self.box.setLineWrapMode(QPlainTextEdit.NoWrap) self.box.setReadOnly(True) self.box.setToolTip(_('<qt>Console output from the last Prince run</qt>')) self.l.addWidget(self.box) self.buttons = QDialogButtonBox(QDialogButtonBox.Ok) self.l.addWidget(self.buttons) self.buttons.accepted.connect(self.accept) self.adjustSize()
class RatingDelegate(QStyledItemDelegate): # {{{ def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.rf = QFont(rating_font()) self.em = Qt.ElideMiddle delta = 0 if iswindows and sys.getwindowsversion().major >= 6: delta = 2 self.rf.setPointSize(QFontInfo(QApplication.font()).pointSize()+delta) def createEditor(self, parent, option, index): sb = QStyledItemDelegate.createEditor(self, parent, option, index) sb.setMinimum(0) sb.setMaximum(5) sb.setSuffix(' ' + _('stars')) return sb def displayText(self, value, locale): r = int(value) if r < 0 or r > 5: r = 0 return u'\u2605'*r def sizeHint(self, option, index): option.font = self.rf option.textElideMode = self.em return QStyledItemDelegate.sizeHint(self, option, index) def paint(self, painter, option, index): option.font = self.rf option.textElideMode = self.em return QStyledItemDelegate.paint(self, painter, option, index)
def __init__(self, parent, recipe_model): ResizableDialog.__init__(self, parent) self._model = self.model = CustomRecipeModel(recipe_model) self.available_profiles.setModel(self._model) self.available_profiles.currentChanged = self.current_changed f = QFont() f.setStyleHint(f.Monospace) self.source_code.setFont(f) self.remove_feed_button.clicked[(bool)].connect(self.added_feeds.remove_selected_items) self.remove_profile_button.clicked[(bool)].connect(self.remove_selected_items) self.add_feed_button.clicked[(bool)].connect(self.add_feed) self.load_button.clicked.connect(self.load) self.opml_button.clicked.connect(self.opml_import) self.builtin_recipe_button.clicked.connect(self.add_builtin_recipe) self.share_button.clicked.connect(self.share) self.show_recipe_files_button.clicked.connect(self.show_recipe_files) self.down_button.clicked.connect(self.down) self.up_button.clicked.connect(self.up) self.add_profile_button.clicked[(bool)].connect(self.add_profile) self.feed_url.returnPressed[()].connect(self.add_feed) self.feed_title.returnPressed[()].connect(self.add_feed) self.toggle_mode_button.clicked[(bool)].connect(self.toggle_mode) self.clear()
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 __init__ (self,parent=None): super(Title,self).__init__(parent) self.setAlignment(QtCore.Qt.AlignCenter) font = QFont() #font.setFamily("Nyala") font.setFamily("Old English Text MT") font.setPointSize(24) self.setFont(font)
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 message_image(text, width=500, height=400, font_size=20): init_environment() img = QImage(width, height, QImage.Format_ARGB32) img.fill(Qt.white) p = QPainter(img) f = QFont() f.setPixelSize(font_size) p.setFont(f) r = img.rect().adjusted(10, 10, -10, -10) p.drawText(r, Qt.AlignJustify | Qt.AlignVCenter | Qt.TextWordWrap, text) p.end() return pixmap_to_data(img)
def __init__(self, parent, icon_name, title): QHBoxLayout.__init__(self) self.title_image_label = QLabel(parent) self.update_title_icon(icon_name) self.addWidget(self.title_image_label) title_font = QFont() title_font.setPointSize(16) shelf_label = QLabel(title, parent) shelf_label.setFont(title_font) self.addWidget(shelf_label) self.insertStretch(-1)
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)
def popup(self, select_first=True): if self.disable_popup: return p = self m = p.model() widget = self.completer_widget() if widget is None: return screen = QApplication.desktop().availableGeometry(widget) h = (p.sizeHintForRow(0) * min(self.max_visible_items, m.rowCount()) + 3) + 3 hsb = p.horizontalScrollBar() if hsb and hsb.isVisible(): h += hsb.sizeHint().height() rh = widget.height() pos = widget.mapToGlobal(QPoint(0, widget.height() - 2)) w = min(widget.width(), screen.width()) if (pos.x() + w) > (screen.x() + screen.width()): pos.setX(screen.x() + screen.width() - w) if pos.x() < screen.x(): pos.setX(screen.x()) top = pos.y() - rh - screen.top() + 2 bottom = screen.bottom() - pos.y() h = max(h, p.minimumHeight()) if h > bottom: h = min(max(top, bottom), h) if top > bottom: pos.setY(pos.y() - h - rh + 2) p.setGeometry(pos.x(), pos.y(), w, h) if ( tweaks["preselect_first_completion"] and select_first and not self.currentIndex().isValid() and self.model().rowCount() > 0 ): self.setCurrentIndex(self.model().index(0)) if not p.isVisible(): if isosx and get_osx_version() >= (10, 9, 0): # On mavericks the popup menu seems to use a font smaller than # the widgets font, see for example: # https://bugs.launchpad.net/bugs/1243761 fp = QFontInfo(widget.font()) f = QFont() f.setPixelSize(fp.pixelSize()) self.setFont(f) p.show()
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 RatingDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{ def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.table_widget = args[0] self.rf = QFont(rating_font()) self.em = Qt.ElideMiddle delta = 0 if iswindows and sys.getwindowsversion().major >= 6: delta = 2 self.rf.setPointSize(QFontInfo(QApplication.font()).pointSize()+delta) def createEditor(self, parent, option, index): sb = QSpinBox(parent) sb.setMinimum(0) sb.setMaximum(5) sb.setSuffix(' ' + _('stars')) sb.setSpecialValueText(_('Not rated')) return sb def get_required_width(self, editor, style, fm): val = editor.maximum() text = editor.textFromValue(val) + editor.suffix() srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft, False, text + u'M') return srect.width() def displayText(self, value, locale): r = int(value) if r < 0 or r > 5: r = 0 return u'\u2605'*r def setEditorData(self, editor, index): if check_key_modifier(Qt.ControlModifier): val = 0 else: val = index.data(Qt.EditRole) editor.setValue(val) def sizeHint(self, option, index): option.font = self.rf option.textElideMode = self.em return QStyledItemDelegate.sizeHint(self, option, index) def paint(self, painter, option, index): option.font = self.rf option.textElideMode = self.em return QStyledItemDelegate.paint(self, painter, option, index)
def populate(self, phrase, ts, process_space=True): phrase_pos = 0 processed = False matches = self.__class__.whitespace.finditer(phrase) font = QFont(ts.font) if self.valign is not None: font.setPixelSize(font.pixelSize()/1.5) fm = QFontMetrics(font) single_space_width = fm.width(' ') height, descent = fm.height(), fm.descent() for match in matches: processed = True left, right = match.span() if not process_space: right = left space_width = single_space_width * (right-left) word = phrase[phrase_pos:left] width = fm.width(word) if self.current_width + width < self.line_length: self.commit(word, width, height, descent, ts, font) if space_width > 0 and self.current_width + space_width < self.line_length: self.add_space(space_width) phrase_pos = right continue # Word doesn't fit on line if self.hyphenate and len(word) > 3: tokens = hyphenate_word(word) for i in range(len(tokens)-2, -1, -1): word = ''.join(tokens[0:i+1])+'-' width = fm.width(word) if self.current_width + width < self.line_length: self.commit(word, width, height, descent, ts, font) return phrase_pos + len(word)-1, True if self.current_width < 5: # Force hyphenation as word is longer than line for i in range(len(word)-5, 0, -5): part = word[:i] + '-' width = fm.width(part) if self.current_width + width < self.line_length: self.commit(part, width, height, descent, ts, font) return phrase_pos + len(part)-1, True # Failed to add word. return phrase_pos, True if not processed: return self.populate(phrase+' ', ts, False) return phrase_pos, False
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
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 apply_theme(self, theme): self.theme = theme pal = self.palette() pal.setColor(pal.Base, theme_color(theme, 'Normal', 'bg')) pal.setColor(pal.AlternateBase, theme_color(theme, 'CursorLine', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'Normal', 'fg')) pal.setColor(pal.Highlight, theme_color(theme, 'Visual', 'bg')) pal.setColor(pal.HighlightedText, theme_color(theme, 'Visual', 'fg')) self.setPalette(pal) self.tooltip_palette = pal = QPalette() pal.setColor(pal.ToolTipBase, theme_color(theme, 'Tooltip', 'bg')) pal.setColor(pal.ToolTipText, theme_color(theme, 'Tooltip', 'fg')) self.line_number_palette = pal = QPalette() pal.setColor(pal.Base, theme_color(theme, 'LineNr', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'LineNr', 'fg')) pal.setColor(pal.BrightText, theme_color(theme, 'LineNrC', 'fg')) self.match_paren_format = theme_format(theme, 'MatchParen') font = self.font() ff = tprefs['editor_font_family'] if ff is None: ff = default_font_family() font.setFamily(ff) font.setPointSize(tprefs['editor_font_size']) self.tooltip_font = QFont(font) self.tooltip_font.setPointSize(font.pointSize() - 1) self.setFont(font) self.highlighter.apply_theme(theme) w = self.fontMetrics() self.number_width = max(map(lambda x:w.width(str(x)), xrange(10))) self.size_hint = QSize(self.expected_geometry[0] * w.averageCharWidth(), self.expected_geometry[1] * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line()
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 __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 __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 __init__(self, args, ids, db, refresh_books, cc_widgets, s_r_func, do_sr, sr_calls, parent=None, window_title=_('Working')): QDialog.__init__(self, parent) self._layout = l = QVBoxLayout() self.setLayout(l) self.msg = QLabel(_('Processing %d books, please wait...') % len(ids)) self.font = QFont() self.font.setPointSize(self.font.pointSize() + 8) self.msg.setFont(self.font) self.pi = ProgressIndicator(self) self.pi.setDisplaySize(100) self._layout.addWidget(self.pi, 0, Qt.AlignHCenter) self._layout.addSpacing(15) self._layout.addWidget(self.msg, 0, Qt.AlignHCenter) self.setWindowTitle(window_title + '...') self.setMinimumWidth(200) self.resize(self.sizeHint()) self.error = None self.all_done.connect(self.on_all_done, type=Qt.QueuedConnection) self.args, self.ids = args, ids self.db, self.cc_widgets = db, cc_widgets self.s_r_func = FunctionDispatcher(s_r_func) self.do_sr = do_sr self.sr_calls = sr_calls self.refresh_books = refresh_books
def apply_theme(self, theme): self.theme = theme pal = self.palette() pal.setColor(pal.Base, theme_color(theme, "Normal", "bg")) pal.setColor(pal.AlternateBase, theme_color(theme, "CursorLine", "bg")) pal.setColor(pal.Text, theme_color(theme, "Normal", "fg")) pal.setColor(pal.Highlight, theme_color(theme, "Visual", "bg")) pal.setColor(pal.HighlightedText, theme_color(theme, "Visual", "fg")) self.setPalette(pal) self.tooltip_palette = pal = QPalette() pal.setColor(pal.ToolTipBase, theme_color(theme, "Tooltip", "bg")) pal.setColor(pal.ToolTipText, theme_color(theme, "Tooltip", "fg")) self.line_number_palette = pal = QPalette() pal.setColor(pal.Base, theme_color(theme, "LineNr", "bg")) pal.setColor(pal.Text, theme_color(theme, "LineNr", "fg")) pal.setColor(pal.BrightText, theme_color(theme, "LineNrC", "fg")) self.match_paren_format = theme_format(theme, "MatchParen") font = self.font() ff = tprefs["editor_font_family"] if ff is None: ff = default_font_family() font.setFamily(ff) font.setPointSize(tprefs["editor_font_size"]) self.tooltip_font = QFont(font) self.tooltip_font.setPointSize(font.pointSize() - 1) self.setFont(font) self.highlighter.apply_theme(theme) w = self.fontMetrics() self.number_width = max(map(lambda x: w.width(str(x)), xrange(10))) self.size_hint = QSize(self.expected_geometry[0] * w.averageCharWidth(), self.expected_geometry[1] * w.height()) self.highlight_color = theme_color(theme, "HighlightRegion", "bg") self.highlight_cursor_line() self.completion_popup.clear_caches(), self.completion_popup.update()
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 }') self.bar.setIconSize(QSize(32, 32)) 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, 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) w.setAutoRaise(True) w.setMinimumWidth(100)
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)
def key_press_event(self, ev, which=0): code = ev.key() if self.capture == 0 or code in (0, Qt.Key_unknown, Qt.Key_Shift, Qt.Key_Control, Qt.Key_Alt, Qt.Key_Meta, Qt.Key_AltGr, Qt.Key_CapsLock, Qt.Key_NumLock, Qt.Key_ScrollLock): return QWidget.keyPressEvent(self, ev) button = getattr(self, 'button%d' % which) font = QFont() button.setFont(font) sequence = QKeySequence(code | (int(ev.modifiers()) & ~Qt.KeypadModifier)) button.setText(sequence.toString(QKeySequence.NativeText)) self.capture = 0 setattr(self, 'shortcut%d' % which, sequence) dup_desc = self.dup_check(sequence, self.key) if dup_desc is not None: error_dialog(self, _('Already assigned'), unicode(sequence.toString(QKeySequence.NativeText)) + ' ' + _('already assigned to') + ' ' + dup_desc, show=True) self.clear_clicked(which=which)
def drawTowerPrices(self): font = QFont("Arial",15) arrowtower = QLabel("50") arrowtower.setFont(font) arrowtower.move(QPoint(650,70)) self.scene.addWidget(arrowtower) guntower = QLabel("100") guntower.setFont(font) guntower.move(QPoint(650,150)) self.scene.addWidget(guntower) lasertower = QLabel("500") lasertower.setFont(font) lasertower.move(QPoint(650,230)) self.scene.addWidget(lasertower)
def set_value(self, g, val): from calibre.gui2.convert.xpath_wizard import XPathEdit from calibre.gui2.convert.regex_builder import RegexEdit from calibre.gui2.widgets import EncodingComboBox if self.set_value_handler(g, val): return if isinstance(g, (QSpinBox, QDoubleSpinBox)): g.setValue(val) elif isinstance(g, (QLineEdit, QTextEdit, QPlainTextEdit)): if not val: val = '' getattr(g, 'setPlainText', getattr(g, 'setText', None))(val) getattr(g, 'setCursorPosition', lambda x: x)(0) elif isinstance(g, QFontComboBox): g.setCurrentFont(QFont(val or '')) elif isinstance(g, FontFamilyChooser): g.font_family = val elif isinstance(g, EncodingComboBox): if val: g.setEditText(val) else: g.setCurrentIndex(0) elif isinstance(g, QComboBox) and val: idx = g.findText(val, Qt.MatchFixedString) if idx < 0: g.addItem(val) idx = g.findText(val, Qt.MatchFixedString) g.setCurrentIndex(idx) elif isinstance(g, QCheckBox): g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked) elif isinstance(g, (XPathEdit, RegexEdit)): g.edit.setText(val if val else '') else: raise Exception('Can\'t set value %s in %s'%(repr(val), unicode(g.objectName()))) self.post_set_value(g, val)
def __init__(self, parent=None): super().__init__(parent) font = QFont('Lucida Console', 10) self.input_prompt_label = QLabel() self.input_prompt_label.setFont(font) self.set_continue_input_prompt(False) self.command_line = QLineEdit() self.command_line.setFont(font) self.command_line.returnPressed.connect(self.on_command_line_return_pressed) self.output_widget = QTextEdit() self.setFont(font) layout = QVBoxLayout() layout.addWidget(self.output_widget) command_line_layout = QFormLayout() command_line_layout.addRow(self.input_prompt_label, self.command_line) layout.addLayout(command_line_layout) self.setLayout(layout)
def __init__(self): super().__init__() self.label = QLabel() self.label.setAlignment(Qt.AlignCenter) self.label.setFont(QFont('Courier', 25)) def loop(): i = 0 while True: self.label.setText(str(i)) i += 1 time.sleep(1) class Thread(QThread): def run(self): loop() self.thread = Thread() self.thread.start() self.setCentralWidget(self.label)
def __init__(self, admin: bool): super().__init__() grid = QGridLayout() self.setLayout(grid) email_label = QLabel("Email:") email_label.setFont(QFont("Segeo UI", 20)) grid.addWidget(email_label) self.email_entry = QLineEdit() self.email_entry.setFont(QFont("Segeo UI", 20)) grid.addWidget(self.email_entry) password_label = QLabel("Password:"******"Segeo UI", 20)) grid.addWidget(password_label) self.password_entry = QLineEdit() self.password_entry.setFont(QFont("Segeo UI", 20)) self.password_entry.setEchoMode(QLineEdit.Password) grid.addWidget(self.password_entry) repeat_password_label = QLabel("Repeat Password:"******"Segeo UI", 20)) grid.addWidget(repeat_password_label) self.repeat_password_entry = QLineEdit() self.repeat_password_entry.setFont(QFont("Segeo UI", 20)) self.repeat_password_entry.setEchoMode(QLineEdit.Password) grid.addWidget(self.repeat_password_entry) create_account_btn = QPushButton(text="Create Account") create_account_btn.setFont(QFont("Segeo UI", 20)) create_account_btn.clicked.connect(lambda: self.create_account(admin)) grid.addWidget(create_account_btn)
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 generate_buttons(self): self.buttons = [] self.GameName.setText(str(self.N)+'-Puzzle') width = int(m.sqrt(self.N+1)) for i in range(width): for j in range(width): button = QtWidgets.QPushButton(self.widget) button.setMaximumHeight(120) button.setMaximumWidth(120) button.setObjectName(str(i*width+j)) font = QFont() font.setPointSize(14) font.setBold(1) button.setFont(font) button.setText(str(state[i*width+j])) button.clicked.connect(lambda x:self.push_button(int(self.sender().objectName()))) self.buttons.append(button) self.ButtonLayout.addWidget(button, i, j)
def __init__(self): super(ModelTrain, self).__init__() self.setObjectName('ModelTrain') self.setStyleSheet( GetQssFile.readQss('../resource/qss/modelTrain.qss')) self.log = logging.getLogger('StarCraftII') # font font = QFont() font.setWeight(50) font.setPixelSize(15) # set widget of layout self.frame = QFrame(self) self.frame.setGeometry(QDesktopWidget().screenGeometry()) self.main_layout = QHBoxLayout(self) self.main_layout.setSpacing(20) self.setLayout(self.main_layout) # model tarin self.modelTrain = QPushButton() self.modelTrain.setObjectName('modelTrain') self.modelTrainLabel = QLabel('model train') self.modelTrainLabel.setFont(font) self.modelTrain_layout = QVBoxLayout() self.modelTrain_layout.addWidget(self.modelTrain, alignment=Qt.AlignCenter) self.modelTrain_layout.addWidget(self.modelTrainLabel, alignment=Qt.AlignCenter) self.main_layout.addLayout(self.modelTrain_layout) # a description dialog self.dialog = None # add stretch self.main_layout.addStretch(1) # initialization self.initUI()
def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.old_look = gprefs['tag_browser_old_look'] self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True)) self.rating_font = QFont(rating_font())
def family_setter(w, val): w.setCurrentFont(QFont(val))
class TextEdit(PlainTextEdit): link_clicked = pyqtSignal(object) smart_highlighting_updated = pyqtSignal() def __init__(self, parent=None, expected_geometry=(100, 50)): PlainTextEdit.__init__(self, parent) self.snippet_manager = SnippetManager(self) self.completion_popup = CompletionPopup(self) self.request_completion = self.completion_doc_name = None self.clear_completion_cache_timer = t = QTimer(self) t.setInterval(5000), t.timeout.connect( self.clear_completion_cache), t.setSingleShot(True) self.textChanged.connect(t.start) self.last_completion_request = -1 self.gutter_width = 0 self.tw = 2 self.expected_geometry = expected_geometry self.saved_matches = {} self.syntax = None self.smarts = NullSmarts(self) self.current_cursor_line = None self.current_search_mark = None self.smarts_highlight_timer = t = QTimer() t.setInterval(750), t.setSingleShot(True), t.timeout.connect( self.update_extra_selections) self.highlighter = SyntaxHighlighter() self.line_number_area = LineNumbers(self) self.apply_settings() self.setMouseTracking(True) self.cursorPositionChanged.connect(self.highlight_cursor_line) self.blockCountChanged[int].connect(self.update_line_number_area_width) self.updateRequest.connect(self.update_line_number_area) def get_droppable_files(self, md): def is_mt_ok(mt): return self.syntax == 'html' and (mt in OEB_DOCS or mt in OEB_STYLES or mt.startswith('image/')) if md.hasFormat(CONTAINER_DND_MIMETYPE): for line in bytes(md.data(CONTAINER_DND_MIMETYPE)).decode( 'utf-8').splitlines(): mt = current_container().mime_map.get( line, 'application/octet-stream') if is_mt_ok(mt): yield line, mt, True return for qurl in md.urls(): if qurl.isLocalFile() and os.access(qurl.toLocalFile(), os.R_OK): path = qurl.toLocalFile() mt = guess_type(path) if is_mt_ok(mt): yield path, mt, False def canInsertFromMimeData(self, md): if md.hasText() or (md.hasHtml() and self.syntax == 'html') or md.hasImage(): return True elif tuple(self.get_droppable_files(md)): return True return False def insertFromMimeData(self, md): files = tuple(self.get_droppable_files(md)) base = self.highlighter.doc_name or None def get_name(name): return get_recommended_folders(current_container(), (name, ))[name] + '/' + name def get_href(name): return current_container().name_to_href(name, base) def insert_text(text): c = self.textCursor() c.insertText(text) self.setTextCursor(c) def add_file(name, data, mt=None): from calibre.gui2.tweak_book.boss import get_boss name = current_container().add_file(name, data, media_type=mt, modify_name_if_needed=True) get_boss().refresh_file_list() return name if files: for path, mt, is_name in files: if is_name: name = path else: name = get_name(os.path.basename(path)) with lopen(path, 'rb') as f: name = add_file(name, f.read(), mt) href = get_href(name) if mt.startswith('image/'): self.insert_image(href) elif mt in OEB_STYLES: insert_text( '<link href="{}" rel="stylesheet" type="text/css"/>'. format(href)) elif mt in OEB_DOCS: self.insert_hyperlink(href, name) return if md.hasImage(): img = md.imageData() if img.isValid(): data = image_to_data(img, fmt='PNG') name = add_file(get_name('dropped_image.png', data)) self.insert_image(get_href(name)) return if md.hasHtml(): insert_text(md.html()) return if md.hasText(): return insert_text(md.text()) @dynamic_property def is_modified(self): ''' True if the document has been modified since it was loaded or since the last time is_modified was set to False. ''' def fget(self): return self.document().isModified() def fset(self, val): self.document().setModified(bool(val)) return property(fget=fget, fset=fset) def sizeHint(self): return self.size_hint def apply_settings(self, prefs=None, dictionaries_changed=False): # {{{ prefs = prefs or tprefs self.setAcceptDrops(prefs.get('editor_accepts_drops', True)) self.setLineWrapMode( QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = get_theme(prefs['editor_theme']) self.apply_theme(theme) w = self.fontMetrics() self.space_width = w.width(' ') self.tw = self.smarts.override_tab_stop_width if self.smarts.override_tab_stop_width is not None else prefs[ 'editor_tab_stop_width'] self.setTabStopWidth(self.tw * self.space_width) if dictionaries_changed: self.highlighter.rehighlight() def apply_theme(self, theme): self.theme = theme pal = self.palette() pal.setColor(pal.Base, theme_color(theme, 'Normal', 'bg')) pal.setColor(pal.AlternateBase, theme_color(theme, 'CursorLine', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'Normal', 'fg')) pal.setColor(pal.Highlight, theme_color(theme, 'Visual', 'bg')) pal.setColor(pal.HighlightedText, theme_color(theme, 'Visual', 'fg')) self.setPalette(pal) self.tooltip_palette = pal = QPalette() pal.setColor(pal.ToolTipBase, theme_color(theme, 'Tooltip', 'bg')) pal.setColor(pal.ToolTipText, theme_color(theme, 'Tooltip', 'fg')) self.line_number_palette = pal = QPalette() pal.setColor(pal.Base, theme_color(theme, 'LineNr', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'LineNr', 'fg')) pal.setColor(pal.BrightText, theme_color(theme, 'LineNrC', 'fg')) self.match_paren_format = theme_format(theme, 'MatchParen') font = self.font() ff = tprefs['editor_font_family'] if ff is None: ff = default_font_family() font.setFamily(ff) font.setPointSize(tprefs['editor_font_size']) self.tooltip_font = QFont(font) self.tooltip_font.setPointSize(font.pointSize() - 1) self.setFont(font) self.highlighter.apply_theme(theme) w = self.fontMetrics() self.number_width = max(map(lambda x: w.width(str(x)), xrange(10))) self.size_hint = QSize( self.expected_geometry[0] * w.averageCharWidth(), self.expected_geometry[1] * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line() self.completion_popup.clear_caches(), self.completion_popup.update() # }}} def load_text(self, text, syntax='html', process_template=False, doc_name=None): self.syntax = syntax self.highlighter = get_highlighter(syntax)() self.highlighter.apply_theme(self.theme) self.highlighter.set_document(self.document(), doc_name=doc_name) sclass = get_smarts(syntax) if sclass is not None: self.smarts = sclass(self) if self.smarts.override_tab_stop_width is not None: self.tw = self.smarts.override_tab_stop_width self.setTabStopWidth(self.tw * self.space_width) self.setPlainText(unicodedata.normalize('NFC', unicode(text))) if process_template and QPlainTextEdit.find(self, '%CURSOR%'): c = self.textCursor() c.insertText('') def change_document_name(self, newname): self.highlighter.doc_name = newname self.highlighter.rehighlight( ) # Ensure links are checked w.r.t. the new name correctly def replace_text(self, text): c = self.textCursor() pos = c.position() c.beginEditBlock() c.clearSelection() c.select(c.Document) c.insertText(unicodedata.normalize('NFC', text)) c.endEditBlock() c.setPosition(min(pos, len(text))) self.setTextCursor(c) self.ensureCursorVisible() def simple_replace(self, text, cursor=None): c = cursor or self.textCursor() c.insertText(unicodedata.normalize('NFC', text)) self.setTextCursor(c) def go_to_line(self, lnum, col=None): lnum = max(1, min(self.blockCount(), lnum)) c = self.textCursor() c.clearSelection() c.movePosition(c.Start) c.movePosition(c.NextBlock, n=lnum - 1) c.movePosition(c.StartOfLine) c.movePosition(c.EndOfLine, c.KeepAnchor) text = unicode(c.selectedText()).rstrip('\0') if col is None: c.movePosition(c.StartOfLine) lt = text.lstrip() if text and lt and lt != text: c.movePosition(c.NextWord) else: c.setPosition(c.block().position() + col) if c.blockNumber() + 1 > lnum: # We have moved past the end of the line c.setPosition(c.block().position()) c.movePosition(c.EndOfBlock) self.setTextCursor(c) self.ensureCursorVisible() def update_extra_selections(self, instant=True): sel = [] if self.current_cursor_line is not None: sel.append(self.current_cursor_line) if self.current_search_mark is not None: sel.append(self.current_search_mark) if instant and not self.highlighter.has_requests and self.smarts is not None: sel.extend(self.smarts.get_extra_selections(self)) self.smart_highlighting_updated.emit() else: self.smarts_highlight_timer.start() self.setExtraSelections(sel) # Search and replace {{{ def mark_selected_text(self): sel = QTextEdit.ExtraSelection() sel.format.setBackground(self.highlight_color) sel.cursor = self.textCursor() if sel.cursor.hasSelection(): self.current_search_mark = sel c = self.textCursor() c.clearSelection() self.setTextCursor(c) else: self.current_search_mark = None self.update_extra_selections() def find_in_marked(self, pat, wrap=False, save_match=None): if self.current_search_mark is None: return False csm = self.current_search_mark.cursor reverse = pat.flags & regex.REVERSE c = self.textCursor() c.clearSelection() m_start = min(csm.position(), csm.anchor()) m_end = max(csm.position(), csm.anchor()) if c.position() < m_start: c.setPosition(m_start) if c.position() > m_end: c.setPosition(m_end) pos = m_start if reverse else m_end if wrap: pos = m_end if reverse else m_start c.setPosition(pos, c.KeepAnchor) raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0') m = pat.search(raw) if m is None: return False start, end = m.span() if start == end: return False if wrap: if reverse: textpos = c.anchor() start, end = textpos + end, textpos + start else: start, end = m_start + start, m_start + end else: if reverse: start, end = m_start + end, m_start + start else: start, end = c.anchor() + start, c.anchor() + end c.clearSelection() c.setPosition(start) c.setPosition(end, c.KeepAnchor) self.setTextCursor(c) # Center search result on screen self.centerCursor() if save_match is not None: self.saved_matches[save_match] = (pat, m) return True def all_in_marked(self, pat, template=None): if self.current_search_mark is None: return 0 c = self.current_search_mark.cursor raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0') if template is None: count = len(pat.findall(raw)) else: from calibre.gui2.tweak_book.function_replace import Function repl_is_func = isinstance(template, Function) if repl_is_func: template.init_env() raw, count = pat.subn(template, raw) if repl_is_func: from calibre.gui2.tweak_book.search import show_function_debug_output if getattr(template.func, 'append_final_output_to_marked', False): retval = template.end() if retval: raw += unicode(retval) else: template.end() show_function_debug_output(template) if count > 0: start_pos = min(c.anchor(), c.position()) c.insertText(raw) end_pos = max(c.anchor(), c.position()) c.setPosition(start_pos), c.setPosition(end_pos, c.KeepAnchor) self.update_extra_selections() return count def smart_comment(self): from calibre.gui2.tweak_book.editor.comments import smart_comment smart_comment(self, self.syntax) def sort_css(self): from calibre.gui2.dialogs.confirm_delete import confirm if confirm(_( 'Sorting CSS rules can in rare cases change the effective styles applied to the book.' ' Are you sure you want to proceed?'), 'edit-book-confirm-sort-css', parent=self, config_set=tprefs): c = self.textCursor() c.beginEditBlock() c.movePosition(c.Start), c.movePosition(c.End, c.KeepAnchor) text = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0') from calibre.ebooks.oeb.polish.css import sort_sheet text = sort_sheet(current_container(), text).cssText c.insertText(text) c.movePosition(c.Start) c.endEditBlock() self.setTextCursor(c) def find(self, pat, wrap=False, marked=False, complete=False, save_match=None): if marked: return self.find_in_marked(pat, wrap=wrap, save_match=save_match) reverse = pat.flags & regex.REVERSE c = self.textCursor() c.clearSelection() if complete: # Search the entire text c.movePosition(c.End if reverse else c.Start) pos = c.Start if reverse else c.End if wrap and not complete: pos = c.End if reverse else c.Start c.movePosition(pos, c.KeepAnchor) raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0') m = pat.search(raw) if m is None: return False start, end = m.span() if start == end: return False if wrap and not complete: if reverse: textpos = c.anchor() start, end = textpos + end, textpos + start else: if reverse: # Put the cursor at the start of the match start, end = end, start else: textpos = c.anchor() start, end = textpos + start, textpos + end c.clearSelection() c.setPosition(start) c.setPosition(end, c.KeepAnchor) self.setTextCursor(c) # Center search result on screen self.centerCursor() if save_match is not None: self.saved_matches[save_match] = (pat, m) return True def find_text(self, pat, wrap=False, complete=False): reverse = pat.flags & regex.REVERSE c = self.textCursor() c.clearSelection() if complete: # Search the entire text c.movePosition(c.End if reverse else c.Start) pos = c.Start if reverse else c.End if wrap and not complete: pos = c.End if reverse else c.Start c.movePosition(pos, c.KeepAnchor) if hasattr(self.smarts, 'find_text'): self.highlighter.join() found, start, end = self.smarts.find_text(pat, c) if not found: return False else: raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0') m = pat.search(raw) if m is None: return False start, end = m.span() if start == end: return False if reverse: start, end = end, start c.clearSelection() c.setPosition(start) c.setPosition(end, c.KeepAnchor) self.setTextCursor(c) # Center search result on screen self.centerCursor() return True def find_spell_word(self, original_words, lang, from_cursor=True, center_on_cursor=True): c = self.textCursor() c.setPosition(c.position()) if not from_cursor: c.movePosition(c.Start) c.movePosition(c.End, c.KeepAnchor) def find_first_word(haystack): match_pos, match_word = -1, None for w in original_words: idx = index_of(w, haystack, lang=lang) if idx > -1 and (match_pos == -1 or match_pos > idx): match_pos, match_word = idx, w return match_pos, match_word while True: text = unicode(c.selectedText()).rstrip('\0') idx, word = find_first_word(text) if idx == -1: return False c.setPosition(c.anchor() + idx) c.setPosition(c.position() + string_length(word), c.KeepAnchor) if self.smarts.verify_for_spellcheck(c, self.highlighter): self.highlighter.join() # Ensure highlighting is finished locale = self.spellcheck_locale_for_cursor(c) if not lang or not locale or (locale and lang == locale.langcode): self.setTextCursor(c) if center_on_cursor: self.centerCursor() return True c.setPosition(c.position()) c.movePosition(c.End, c.KeepAnchor) return False def find_next_spell_error(self, from_cursor=True): c = self.textCursor() if not from_cursor: c.movePosition(c.Start) block = c.block() while block.isValid(): for r in block.layout().additionalFormats(): if r.format.property(SPELL_PROPERTY): if not from_cursor or block.position( ) + r.start + r.length > c.position(): c.setPosition(block.position() + r.start) c.setPosition(c.position() + r.length, c.KeepAnchor) self.setTextCursor(c) return True block = block.next() return False def replace(self, pat, template, saved_match='gui'): c = self.textCursor() raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0') m = pat.fullmatch(raw) if m is None: # This can happen if either the user changed the selected text or # the search expression uses lookahead/lookbehind operators. See if # the saved match matches the currently selected text and # use it, if so. if saved_match is not None and saved_match in self.saved_matches: saved_pat, saved = self.saved_matches.pop(saved_match) if saved_pat == pat and saved.group() == raw: m = saved if m is None: return False if callable(template): text = template(m) else: text = m.expand(template) c.insertText(text) return True def go_to_anchor(self, anchor): if anchor is TOP: c = self.textCursor() c.movePosition(c.Start) self.setTextCursor(c) return True base = r'''%%s\s*=\s*['"]{0,1}%s''' % regex.escape(anchor) raw = unicode(self.toPlainText()) m = regex.search(base % 'id', raw) if m is None: m = regex.search(base % 'name', raw) if m is not None: c = self.textCursor() c.setPosition(m.start()) self.setTextCursor(c) return True return False # }}} # Line numbers and cursor line {{{ def highlight_cursor_line(self): sel = QTextEdit.ExtraSelection() sel.format.setBackground(self.palette().alternateBase()) sel.format.setProperty(QTextFormat.FullWidthSelection, True) sel.cursor = self.textCursor() sel.cursor.clearSelection() self.current_cursor_line = sel self.update_extra_selections(instant=False) # Update the cursor line's line number in the line number area try: self.line_number_area.update(0, self.last_current_lnum[0], self.line_number_area.width(), self.last_current_lnum[1]) except AttributeError: pass block = self.textCursor().block() top = int( self.blockBoundingGeometry(block).translated( self.contentOffset()).top()) height = int(self.blockBoundingRect(block).height()) self.line_number_area.update(0, top, self.line_number_area.width(), height) def update_line_number_area_width(self, block_count=0): self.gutter_width = self.line_number_area_width() self.setViewportMargins(self.gutter_width, 0, 0, 0) def line_number_area_width(self): digits = 1 limit = max(1, self.blockCount()) while limit >= 10: limit /= 10 digits += 1 return 8 + self.number_width * digits def update_line_number_area(self, rect, dy): if dy: self.line_number_area.scroll(0, dy) else: self.line_number_area.update(0, rect.y(), self.line_number_area.width(), rect.height()) if rect.contains(self.viewport().rect()): self.update_line_number_area_width() def resizeEvent(self, ev): QPlainTextEdit.resizeEvent(self, ev) cr = self.contentsRect() self.line_number_area.setGeometry( QRect(cr.left(), cr.top(), self.line_number_area_width(), cr.height())) 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 # }}} def override_shortcut(self, ev): # Let the global cut/copy/paste/undo/redo shortcuts work, this avoids the nbsp # problem as well, since they use the overridden copy() method # instead of the one from Qt, and allows proper customization # of the shortcuts if ev in (QKeySequence.Copy, QKeySequence.Cut, QKeySequence.Paste, QKeySequence.Undo, QKeySequence.Redo): ev.ignore() return True # This is used to convert typed hex codes into unicode # characters if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier: ev.accept() return True return PlainTextEdit.override_shortcut(self, ev) def text_for_range(self, block, r): c = self.textCursor() c.setPosition(block.position() + r.start) c.setPosition(c.position() + r.length, c.KeepAnchor) return unicode(c.selectedText()) def spellcheck_locale_for_cursor(self, c): with store_locale: formats = self.highlighter.parse_single_block(c.block())[0] pos = c.positionInBlock() for r in formats: if r.start <= pos <= r.start + r.length and r.format.property( SPELL_PROPERTY): return r.format.property(SPELL_LOCALE_PROPERTY) def recheck_word(self, word, locale): c = self.textCursor() c.movePosition(c.Start) block = c.block() while block.isValid(): for r in block.layout().additionalFormats(): if r.format.property(SPELL_PROPERTY) and self.text_for_range( block, r) == word: self.highlighter.reformat_block(block) break block = block.next() # Tooltips {{{ def syntax_range_for_cursor(self, cursor): if cursor.isNull(): return pos = cursor.positionInBlock() for r in cursor.block().layout().additionalFormats(): if r.start <= pos <= r.start + r.length and r.format.property( SYNTAX_PROPERTY): return r def syntax_format_for_cursor(self, cursor): return getattr(self.syntax_range_for_cursor(cursor), 'format', None) def show_tooltip(self, ev): c = self.cursorForPosition(ev.pos()) fmt = self.syntax_format_for_cursor(c) if fmt is not None: tt = unicode(fmt.toolTip()) if tt: QToolTip.setFont(self.tooltip_font) QToolTip.setPalette(self.tooltip_palette) QToolTip.showText(ev.globalPos(), textwrap.fill(tt)) return QToolTip.hideText() ev.ignore() # }}} def link_for_position(self, pos): c = self.cursorForPosition(pos) r = self.syntax_range_for_cursor(c) if r is not None and r.format.property(LINK_PROPERTY): return self.text_for_range(c.block(), r) def mousePressEvent(self, ev): if self.completion_popup.isVisible( ) and not self.completion_popup.rect().contains(ev.pos()): # For some reason using eventFilter for this does not work, so we # implement it here self.completion_popup.abort() if ev.modifiers() & Qt.CTRL: url = self.link_for_position(ev.pos()) if url is not None: ev.accept() self.link_clicked.emit(url) return return PlainTextEdit.mousePressEvent(self, ev) def get_range_inside_tag(self): c = self.textCursor() left = min(c.anchor(), c.position()) right = max(c.anchor(), c.position()) # For speed we use QPlainTextEdit's toPlainText as we dont care about # spaces in this context raw = unicode(QPlainTextEdit.toPlainText(self)) # Make sure the left edge is not within a <> gtpos = raw.find('>', left) ltpos = raw.find('<', left) if gtpos < ltpos: left = gtpos + 1 if gtpos > -1 else left right = max(left, right) if right != left: gtpos = raw.find('>', right) ltpos = raw.find('<', right) if ltpos > gtpos: ltpos = raw.rfind('<', left, right + 1) right = max(ltpos, left) return left, right def format_text(self, formatting): if self.syntax != 'html': return if formatting.startswith('justify_'): return self.smarts.set_text_alignment( self, formatting.partition('_')[-1]) color = 'currentColor' if formatting in {'color', 'background-color'}: color = QColorDialog.getColor( QColor(Qt.black if formatting == 'color' else Qt.white), self, _('Choose color'), QColorDialog.ShowAlphaChannel) if not color.isValid(): return r, g, b, a = color.getRgb() if a == 255: color = 'rgb(%d, %d, %d)' % (r, g, b) else: color = 'rgba(%d, %d, %d, %.2g)' % (r, g, b, a / 255) prefix, suffix = { 'bold': ('<b>', '</b>'), 'italic': ('<i>', '</i>'), 'underline': ('<u>', '</u>'), 'strikethrough': ('<strike>', '</strike>'), 'superscript': ('<sup>', '</sup>'), 'subscript': ('<sub>', '</sub>'), 'color': ('<span style="color: %s">' % color, '</span>'), 'background-color': ('<span style="background-color: %s">' % color, '</span>'), }[formatting] left, right = self.get_range_inside_tag() c = self.textCursor() c.setPosition(left) c.setPosition(right, c.KeepAnchor) prev_text = unicode(c.selectedText()).rstrip('\0') c.insertText(prefix + prev_text + suffix) if prev_text: right = c.position() c.setPosition(left) c.setPosition(right, c.KeepAnchor) else: c.setPosition(c.position() - len(suffix)) self.setTextCursor(c) def insert_image(self, href, fullpage=False, preserve_aspect_ratio=False): c = self.textCursor() template, alt = 'url(%s)', '' left = min(c.position(), c.anchor) if self.syntax == 'html': left, right = self.get_range_inside_tag() c.setPosition(left) c.setPosition(right, c.KeepAnchor) href = prepare_string_for_xml(href, True) if fullpage: template = '''\ <div style="page-break-before:always; page-break-after:always; page-break-inside:avoid">\ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" \ version="1.1" width="100%%" height="100%%" viewBox="0 0 1200 1600" preserveAspectRatio="{}">\ <image width="1200" height="1600" xlink:href="%s"/>\ </svg></div>'''.format('xMidYMid meet' if preserve_aspect_ratio else 'none') else: alt = _('Image') template = '<img alt="{0}" src="%s" />'.format(alt) text = template % href c.insertText(text) if self.syntax == 'html' and not fullpage: c.setPosition(left + 10) c.setPosition(c.position() + len(alt), c.KeepAnchor) else: c.setPosition(left) c.setPosition(left + len(text), c.KeepAnchor) self.setTextCursor(c) def insert_hyperlink(self, target, text): if hasattr(self.smarts, 'insert_hyperlink'): self.smarts.insert_hyperlink(self, target, text) def insert_tag(self, tag): if hasattr(self.smarts, 'insert_tag'): self.smarts.insert_tag(self, tag) def keyPressEvent(self, ev): if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier: if self.replace_possible_unicode_sequence(): ev.accept() return if ev.key() == Qt.Key_Insert: self.setOverwriteMode(self.overwriteMode() ^ True) ev.accept() return if self.snippet_manager.handle_key_press(ev): self.completion_popup.hide() return if self.smarts.handle_key_press(ev, self): self.handle_keypress_completion(ev) return QPlainTextEdit.keyPressEvent(self, ev) self.handle_keypress_completion(ev) def handle_keypress_completion(self, ev): if self.request_completion is None: return code = ev.key() if code in (0, Qt.Key_unknown, Qt.Key_Shift, Qt.Key_Control, Qt.Key_Alt, Qt.Key_Meta, Qt.Key_AltGr, Qt.Key_CapsLock, Qt.Key_NumLock, Qt.Key_ScrollLock, Qt.Key_Up, Qt.Key_Down): # We ignore up/down arrow so as to not break scrolling through the # text with the arrow keys return result = self.smarts.get_completion_data(self, ev) if result is None: self.last_completion_request += 1 else: self.last_completion_request = self.request_completion(*result) self.completion_popup.mark_completion( self, None if result is None else result[-1]) def handle_completion_result(self, result): if result.request_id[0] >= self.last_completion_request: self.completion_popup.handle_result(result) def clear_completion_cache(self): if self.request_completion is not None and self.completion_doc_name: self.request_completion(None, 'file:' + self.completion_doc_name) def replace_possible_unicode_sequence(self): c = self.textCursor() has_selection = c.hasSelection() if has_selection: text = unicode(c.selectedText()).rstrip('\0') else: c.setPosition(c.position() - min(c.positionInBlock(), 6), c.KeepAnchor) text = unicode(c.selectedText()).rstrip('\0') m = re.search(r'[a-fA-F0-9]{2,6}$', text) if m is None: return False text = m.group() try: num = int(text, 16) except ValueError: return False if num > 0x10ffff or num < 1: return False end_pos = max(c.anchor(), c.position()) c.setPosition(end_pos - len(text)), c.setPosition( end_pos, c.KeepAnchor) c.insertText(safe_chr(num)) return True def select_all(self): c = self.textCursor() c.clearSelection() c.setPosition(0) c.movePosition(c.End, c.KeepAnchor) self.setTextCursor(c) def rename_block_tag(self, new_name): if hasattr(self.smarts, 'rename_block_tag'): self.smarts.rename_block_tag(self, new_name) def current_tag(self, for_position_sync=True): return self.smarts.cursor_position_with_sourceline( self.textCursor(), for_position_sync=for_position_sync) def goto_sourceline(self, sourceline, tags, attribute=None): return self.smarts.goto_sourceline(self, sourceline, tags, attribute=attribute) def get_tag_contents(self): c = self.smarts.get_inner_HTML(self) if c is not None: return self.selected_text_from_cursor(c) def goto_css_rule(self, rule_address, sourceline_address=None): from calibre.gui2.tweak_book.editor.smarts.css import find_rule block = None if self.syntax == 'css': raw = unicode(self.toPlainText()) line, col = find_rule(raw, rule_address) if line is not None: block = self.document().findBlockByNumber(line - 1) elif sourceline_address is not None: sourceline, tags = sourceline_address if self.goto_sourceline(sourceline, tags): c = self.textCursor() c.setPosition(c.position() + 1) self.setTextCursor(c) raw = self.get_tag_contents() line, col = find_rule(raw, rule_address) if line is not None: block = self.document().findBlockByNumber(c.blockNumber() + line - 1) if block is not None and block.isValid(): c = self.textCursor() c.setPosition(block.position() + col) self.setTextCursor(c) def change_case(self, action, cursor=None): cursor = cursor or self.textCursor() text = self.selected_text_from_cursor(cursor) text = { 'lower': lower, 'upper': upper, 'capitalize': capitalize, 'title': titlecase, 'swap': swapcase }[action](text) cursor.insertText(text) self.setTextCursor(cursor)
class ResultsList(QTreeWidget): current_result_changed = pyqtSignal(object) open_annotation = pyqtSignal(object, object, object) def __init__(self, parent): QTreeWidget.__init__(self, parent) self.setHeaderHidden(True) self.setSelectionMode(self.ExtendedSelection) self.delegate = AnnotsResultsDelegate(self) self.setItemDelegate(self.delegate) self.section_font = QFont(self.font()) self.itemDoubleClicked.connect(self.item_activated) self.section_font.setItalic(True) self.currentItemChanged.connect(self.current_item_changed) self.number_of_results = 0 self.item_map = [] def item_activated(self, item): r = item.data(0, Qt.UserRole) if isinstance(r, dict): self.open_annotation.emit(r['book_id'], r['format'], r['annotation']) def set_results(self, results, emphasize_text): self.clear() self.delegate.emphasize_text = emphasize_text self.number_of_results = 0 self.item_map = [] book_id_map = {} db = current_db() for result in results: book_id = result['book_id'] if book_id not in book_id_map: book_id_map[book_id] = { 'title': db.field_for('title', book_id), 'matches': [] } book_id_map[book_id]['matches'].append(result) for book_id, entry in book_id_map.items(): section = QTreeWidgetItem([entry['title']], 1) section.setFlags(Qt.ItemIsEnabled) section.setFont(0, self.section_font) self.addTopLevelItem(section) section.setExpanded(True) for result in entry['matches']: item = QTreeWidgetItem(section, [' '], 2) self.item_map.append(item) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) item.setData(0, Qt.UserRole, result) item.setData(0, Qt.UserRole + 1, self.number_of_results) self.number_of_results += 1 if self.item_map: self.setCurrentItem(self.item_map[0]) def current_item_changed(self, current, previous): if current is not None: r = current.data(0, Qt.UserRole) if isinstance(r, dict): self.current_result_changed.emit(r) else: self.current_result_changed.emit(None) def show_next(self, backwards=False): item = self.currentItem() if item is None: return i = int(item.data(0, Qt.UserRole + 1)) i += -1 if backwards else 1 i %= self.number_of_results self.setCurrentItem(self.item_map[i]) def keyPressEvent(self, ev): key = ev.key() if key == Qt.Key_Down: self.show_next() return if key == Qt.Key_Up: self.show_next(backwards=True) return return QTreeWidget.keyPressEvent(self, ev) @property def selected_annot_ids(self): for item in self.selectedItems(): yield item.data(0, Qt.UserRole)['id'] @property def selected_annotations(self): for item in self.selectedItems(): x = item.data(0, Qt.UserRole) ans = x['annotation'].copy() for key in ('book_id', 'format'): ans[key] = x[key] yield ans
def set_subtitle_font(self, for_ratings=True): if for_ratings: self.setSubtitleFont(QFont(rating_font())) else: self.setSubtitleFont(self.font())
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 family_setter(which, w, val): w.setCurrentFont(QFont(val or default_font(which)))
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 (self.accounts[account][0]) 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] = 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()
def __init__(self): super(SituationInformation, self).__init__() self.setObjectName('SituationInformation') self.setStyleSheet( GetQssFile.readQss('../resource/qss/situationInformation.qss')) self.log = logging.getLogger('StarCraftII') # font font = QFont() font.setWeight(50) font.setPixelSize(15) # set widget of layout self.frame = QFrame(self) self.frame.setGeometry(QDesktopWidget().screenGeometry()) self.main_layout = QHBoxLayout(self) self.main_layout.setSpacing(20) self.setLayout(self.main_layout) # consumption action self.consumption = QPushButton() self.consumption.setObjectName('consumption') self.consumptionLabel = QLabel('consumption') self.consumptionLabel.setFont(font) self.consumption_layout = QVBoxLayout() self.consumption_layout.addWidget(self.consumption, alignment=Qt.AlignCenter) self.consumption_layout.addWidget(self.consumptionLabel, alignment=Qt.AlignCenter) self.main_layout.addLayout(self.consumption_layout) # resource action self.resource = QPushButton() self.resource.setObjectName('resource') self.resourceLabel = QLabel('resource') self.resourceLabel.setFont(font) self.resource_layout = QVBoxLayout() self.resource_layout.addWidget(self.resource, alignment=Qt.AlignCenter) self.resource_layout.addWidget(self.resourceLabel, alignment=Qt.AlignCenter) self.main_layout.addLayout(self.resource_layout) # score action self.score = QPushButton() self.score.setObjectName('score') self.scoreLabel = QLabel('score') self.scoreLabel.setFont(font) self.score_layout = QVBoxLayout() self.score_layout.addWidget(self.score, alignment=Qt.AlignCenter) self.score_layout.addWidget(self.scoreLabel, alignment=Qt.AlignCenter) self.main_layout.addLayout(self.score_layout) # output action self.output = QPushButton() self.output.setObjectName('output') self.outputLabel = QLabel('output') self.outputLabel.setFont(font) self.output_layout = QVBoxLayout() self.output_layout.addWidget(self.output, alignment=Qt.AlignCenter) self.output_layout.addWidget(self.outputLabel, alignment=Qt.AlignCenter) self.main_layout.addLayout(self.output_layout) # a description dialog self.dialog = None # add stretch self.main_layout.addStretch(1) # initialization self.initUI()
def layout_text(prefs, img, title, subtitle, footer, max_height, style): width = img.width() - 2 * style.hmargin title, subtitle, footer = title, subtitle, footer title_font = QFont(prefs.title_font_family or 'Liberation Serif') title_font.setPixelSize(prefs.title_font_size) title_block = Block(title, width, title_font, img, max_height, style.TITLE_ALIGN) title_block.position = style.hmargin, style.vmargin subtitle_block = Block() if subtitle: subtitle_font = QFont(prefs.subtitle_font_family or 'Liberation Sans') subtitle_font.setPixelSize(prefs.subtitle_font_size) gap = 2 * title_block.leading mh = max_height - title_block.height - gap subtitle_block = Block(subtitle, width, subtitle_font, img, mh, style.SUBTITLE_ALIGN) subtitle_block.position = style.hmargin, title_block.position.y + title_block.height + gap footer_font = QFont(prefs.footer_font_family or 'Liberation Serif') footer_font.setPixelSize(prefs.footer_font_size) footer_block = Block(footer, width, footer_font, img, max_height, style.FOOTER_ALIGN) footer_block.position = style.hmargin, img.height( ) - style.vmargin - footer_block.height return title_block, subtitle_block, footer_block
def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.old_look = False self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True)) self.rating_font = QFont(rating_font()) self.completion_data = None
class AnnotationsAppearance(SizePersistedDialog): ''' Dialog for managing CSS rules, including Preview window ''' if isosx: FONT = QFont('Monaco', 12) elif iswindows: FONT = QFont('Lucida Console', 9) elif islinux: FONT = QFont('Monospace', 9) FONT.setStyleHint(QFont.TypeWriter) def __init__(self, parent, icon, prefs): self.parent = parent self.prefs = prefs self.icon = icon super(AnnotationsAppearance, self).__init__(parent, 'appearance_dialog') self.setWindowTitle(_('Modify appearance')) self.setWindowIcon(icon) self.l = QVBoxLayout(self) self.setLayout(self.l) # Add a label for description #self.description_label = QLabel(_("Descriptive text here")) #self.l.addWidget(self.description_label) # Add a group box, vertical layout for preview window self.preview_gb = QGroupBox(self) self.preview_gb.setTitle(_("Preview")) self.preview_vl = QVBoxLayout(self.preview_gb) self.l.addWidget(self.preview_gb) self.wv = QWebView() self.wv.setHtml('<p></p>') self.wv.setMinimumHeight(100) self.wv.setMaximumHeight(16777215) self.wv.setGeometry(0, 0, 200, 100) self.wv.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.preview_vl.addWidget(self.wv) # Create a group box, horizontal layout for the table self.css_table_gb = QGroupBox(self) self.css_table_gb.setTitle(_("Annotation elements")) self.elements_hl = QHBoxLayout(self.css_table_gb) self.l.addWidget(self.css_table_gb) # Add the group box to the main layout self.elements_table = AnnotationElementsTable(self, 'annotation_elements_tw') self.elements_hl.addWidget(self.elements_table) self.elements_table.initialize() # Options self.options_gb = QGroupBox(self) self.options_gb.setTitle(_("Options")) self.options_gl = QGridLayout(self.options_gb) self.l.addWidget(self.options_gb) current_row = 0 # <hr/> separator # addWidget(widget, row, col, rowspan, colspan) self.hr_checkbox = QCheckBox(_('Add horizontal rule between annotations')) self.hr_checkbox.stateChanged.connect(self.hr_checkbox_changed) self.hr_checkbox.setCheckState( JSONConfig('plugins/annotations').get('appearance_hr_checkbox', False)) self.options_gl.addWidget(self.hr_checkbox, current_row, 0, 1, 4) current_row += 1 # Timestamp self.timestamp_fmt_label = QLabel(_("Timestamp format:")) self.options_gl.addWidget(self.timestamp_fmt_label, current_row, 0) self.timestamp_fmt_le = QLineEdit( JSONConfig('plugins/annotations').get('appearance_timestamp_format', default_timestamp), parent=self) self.timestamp_fmt_le.textEdited.connect(self.timestamp_fmt_changed) self.timestamp_fmt_le.setFont(self.FONT) self.timestamp_fmt_le.setObjectName('timestamp_fmt_le') self.timestamp_fmt_le.setToolTip(_('Format string for timestamp')) self.timestamp_fmt_le.setMaximumWidth(16777215) self.timestamp_fmt_le.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.options_gl.addWidget(self.timestamp_fmt_le, current_row, 1) self.timestamp_fmt_reset_tb = QToolButton(self) self.timestamp_fmt_reset_tb.setToolTip(_("Reset to default")) self.timestamp_fmt_reset_tb.setIcon(QIcon(I('trash.png'))) self.timestamp_fmt_reset_tb.clicked.connect(self.reset_timestamp_to_default) self.options_gl.addWidget(self.timestamp_fmt_reset_tb, current_row, 2) self.timestamp_fmt_help_tb = QToolButton(self) self.timestamp_fmt_help_tb.setToolTip(_("Format string reference")) self.timestamp_fmt_help_tb.setIcon(QIcon(I('help.png'))) self.timestamp_fmt_help_tb.clicked.connect(self.show_help) self.options_gl.addWidget(self.timestamp_fmt_help_tb, current_row, 3) # Button box bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.l.addWidget(bb) # Spacer self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.l.addItem(self.spacerItem) # Sizing sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog() def hr_checkbox_changed(self, state): self.prefs.set('appearance_hr_checkbox', state) self.elements_table.preview_css() def reset_timestamp_to_default(self): from calibre_plugins.annotations.appearance import default_timestamp self.timestamp_fmt_le.setText(default_timestamp) self.timestamp_fmt_changed() def show_help(self): ''' Display strftime help file ''' hv = HelpView(self, self.icon, self.prefs, html=get_resources('help/timestamp_formats.html'), title=_("Timestamp formats")) hv.show() def sizeHint(self): return QtCore.QSize(600, 200) def timestamp_fmt_changed(self): self.prefs.set('appearance_timestamp_format', str(self.timestamp_fmt_le.text())) self.elements_table.preview_css()
class AnnotationElementsTable(QTableWidget): ''' QTableWidget managing CSS rules ''' DEBUG = True #MAXIMUM_TABLE_HEIGHT = 113 ELEMENT_FIELD_WIDTH = 250 if isosx: FONT = QFont('Monaco', 11) elif iswindows: FONT = QFont('Lucida Console', 9) elif islinux: FONT = QFont('Monospace', 9) FONT.setStyleHint(QFont.TypeWriter) COLUMNS = { 'ELEMENT_NAME': {'ordinal': 0, 'name': 'Element Name'}, 'ELEMENT': {'ordinal': 1, 'name': _('Element')}, 'CSS': {'ordinal': 2, 'name': _('CSS')}, } sample_ann_1 = { 'text': ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean placerat condimentum semper. Aliquam hendrerit nisl mauris, nec laoreet orci. Donec rutrum consequat ultricies.', 'Curabitur sollicitudin euismod felis, vitae mollis magna vestibulum id.'], 'note': [_('This is a note appended to the highlight.'), _('And additional comments after a linebreak.')], 'highlightcolor': 'Yellow', 'timestamp': time.mktime(time.localtime()), 'location': 'Chapter 4', 'location_sort': 0 } sample_ann_2 = { 'text': ['Phasellus sit amet ipsum id velit commodo convallis. In dictum felis non tellus volutpat in tincidunt neque varius. Sed at mauris augue. Vestibulum ligula nunc, ullamcorper id suscipit sed, auctor quis erat. In hac habitasse platea dictumst. Aliquam sit amet nulla dolor, ut tempus libero. In hac habitasse platea dictumst. Etiam consectetur orci vel massa eleifend in vestibulum odio auctor. Praesent orci turpis, aliquet non eleifend sit amet, sollicitudin sit amet augue.'], 'highlightcolor': 'Green', 'timestamp': time.mktime(time.localtime()), 'location': 'Chapter 12', 'location_sort': 1 } sample_ann_3 = { 'text': ['Morbi massa tellus, laoreet id pretium sed, volutpat in felis.', 'Donec massa nulla, malesuada vitae volutpat quis, accumsan ut tellus.'], 'note': [_('This is a note appended to the highlight.'), _('And additional comments after a linebreak.')], 'highlightcolor': 'Purple', 'timestamp': time.mktime(time.localtime()), 'location': 'Chapter 53', 'location_sort': 2 } def __init__(self, parent, object_name): self.parent = parent self.prefs = parent.prefs self.elements = self.prefs.get('appearance_css', None) debug_print("AnnotationElementsTable::__init__ - self.elements", self.elements) if not self.elements: self.elements = default_elements debug_print("AnnotationElementsTable::__init__ - self.elements", self.elements) QTableWidget.__init__(self) self.setObjectName(object_name) self.layout = parent.elements_hl.layout() # Add ourselves to the layout sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) #sizePolicy.setVerticalStretch(0) #sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) #self.setMaximumSize(QSize(16777215, self.MAXIMUM_TABLE_HEIGHT)) self.setColumnCount(0) self.setRowCount(0) self.layout.addWidget(self) def _init_controls(self): # Add the control set vbl = QVBoxLayout() self.move_element_up_tb = QToolButton() self.move_element_up_tb.setObjectName("move_element_up_tb") self.move_element_up_tb.setToolTip(_('Move element up')) self.move_element_up_tb.setIcon(QIcon(I('arrow-up.png'))) self.move_element_up_tb.clicked.connect(self.move_row_up) vbl.addWidget(self.move_element_up_tb) self.undo_css_tb = QToolButton() self.undo_css_tb.setObjectName("undo_css_tb") self.undo_css_tb.setToolTip(_('Restore CSS to last saved')) self.undo_css_tb.setIcon(QIcon(I('edit-undo.png'))) self.undo_css_tb.clicked.connect(partial(self.undo_reset_button_clicked, 'undo')) vbl.addWidget(self.undo_css_tb) self.reset_css_tb = QToolButton() self.reset_css_tb.setObjectName("reset_css_tb") self.reset_css_tb.setToolTip(_('Reset CSS to default')) self.reset_css_tb.setIcon(QIcon(I('trash.png'))) self.reset_css_tb.clicked.connect(partial(self.undo_reset_button_clicked, 'reset')) vbl.addWidget(self.reset_css_tb) self.move_element_down_tb = QToolButton() self.move_element_down_tb.setObjectName("move_element_down_tb") self.move_element_down_tb.setToolTip(_('Move element down')) self.move_element_down_tb.setIcon(QIcon(I('arrow-down.png'))) self.move_element_down_tb.clicked.connect(self.move_row_down) vbl.addWidget(self.move_element_down_tb) self.layout.addLayout(vbl) def _init_table_widget(self): header_labels = [self.COLUMNS[index]['name'] \ for index in sorted(self.COLUMNS.keys(), key=lambda c: self.COLUMNS[c]['ordinal'])] self.setColumnCount(len(header_labels)) self.setHorizontalHeaderLabels(header_labels) self.verticalHeader().setVisible(False) self.setSortingEnabled(False) # Select single rows self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSelectionMode(QAbstractItemView.SingleSelection) def convert_row_to_data(self, row): data = {} data['ordinal'] = row # data['name'] = unicode(self.cellWidget(row, self.COLUMNS['ELEMENT']['ordinal']).text()).strip() data['name'] = unicode(self.cellWidget(row, self.COLUMNS['ELEMENT_NAME']['ordinal']).text()).strip() data['css'] = unicode(self.cellWidget(row, self.COLUMNS['CSS']['ordinal']).toPlainText()).strip() return data def css_edited(self, row): self.select_and_scroll_to_row(row) col = self.COLUMNS['CSS']['ordinal'] widget = self.cellWidget(row, col) css = unicode(widget.toPlainText()) lines = [] for line in css.split('\n'): lines.append(re.sub('^\s*', '', line)) self.resize_row_height(lines, row) # self.prefs.set('appearance_css', self.get_data()) widget.setFocus() self.preview_css() def get_data(self): data_items = [] for row in range(self.rowCount()): data = self.convert_row_to_data(row) data_items.append( {'ordinal': data['ordinal'], 'name': data['name'], # 'translatable_name': translatable_element_names[data['name']], 'css': data['css'], }) return data_items def initialize(self): self._init_table_widget() self._init_controls() self.populate_table() self.resizeColumnsToContents() self.horizontalHeader().setStretchLastSection(True) # Update preview window, select first row self.css_edited(0) def move_row(self, source, dest): self.blockSignals(True) # Save the contents of the destination row saved_data = self.convert_row_to_data(dest) debug_print("Annotations::appearance.py::move_row - saved_data", saved_data) # Remove the destination row self.removeRow(dest) # Insert a new row at the original location self.insertRow(source) # Populate it with the saved data self.populate_table_row(source, saved_data) self.select_and_scroll_to_row(dest) self.blockSignals(False) self.css_edited(dest) def move_row_down(self): src_row = self.currentRow() dest_row = src_row + 1 if dest_row == self.rowCount(): return self.move_row(src_row, dest_row) def move_row_up(self): src_row = self.currentRow() dest_row = src_row - 1 if dest_row < 0: return self.move_row(src_row, dest_row) def populate_table(self): # Format of rules list is different if default values vs retrieved JSON # Hack to normalize list style elements = self.elements if elements and type(elements[0]) is list: elements = elements[0] self.setFocus() elements = sorted(elements, key=lambda k: k['ordinal']) for row, element in enumerate(elements): self.insertRow(row) self.select_and_scroll_to_row(row) self.populate_table_row(row, element) self.selectRow(0) self.setColumnHidden(0, True) def populate_table_row(self, row, data): self.blockSignals(True) self.setCellWidget(row, self.COLUMNS['ELEMENT_NAME']['ordinal'], QLabel(data['name'])) translatable_name = translatable_element_names[data['name']] self.set_element_name_in_row(row, self.COLUMNS['ELEMENT']['ordinal'], translatable_name) self.set_css_in_row(row, self.COLUMNS['CSS']['ordinal'], data['css']) self.blockSignals(False) def preview_css(self): ''' Construct a dummy annotation for preview purposes ''' from calibre_plugins.annotations.annotations import Annotation, Annotations pas = Annotations(None, title=_("Preview")) pas.annotations.append(Annotation(self.sample_ann_1)) pas.annotations.append(Annotation(self.sample_ann_2)) pas.annotations.append(Annotation(self.sample_ann_3)) self.parent.wv.setHtml(pas.to_HTML()) def resize_row_height(self, lines, row): point_size = self.FONT.pointSize() if isosx: height = 30 + (len(lines) - 1) * (point_size + 4) elif iswindows: height = 26 + (len(lines) - 1) * (point_size + 3) elif islinux: height = 30 + (len(lines) - 1) * (point_size + 6) self.verticalHeader().resizeSection(row, height) def select_and_scroll_to_row(self, row): self.setFocus() self.selectRow(row) self.scrollToItem(self.currentItem()) def set_element_name_in_row(self, row, col, name): rule_name = QLabel(" %s " % name) rule_name.setFont(self.FONT) self.setCellWidget(row, col, rule_name) def set_css_in_row(self, row, col, css): # Clean up multi-line css formatting # A single line is 30px tall, subsequent lines add 16px lines = [] for line in css.split('\n'): lines.append(re.sub('^\s*', '', line)) css_content = QPlainTextEdit('\n'.join(lines)) css_content.setFont(self.FONT) css_content.textChanged.connect(partial(self.css_edited, row)) self.setCellWidget(row, col, css_content) self.resize_row_height(lines, row) def undo_reset_button_clicked(self, mode): """ Figure out which element is being reset Reset to last save or default """ debug_print("undo_reset_button_clicked - mode=", mode) row = self.currentRow() data = self.convert_row_to_data(row) debug_print("undo_reset_button_clicked - data=", data) # Get default default_css = None for de in default_elements: if de['name'] == data['name']: default_css = de break # Get last saved last_saved_css = None saved_elements = self.prefs.get('appearance_css', None) last_saved_css = default_css debug_print("undo_reset_button_clicked - saved_elements=", saved_elements) debug_print("undo_reset_button_clicked - last_saved_css=", last_saved_css) if saved_elements: for se in saved_elements: if se['name'] == data['name']: debug_print("undo_reset_button_clicked - se=", se) last_saved_css = se break debug_print("undo_reset_button_clicked - last_saved_css=", last_saved_css) # Restore css if mode == 'reset': self.populate_table_row(row, default_css) elif mode == 'undo': self.populate_table_row(row, last_saved_css) # Refresh the stored data #self.prefs.set('appearance_css', self.get_data()) self.css_edited(row)
def __init__(self, right=False, parent=None, show_open_in_editor=False): PlainTextEdit.__init__(self, parent) self.setFrameStyle(0) self.show_open_in_editor = show_open_in_editor self.side_margin = 0 self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) self.setFocusPolicy(Qt.NoFocus) self.right = right self.setReadOnly(True) w = self.fontMetrics() self.number_width = max(map(lambda x: w.width(str(x)), xrange(10))) self.space_width = w.width(' ') self.setLineWrapMode(self.WidgetWidth) self.setTabStopWidth(tprefs['editor_tab_stop_width'] * self.space_width) font = self.font() ff = tprefs['editor_font_family'] if ff is None: ff = default_font_family() font.setFamily(ff) font.setPointSize(tprefs['editor_font_size']) self.setFont(font) font = self.heading_font = QFont(self.font()) font.setPointSize(int(tprefs['editor_font_size'] * 1.5)) font.setBold(True) theme = get_theme(tprefs['editor_theme']) pal = self.palette() pal.setColor(pal.Base, theme_color(theme, 'Normal', 'bg')) pal.setColor(pal.AlternateBase, theme_color(theme, 'CursorLine', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'Normal', 'fg')) pal.setColor(pal.Highlight, theme_color(theme, 'Visual', 'bg')) pal.setColor(pal.HighlightedText, theme_color(theme, 'Visual', 'fg')) self.setPalette(pal) self.viewport().setCursor(Qt.ArrowCursor) self.line_number_area = LineNumbers(self) self.blockCountChanged[int].connect(self.update_line_number_area_width) self.updateRequest.connect(self.update_line_number_area) self.line_number_palette = pal = QPalette() pal.setColor(pal.Base, theme_color(theme, 'LineNr', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'LineNr', 'fg')) pal.setColor(pal.BrightText, theme_color(theme, 'LineNrC', 'fg')) self.line_number_map = LineNumberMap() self.search_header_pos = 0 self.changes, self.headers, self.images = [], [], OrderedDict() self.setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff), self.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff) self.diff_backgrounds = { 'replace': theme_color(theme, 'DiffReplace', 'bg'), 'insert': theme_color(theme, 'DiffInsert', 'bg'), 'delete': theme_color(theme, 'DiffDelete', 'bg'), 'replacereplace': theme_color(theme, 'DiffReplaceReplace', 'bg'), 'boundary': QBrush(theme_color(theme, 'Normal', 'fg'), Qt.Dense7Pattern), } self.diff_foregrounds = { 'replace': theme_color(theme, 'DiffReplace', 'fg'), 'insert': theme_color(theme, 'DiffInsert', 'fg'), 'delete': theme_color(theme, 'DiffDelete', 'fg'), 'boundary': QColor(0, 0, 0, 0), } for x in ('replacereplace', 'insert', 'delete'): f = QTextCharFormat() f.setBackground(self.diff_backgrounds[x]) setattr(self, '%s_format' % x, f)
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 self.href = toc.href 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 iteritems(anchor_map)} 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 build_font_obj(self): font_info = qt_app.original_font if self.current_font is None else self.current_font font = QFont(*(font_info[:4])) font.setStretch(font_info[4]) return font
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 clear_button(self, which): b = getattr(self, 'button%d' % which) s = getattr(self, 'shortcut%d' % which, None) b.setText( _('None') if s is None else s.toString(QKeySequence.NativeText)) b.setFont(QFont())
def __init__(self, parent=None, is_half_star=False): QAbstractListModel.__init__(self, parent) self.is_half_star = is_half_star self.rating_font = QFont(rating_font()) self.null_text = _('Not rated')
def mark_item_as_current(self, item): font = QFont(self.font()) font.setItalic(True) font.setBold(True) item.setData(0, Qt.FontRole, font)