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
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 = value.toInt()[0] 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)
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 = value.toInt()[0] 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 do_paint(self, painter, option, index): text = unicode(index.data(Qt.DisplayRole).toString()) font = QFont(option.font) font.setPointSize(QFontInfo(font).pointSize() * 1.5) font2 = QFont(font) font2.setFamily(text) system, has_latin = writing_system_for_font(font2) if has_latin: font = font2 r = option.rect if option.state & QStyle.State_Selected: painter.setPen(QPen(option.palette.highlightedText(), 0)) if (option.direction == Qt.RightToLeft): r.setRight(r.right() - 4) else: r.setLeft(r.left() + 4) painter.setFont(font) painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, text) if (system != QFontDatabase.Any): w = painter.fontMetrics().width(text + " ") painter.setFont(font2) sample = QFontDatabase().writingSystemSample(system) if (option.direction == Qt.RightToLeft): r.setRight(r.right() - w) else: r.setLeft(r.left() + w) painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, sample)
def _getHeaderFont(self): """ Returns the font used for the header. @return: the header font @rtype: QFont """ font = QFont() font.setFamily(PM_HEADER_FONT) font.setPointSize(PM_HEADER_FONT_POINT_SIZE) font.setBold(PM_HEADER_FONT_BOLD) return font
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 getButtonFont(self): """ Returns the font for the tool buttons in the grid. @return: Button font. @rtype: U{B{QFont}<http://doc.trolltech.com/4/qfont.html>} """ # Font for tool buttons. buttonFont = QFont(self.font()) buttonFont.setFamily(BUTTON_FONT) buttonFont.setPointSize(BUTTON_FONT_POINT_SIZE) buttonFont.setBold(BUTTON_FONT_BOLD) return buttonFont
def __init__(self, parent, icon_name, title): QHBoxLayout.__init__(self) title_font = QFont() title_font.setPointSize(16) title_image_label = QLabel(parent) pixmap = get_pixmap(icon_name) if pixmap is None: error_dialog(parent, _('Restart required'), _('You must restart Calibre before using this plugin!'), show=True) else: title_image_label.setPixmap(pixmap) title_image_label.setMaximumSize(32, 32) title_image_label.setScaledContents(True) self.addWidget(title_image_label) shelf_label = QLabel(title, parent) shelf_label.setFont(title_font) self.addWidget(shelf_label) self.insertStretch(-1)
def __init__(self, parent, icon_name, title): QHBoxLayout.__init__(self) title_font = QFont() title_font.setPointSize(16) title_image_label = QLabel(parent) pixmap = QPixmap() pixmap.load(I(icon_name)) if pixmap is None: error_dialog(parent, _('Restart required'), _('You must restart Calibre before using this plugin!'), show=True) else: title_image_label.setPixmap(pixmap) title_image_label.setMaximumSize(32, 32) title_image_label.setScaledContents(True) self.addWidget(title_image_label) shelf_label = QLabel(title, parent) shelf_label.setFont(title_font) self.addWidget(shelf_label) self.insertStretch(-1)
def __init__(self, widget: Dependency('widget/', lambda v: hasattr(v.view, 'mode'))): view = widget.view mode = view.mode if mode.name in self.mode_lexer_map: lexer_cls = self.mode_lexer_map[mode.name] else: lexer_name_possibilities = ["QsciLexer" + mode.name, "QsciLexer" + mode.name.title(), "QsciLexer" + mode.name.upper()] for lexer_name in lexer_name_possibilities: if hasattr(Qsci, lexer_name): lexer_cls = getattr(Qsci, lexer_name, None) break else: lexer_cls = None if lexer_cls is not None: font = QFont() font.setFamily('DejaVu Sans Mono') font.setFixedPitch(True) font.setPointSize(10) lexer = lexer_cls(widget) lexer.setDefaultFont(font) widget.setLexer(lexer)
def do_paint(self, painter, option, index): text = unicode(index.data(Qt.DisplayRole).toString()) font = QFont(option.font) font.setPointSize(QFontInfo(font).pointSize() * 1.5) font2 = QFont(font) font2.setFamily(text) system, has_latin = writing_system_for_font(font2) if has_latin: font = font2 r = option.rect if option.state & QStyle.State_Selected: painter.setPen(QPen(option.palette.highlightedText(), 0)) if (option.direction == Qt.RightToLeft): r.setRight(r.right() - 4) else: r.setLeft(r.left() + 4) painter.setFont(font) painter.drawText(r, Qt.AlignVCenter | Qt.AlignLeading | Qt.TextSingleLine, text) if (system != QFontDatabase.Any): w = painter.fontMetrics().width(text + " ") painter.setFont(font2) sample = QFontDatabase().writingSystemSample(system) if (option.direction == Qt.RightToLeft): r.setRight(r.right() - w) else: r.setLeft(r.left() + w) painter.drawText( r, Qt.AlignVCenter | Qt.AlignLeading | Qt.TextSingleLine, sample)
class TextEdit(PlainTextEdit): def __init__(self, parent=None): PlainTextEdit.__init__(self, parent) self.saved_matches = {} self.smarts = NullSmarts(self) self.current_cursor_line = None self.current_search_mark = None self.highlighter = SyntaxHighlighter(self) 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) self.syntax = None @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): # {{{ prefs = prefs or tprefs self.setLineWrapMode(QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = THEMES.get(prefs['editor_theme'], None) if theme is None: theme = THEMES[default_theme()] self.apply_theme(theme) w = self.fontMetrics() self.space_width = w.width(' ') self.setTabStopWidth(prefs['editor_tab_stop_width'] * self.space_width) 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(100 * w.averageCharWidth(), 50 * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line() # }}} def load_text(self, text, syntax='html', process_template=False): self.syntax = syntax self.highlighter = get_highlighter(syntax)(self) self.highlighter.apply_theme(self.theme) self.highlighter.setDocument(self.document()) sclass = {'html':HTMLSmarts, 'xml':HTMLSmarts}.get(syntax, None) if sclass is not None: self.smarts = sclass(self) self.setPlainText(unicodedata.normalize('NFC', text)) if process_template and QPlainTextEdit.find(self, '%CURSOR%'): c = self.textCursor() c.insertText('') 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 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): 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) sel.extend(self.smarts.get_extra_selections(self)) 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] = 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: raw, count = pat.subn(template, raw) if count > 0: c.setKeepPositionOnInsert(True) c.insertText(raw) c.setKeepPositionOnInsert(False) self.update_extra_selections() return count 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] = m return True 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 = self.saved_matches.pop(saved_match) if saved.group() == raw: m = saved if m is None: return False 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() # 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.setViewportMargins(self.line_number_area_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 event(self, ev): if ev.type() == ev.ToolTip: self.show_tooltip(ev) return True if ev.type() == ev.ShortcutOverride: if ev in ( # Let the global cut/copy/paste shortcuts work,this avoids the nbsp # problem as well, since they use the overridden copy() method # instead of the one from Qt QKeySequence.Copy, QKeySequence.Cut, QKeySequence.Paste, ) or ( # This is used to convert typed hex codes into unicode # characters ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier ): ev.ignore() return False return QPlainTextEdit.event(self, ev) # Tooltips {{{ def syntax_format_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).toBool(): return r.format 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)) QToolTip.hideText() ev.ignore() # }}} 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 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): 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) alt = _('Image') template = '<img alt="{0}" src="%s" />'.format(alt) href = prepare_string_for_xml(href, True) text = template % href c.insertText(text) if self.syntax == 'html': 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 keyPressEvent(self, ev): if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier: if self.replace_possible_unicode_sequence(): ev.accept() return QPlainTextEdit.keyPressEvent(self, ev) if (ev.key() == Qt.Key_Semicolon or ';' in unicode(ev.text())) and tprefs['replace_entities_as_typed'] and self.syntax == 'html': self.replace_possible_entity() 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 from calibre.gui2.tweak_book.char_select import chr end_pos = max(c.anchor(), c.position()) c.setPosition(end_pos - len(text)), c.setPosition(end_pos, c.KeepAnchor) c.insertText(chr(num)) return True def replace_possible_entity(self): c = self.textCursor() c.setPosition(c.position() - min(c.positionInBlock(), 10), c.KeepAnchor) text = unicode(c.selectedText()).rstrip('\0') m = entity_pat.search(text) if m is None: return ent = m.group() repl = xml_entity_to_unicode(m) if repl != ent: c.setPosition(c.position() + m.start(), c.KeepAnchor) c.insertText(repl) 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 ui_MMKit_GroupBox(self, MMKitDialog): #Start MMKit groupbox (includes atom, clipboard and library tabs) self.MMKit_groupBox = QtGui.QGroupBox(MMKitDialog) self.MMKit_groupBox.setObjectName("MMKit_groupBox") self.MMKit_groupBox.setAutoFillBackground(True) palette = MMKitDialog.getGroupBoxPalette() self.MMKit_groupBox.setPalette(palette) styleSheet = MMKitDialog.getGroupBoxStyleSheet() self.MMKit_groupBox.setStyleSheet(styleSheet) self.MMKitGrpBox_VBoxLayout = QtGui.QVBoxLayout(self.MMKit_groupBox) self.MMKitGrpBox_VBoxLayout.setMargin(pmGrpBoxVboxLayoutMargin) self.MMKitGrpBox_VBoxLayout.setSpacing(pmGrpBoxVboxLayoutSpacing) self.MMKitGrpBox_VBoxLayout.setObjectName("MMKitGrpBox_VBoxLayout") self.MMKitGrpBox_TitleButton = MMKitDialog.getGroupBoxTitleButton("MMKit", self.MMKit_groupBox) self.MMKitGrpBox_VBoxLayout.addWidget(self.MMKitGrpBox_TitleButton) self.mmkit_tab = QtGui.QTabWidget(self.MMKit_groupBox) self.mmkit_tab.setEnabled(True) # Height is fixed. Mark 2007-05-29. self.mmkit_tab.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Preferred), QSizePolicy.Policy(QSizePolicy.Fixed))) self.mmkit_tab.setObjectName("mmkit_tab") self.atomsPage = QtGui.QWidget() self.atomsPage.setObjectName("atomsPage") self.mmkit_tab.addTab(self.atomsPage, "") self.atomsPageFrame = QtGui.QFrame(self.atomsPage) # atomsPageFrame needs to be reviewed carefully. Mark 2007-06-20 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Policy(3),QtGui.QSizePolicy.Policy(1)) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.atomsPageFrame.sizePolicy().hasHeightForWidth()) self.atomsPageFrame.setSizePolicy(sizePolicy) self.atomsPageFrame.setFrameShape(QtGui.QFrame.NoFrame) self.atomsPageFrame.setFrameShadow(QtGui.QFrame.Plain) self.atomsPageFrame.setMinimumSize(QtCore.QSize(100,100)) self.atomsPageFrame.setObjectName("atomsPageFrame") self.atomsPage_VBoxLayout = QtGui.QVBoxLayout(self.atomsPageFrame) self.atomsPage_VBoxLayout.setMargin(pmMMKitPageMargin) # Was 4. Mark 2007-05-30 self.atomsPage_VBoxLayout.setSpacing(2) # Element Button GroupBox begins here. ##################### self.elementButtonGroup = QtGui.QGroupBox(self.atomsPageFrame) sizePolicy = QtGui.QSizePolicy( QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) self.elementButtonGroup.setSizePolicy(sizePolicy) self.elementButtonGroup.setMinimumSize( QtCore.QSize(pmMMKitButtonWidth * 4, pmMMKitButtonHeight * 4 + 4)) self.elementButtonGroup.setObjectName("elementButtonGroup") self.MMKit_GridLayout = QtGui.QGridLayout(self.elementButtonGroup) self.MMKit_GridLayout.setMargin(1) # Was 0. Mark 2007-05-30 self.MMKit_GridLayout.setSpacing(0) self.MMKit_GridLayout.setObjectName("MMKit_GridLayout") # Font for toolbuttons. font = QFont(self.atomsPageFrame.font()) font.setFamily(pmMMKitButtonFont) font.setPointSize(pmMMKitButtonFontPointSize) font.setBold(pmMMKitButtonFontBold) #font.setWeight(75) #font.setItalic(False) #font.setUnderline(False) #font.setStrikeOut(False) # All this would be much nicer using a dictionary in a loop. # Later, when time permits. Mark 2007-05-30. self.toolButton1 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton1.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton1.setCheckable(True) self.toolButton1.setFont(font) self.toolButton1.setObjectName("toolButton1") self.MMKit_GridLayout.addWidget(self.toolButton1,0,4,1,1) self.toolButton2 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton2.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton2.setCheckable(True) self.toolButton2.setFont(font) self.toolButton2.setObjectName("toolButton2") self.MMKit_GridLayout.addWidget(self.toolButton2,0,5,1,1) self.toolButton6 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton6.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton6.setCheckable(True) self.toolButton6.setFont(font) self.toolButton6.setObjectName("toolButton6") self.MMKit_GridLayout.addWidget(self.toolButton6,1,1,1,1) self.toolButton7 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton7.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton7.setCheckable(True) self.toolButton7.setFont(font) self.toolButton7.setObjectName("toolButton7") self.MMKit_GridLayout.addWidget(self.toolButton7,1,2,1,1) self.toolButton8 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton8.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton8.setCheckable(True) self.toolButton8.setFont(font) self.toolButton8.setObjectName("toolButton8") self.MMKit_GridLayout.addWidget(self.toolButton8,1,3,1,1) self.toolButton10 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton10.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton10.setCheckable(True) self.toolButton10.setFont(font) self.toolButton10.setObjectName("toolButton10") self.MMKit_GridLayout.addWidget(self.toolButton10,1,5,1,1) self.toolButton9 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton9.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton9.setCheckable(True) self.toolButton9.setFont(font) self.toolButton9.setObjectName("toolButton9") self.MMKit_GridLayout.addWidget(self.toolButton9,1,4,1,1) self.toolButton13 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton13.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton13.setCheckable(True) self.toolButton13.setFont(font) self.toolButton13.setObjectName("toolButton13") self.MMKit_GridLayout.addWidget(self.toolButton13,2,0,1,1) self.toolButton17 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton17.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton17.setCheckable(True) self.toolButton17.setFont(font) self.toolButton17.setObjectName("toolButton17") self.MMKit_GridLayout.addWidget(self.toolButton17,2,4,1,1) self.toolButton5 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton5.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton5.setCheckable(True) self.toolButton5.setFont(font) self.toolButton5.setObjectName("toolButton5") self.MMKit_GridLayout.addWidget(self.toolButton5,1,0,1,1) self.toolButton10_2 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton10_2.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton10_2.setCheckable(True) self.toolButton10_2.setFont(font) self.toolButton10_2.setObjectName("toolButton10_2") self.MMKit_GridLayout.addWidget(self.toolButton10_2,2,5,1,1) self.toolButton15 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton15.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton15.setCheckable(True) self.toolButton15.setFont(font) self.toolButton15.setObjectName("toolButton15") self.MMKit_GridLayout.addWidget(self.toolButton15,2,2,1,1) self.toolButton16 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton16.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton16.setCheckable(True) self.toolButton16.setFont(font) self.toolButton16.setObjectName("toolButton16") self.MMKit_GridLayout.addWidget(self.toolButton16,2,3,1,1) self.toolButton14 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton14.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton14.setCheckable(True) self.toolButton14.setFont(font) self.toolButton14.setObjectName("toolButton14") self.MMKit_GridLayout.addWidget(self.toolButton14,2,1,1,1) self.toolButton33 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton33.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton33.setCheckable(True) self.toolButton33.setFont(font) self.toolButton33.setObjectName("toolButton33") self.MMKit_GridLayout.addWidget(self.toolButton33,3,2,1,1) self.toolButton34 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton34.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton34.setCheckable(True) self.toolButton34.setFont(font) self.toolButton34.setObjectName("toolButton34") self.MMKit_GridLayout.addWidget(self.toolButton34,3,3,1,1) self.toolButton35 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton35.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton35.setCheckable(True) self.toolButton35.setFont(font) self.toolButton35.setObjectName("toolButton35") self.MMKit_GridLayout.addWidget(self.toolButton35,3,4,1,1) self.toolButton32 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton32.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton32.setCheckable(True) self.toolButton32.setFont(font) self.toolButton32.setObjectName("toolButton32") self.MMKit_GridLayout.addWidget(self.toolButton32,3,1,1,1) self.toolButton36 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton36.setFixedSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.toolButton36.setCheckable(True) self.toolButton36.setFont(font) self.toolButton36.setObjectName("toolButton36") self.MMKit_GridLayout.addWidget(self.toolButton36,3,5,1,1) self.atomsPage_VBoxLayout.addWidget(self.elementButtonGroup) # Height is fixed (i.e. locked). Mark 2007-05-29. self.elementButtonGroup.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Preferred), QSizePolicy.Policy(QSizePolicy.Fixed))) # Atomic Hybrid label self.atomic_hybrids_label = QtGui.QLabel(self.atomsPageFrame) self.atomic_hybrids_label.setText("Atomic Hybrids :") self.atomsPage_VBoxLayout.addWidget(self.atomic_hybrids_label) # Elements Button GroupBox ends here. ####################### # This special HBoxLayout contains both the hybrid button group and a # vert spacer (width = 0) to keep the Qt layout working properly # in certain situations like that described in bug 2407. # Mark 2007-06-20. self.special_HBoxLayout = QtGui.QHBoxLayout() self.special_HBoxLayout.setMargin(0) self.special_HBoxLayout.setSpacing(6) self.special_HBoxLayout.setObjectName("special_HBoxLayout") self.atomsPage_VBoxLayout.addLayout(self.special_HBoxLayout) # Hybrid GroupBox begins here ############################### self.hybrid_btngrp = QtGui.QGroupBox(self.atomsPageFrame) self.hybrid_btngrp.setObjectName("hybrid_btngrp") self.special_HBoxLayout.addWidget(self.hybrid_btngrp) self.hybridBtns_HBoxLayout = QtGui.QHBoxLayout(self.hybrid_btngrp) self.hybridBtns_HBoxLayout.setMargin(2) self.hybridBtns_HBoxLayout.setSpacing(0) self.hybridBtns_HBoxLayout.setObjectName("hybridBtns_HBoxLayout") self.sp3_btn = QtGui.QToolButton(self.hybrid_btngrp) self.sp3_btn.setMinimumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.sp3_btn.setMaximumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.sp3_btn.setCheckable(True) self.sp3_btn.setObjectName("sp3_btn") self.hybridBtns_HBoxLayout.addWidget(self.sp3_btn) self.sp2_btn = QtGui.QToolButton(self.hybrid_btngrp) self.sp2_btn.setMinimumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.sp2_btn.setMaximumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.sp2_btn.setCheckable(True) self.sp2_btn.setObjectName("sp2_btn") self.hybridBtns_HBoxLayout.addWidget(self.sp2_btn) self.sp_btn = QtGui.QToolButton(self.hybrid_btngrp) self.sp_btn.setMinimumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.sp_btn.setMaximumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.sp_btn.setCheckable(True) self.sp_btn.setObjectName("sp_btn") self.hybridBtns_HBoxLayout.addWidget(self.sp_btn) self.graphitic_btn = QtGui.QToolButton(self.hybrid_btngrp) self.graphitic_btn.setMinimumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.graphitic_btn.setMaximumSize(QtCore.QSize(pmMMKitButtonWidth,pmMMKitButtonHeight)) self.graphitic_btn.setCheckable(True) self.graphitic_btn.setObjectName("graphitic_btn") self.hybridBtns_HBoxLayout.addWidget(self.graphitic_btn) # This VSpacer is needed to help (but not completely) fix bug 2407. It # maintains the height of the layout(s) containing the hybrid button # group when it is hidden using hide(). Without this spacer the layout # gets screwed up in special situations like that described in bug 2407. # The + 10 below is needed to account for the margin (4 pixels) and # the additional 6 just help (I have a theory that the height of the # frame containing the label above the hybrid group box shrinks when # the label is hidden by inserting a space character). I'm # not going to worry about this now. +10 works well enough. # Mark 2007-06-20. VSpacer = QtGui.QSpacerItem(0, pmMMKitButtonHeight + 10, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) #self.hybridBtns_HBoxLayout.addItem(VSpacer) self.special_HBoxLayout.addItem(VSpacer) self.hybridBtns_HBoxLayout.addStretch(0) # Height is fixed. Mark 2007-05-29. self.hybrid_btngrp.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.MinimumExpanding), QSizePolicy.Policy(QSizePolicy.Fixed))) # This spacer keeps the MMKit button grid compressed when # the hybrid button group is hidden. self.atomsPageBottomVSpacer = \ QtGui.QSpacerItem(5, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.MinimumExpanding) self.atomsPage_VBoxLayout.addItem(self.atomsPageBottomVSpacer) # Clipboard page begins here ############################################ self.clipboardPage = QtGui.QWidget() self.clipboardPage.setObjectName("clipboardPage") self.gridlayout3 = QtGui.QGridLayout(self.clipboardPage) self.gridlayout3.setMargin(pmMMKitPageMargin) # Was 4. Mark 2007-05-30 self.gridlayout3.setSpacing(2) self.gridlayout3.setObjectName("gridlayout3") self.chunkListBox = QtGui.QListWidget(self.clipboardPage) self.chunkListBox.setMinimumSize(QtCore.QSize(100,100)) # Height is fixed. Mark 2007-05-29. self.chunkListBox.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.MinimumExpanding), QSizePolicy.Policy(QSizePolicy.Fixed))) self.chunkListBox.setObjectName("chunkListBox") self.gridlayout3.addWidget(self.chunkListBox,0,0,1,1) self.mmkit_tab.addTab(self.clipboardPage, "") self.libraryPage = QtGui.QWidget() #self.libraryPage = QtGui.QScrollArea() #self.libraryPageWidget = QtGui.QWidget() #self.libraryPage.setWidget(self.libraryPageWidget) self.libraryPage.setObjectName("libraryPage") self.mmkit_tab.addTab(self.libraryPage, "") self.MMKitGrpBox_VBoxLayout.addWidget(self.mmkit_tab) self.transmuteAtomsAction = QtGui.QWidgetAction(self.w) self.transmuteAtomsAction.setText("Transmute Atoms") self.transmuteAtomsAction.setIcon(geticon( 'ui/actions/Toolbars/Smart/Transmute_Atoms')) self.transmuteAtomsAction.setCheckable(False) transmuteBtn_HBoxLayout = QtGui.QHBoxLayout() self.transmuteBtn = QtGui.QToolButton(self.MMKit_groupBox) self.transmuteBtn.setDefaultAction(self.transmuteAtomsAction) self.transmuteBtn.setFixedSize(QtCore.QSize(36, 36)) self.transmuteBtn.setIconSize(QtCore.QSize(22,22)) transmuteBtn_HBoxLayout.addWidget(self.transmuteBtn) self.browseButton = QtGui.QPushButton(MMKitDialog) transmuteBtn_HBoxLayout.addWidget(self.browseButton) self.defaultPartLibButton = QtGui.QPushButton(MMKitDialog) transmuteBtn_HBoxLayout.addWidget(self.defaultPartLibButton) self.atomsPageSpacer = QtGui.QSpacerItem(0, 5, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) transmuteBtn_HBoxLayout.addItem(self.atomsPageSpacer) self.MMKitGrpBox_VBoxLayout.addLayout(transmuteBtn_HBoxLayout) self.transmuteCB = QtGui.QCheckBox(" Force to Keep Bonds", self.MMKit_groupBox) self.MMKitGrpBox_VBoxLayout.addWidget(self.transmuteCB) #End MMKit groupbox self.pmVBoxLayout.addWidget(self.MMKit_groupBox) # This line is important. Without it, the MMKit groupbox is # too wide by default and causes a horizontal scrollbar # to be displayed at the bottom of the PropMgr. Mark 2007-05-30 self.MMKit_groupBox.setMinimumWidth(200) # Height is fixed. Mark 2007-05-29. self.MMKit_groupBox.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.MinimumExpanding), QSizePolicy.Policy(QSizePolicy.Fixed)))
def __init__(self, parent = None, desc = None, name = None, modal = 0, fl = 0, env = None, type = "QDialog"): if env is None: import foundation.env as env # this is a little weird... probably it'll be ok, and logically it seems correct. self.desc = desc self.typ = type if type == "QDialog": QDialog.__init__(self,parent,name,modal,fl) elif type == "QTextEdit": QTextEdit.__init__(self, parent, name) elif type == "QFrame": QFrame.__init__(self,parent,name) else: print "don't know about type == %r" % (type,) self.image1 = QPixmap() self.image1.loadFromData(image1_data,"PNG") # should be: title_icon #### self.image3 = QPixmap() self.image3.loadFromData(image3_data,"PNG") self.image4 = QPixmap() self.image4.loadFromData(image4_data,"PNG") self.image5 = QPixmap() self.image5.loadFromData(image5_data,"PNG") self.image6 = QPixmap() self.image6.loadFromData(image6_data,"PNG") self.image7 = QPixmap() self.image7.loadFromData(image7_data,"PNG") self.image0 = QPixmap(image0_data) # should be: border_icon #### self.image2 = QPixmap(image2_data) # should be: sponsor_pixmap #### try: ####@@@@ title_icon_name = self.desc.options.get('title_icon') border_icon_name = self.desc.options.get('border_icon') if title_icon_name: self.image1 = imagename_to_pixmap(title_icon_name) ###@@@ pass icon_path ###@@@ import imagename_to_pixmap or use env function # or let that func itself be an arg, or have an env arg for it ###e rename it icon_name_to_pixmap, or find_icon? (the latter only if it's ok if it returns an iconset) ###e use iconset instead? if border_icon_name: self.image0 = imagename_to_pixmap(border_icon_name) except: print_compact_traceback("bug in icon-setting code, using fallback icons: ") pass if not name: self.setName("parameter_dialog_or_frame") ### ###k guess this will need: if type == 'QDialog' self.setIcon(self.image0) # should be: border_icon #### nanotube_dialogLayout = QVBoxLayout(self,0,0,"nanotube_dialogLayout") self.heading_frame = QFrame(self,"heading_frame") self.heading_frame.setPaletteBackgroundColor(QColor(122,122,122)) self.heading_frame.setFrameShape(QFrame.NoFrame) self.heading_frame.setFrameShadow(QFrame.Plain) heading_frameLayout = QHBoxLayout(self.heading_frame,0,3,"heading_frameLayout") self.heading_pixmap = QLabel(self.heading_frame,"heading_pixmap") self.heading_pixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.heading_pixmap.sizePolicy().hasHeightForWidth())) self.heading_pixmap.setPixmap(self.image1) # should be: title_icon #### self.heading_pixmap.setScaledContents(1) heading_frameLayout.addWidget(self.heading_pixmap) self.heading_label = QLabel(self.heading_frame,"heading_label") self.heading_label.setPaletteForegroundColor(QColor(255,255,255)) heading_label_font = QFont(self.heading_label.font()) heading_label_font.setPointSize(12) heading_label_font.setBold(1) self.heading_label.setFont(heading_label_font) heading_frameLayout.addWidget(self.heading_label) nanotube_dialogLayout.addWidget(self.heading_frame) self.body_frame = QFrame(self,"body_frame") self.body_frame.setFrameShape(QFrame.StyledPanel) self.body_frame.setFrameShadow(QFrame.Raised) body_frameLayout = QVBoxLayout(self.body_frame,3,3,"body_frameLayout") self.sponsor_frame = QFrame(self.body_frame,"sponsor_frame") self.sponsor_frame.setPaletteBackgroundColor(QColor(255,255,255)) self.sponsor_frame.setFrameShape(QFrame.StyledPanel) self.sponsor_frame.setFrameShadow(QFrame.Raised) sponsor_frameLayout = QHBoxLayout(self.sponsor_frame,0,0,"sponsor_frameLayout") self.sponsor_btn = QPushButton(self.sponsor_frame,"sponsor_btn") self.sponsor_btn.setAutoDefault(0) #bruce 060703 bugfix self.sponsor_btn.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred,0,0,self.sponsor_btn.sizePolicy().hasHeightForWidth())) self.sponsor_btn.setPaletteBackgroundColor(QColor(255,255,255)) self.sponsor_btn.setPixmap(self.image2) # should be: sponsor_pixmap #### [also we'll need to support >1 sponsor] self.sponsor_btn.setFlat(1) sponsor_frameLayout.addWidget(self.sponsor_btn) body_frameLayout.addWidget(self.sponsor_frame) layout59 = QHBoxLayout(None,0,6,"layout59") left_spacer = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum) layout59.addItem(left_spacer) self.done_btn = QToolButton(self.body_frame,"done_btn") self.done_btn.setIcon(QIcon(self.image3)) layout59.addWidget(self.done_btn) self.abort_btn = QToolButton(self.body_frame,"abort_btn") self.abort_btn.setIcon(QIcon(self.image4)) layout59.addWidget(self.abort_btn) self.preview_btn = QToolButton(self.body_frame,"preview_btn") self.preview_btn.setIcon(QIcon(self.image5)) layout59.addWidget(self.preview_btn) self.whatsthis_btn = QToolButton(self.body_frame,"whatsthis_btn") self.whatsthis_btn.setIcon(QIcon(self.image6)) layout59.addWidget(self.whatsthis_btn) right_spacer = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum) layout59.addItem(right_spacer) body_frameLayout.addLayout(layout59) self.groups = [] self.param_getters = {} # map from param name to get-function (which gets current value out of its widget or controller) for group_desc in self.desc.kids('group'): # == start parameters_grpbox ### this will differ for Windows style header_refs = [] # keep python refcounted refs to all objects we make (at least the ones pyuic stored in self attrs) self.parameters_grpbox = QGroupBox(self.body_frame,"parameters_grpbox") self.parameters_grpbox.setFrameShape(QGroupBox.StyledPanel) self.parameters_grpbox.setFrameShadow(QGroupBox.Sunken) self.parameters_grpbox.setMargin(0) self.parameters_grpbox.setColumnLayout(0,Qt.Vertical) self.parameters_grpbox.layout().setSpacing(1) self.parameters_grpbox.layout().setMargin(4) parameters_grpboxLayout = QVBoxLayout(self.parameters_grpbox.layout()) parameters_grpboxLayout.setAlignment(Qt.AlignTop) layout20 = QHBoxLayout(None,0,6,"layout20") self.nt_parameters_grpbtn = QPushButton(self.parameters_grpbox,"nt_parameters_grpbtn") self.nt_parameters_grpbtn.setSizePolicy(QSizePolicy(QSizePolicy.Minimum,QSizePolicy.Fixed,0,0,self.nt_parameters_grpbtn.sizePolicy().hasHeightForWidth())) self.nt_parameters_grpbtn.setMaximumSize(QSize(16,16)) self.nt_parameters_grpbtn.setAutoDefault(0) self.nt_parameters_grpbtn.setIcon(QIcon(self.image7)) ### not always right, but doesn't matter self.nt_parameters_grpbtn.setFlat(1) layout20.addWidget(self.nt_parameters_grpbtn) self.parameters_grpbox_label = QLabel(self.parameters_grpbox,"parameters_grpbox_label") self.parameters_grpbox_label.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Minimum,0,0,self.parameters_grpbox_label.sizePolicy().hasHeightForWidth())) self.parameters_grpbox_label.setAlignment(QLabel.AlignVCenter) layout20.addWidget(self.parameters_grpbox_label) gbx_spacer1 = QSpacerItem(67,16,QSizePolicy.Expanding,QSizePolicy.Minimum) layout20.addItem(gbx_spacer1) parameters_grpboxLayout.addLayout(layout20) nt_parameters_body_layout = QGridLayout(None,1,1,0,6,"nt_parameters_body_layout") ### what is 6 -- is it related to number of items??? # is it 6 in all the ones we got, but that could be a designer error so i better look it up sometime. # == start its kids # will use from above: self.parameters_grpbox, nt_parameters_body_layout nextrow = 0 # which row of the QGridLayout to start filling next (loop variable) hidethese = [] # set of objects to hide or show, when this group is closed or opened for param in group_desc.kids('parameter'): # param (a group subobj desc) is always a parameter, but we already plan to extend this beyond that, # so we redundantly test for this here. getter = None paramname = None # set these for use by uniform code at the end (e.g. for tooltips) editfield = None label = None if param.isa('parameter'): label = QLabel(self.parameters_grpbox,"members_label") label.setAlignment(QLabel.AlignVCenter | QLabel.AlignRight) nt_parameters_body_layout.addWidget(label,nextrow,0) hidethese.append(label) thisrow = nextrow nextrow += 1 #e following should be known in a place that knows the input language, not here paramname = param.options.get('name') or (param.args and param.args[0]) or "?" paramlabel = param.options.get('label') or paramname ##e wrong, label "" or none ought to be possible # QtGui.QApplication.translate(self.__class__.__name__, "xyz") label.setText(QtGui.QApplication.translate(self.__class__.__name__, paramlabel)) if param.isa('parameter', widget = 'combobox', type = ('str',None)): self.members_combox = QComboBox(0,self.parameters_grpbox,"members_combox") ###k what's 0? editfield = self.members_combox #### it probably needs a handler class, and then that could do this setup self.members_combox.clear() default = param.options.get('default', None) # None is not equal to any string thewidgetkid = param.kids('widget')[-1] # kluge; need to think what the desc method for this should be for item in thewidgetkid.kids('item'): itemval = item.args[0] itemtext = itemval self.members_combox.insertItem(QtGui.QApplication.translate(self.__class__.__name__, itemtext)) #k __tr ok?? if itemval == default: #k or itemtext? pass ##k i find no setItem in our py code, so not sure yet what to do for this. nt_parameters_body_layout.addWidget(self.members_combox,thisrow,1) hidethese.append(self.members_combox) getter = (lambda combobox = self.members_combox: str(combobox.currentText())) ##e due to __tr or non-str values, it might be better to use currentIndex and look it up in a table # (though whether __tr is good here might depend on what it's used for) elif param.isa('parameter', widget = ('lineedit', None), type = ('str',None)): # this covers explicit str|lineedit, and 3 default cases str, lineedit, neither. # (i.e. if you say parameter and nothing else, it's str lineedit by default.) self.length_linedit = QLineEdit(self.parameters_grpbox,"length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget(self.length_linedit,thisrow,1) hidethese.append(self.length_linedit) default = str(param.options.get('default', "")) self.length_linedit.setText(QtGui.QApplication.translate(self.__class__.__name__, default)) # __tr ok? getter = (lambda lineedit = self.length_linedit: str(lineedit.text())) elif param.isa('parameter', widget = ('lineedit', None), type = 'float'): self.length_linedit = QLineEdit(self.parameters_grpbox,"length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget(self.length_linedit,thisrow,1) hidethese.append(self.length_linedit) controller = FloatLineeditController_Qt(self, param, self.length_linedit) header_refs.append(controller) getter = controller.get_value elif param.isa('parameter', widget = ('spinbox', None), type = 'int') or \ param.isa('parameter', widget = ('spinbox'), type = None): self.chirality_N_spinbox = QSpinBox(self.parameters_grpbox,"chirality_N_spinbox") # was chirality_m_spinbox, now chirality_N_spinbox editfield = self.chirality_N_spinbox ### seems like Qt defaults for min and max are 0,100 -- way too small a range! if param.options.has_key('min') or 1: self.chirality_N_spinbox.setMinimum(param.options.get('min', -999999999)) # was 0 if param.options.has_key('max') or 1: self.chirality_N_spinbox.setMaximum(param.options.get('max', +999999999)) # wasn't in egcode, but needed self.chirality_N_spinbox.setValue(param.options.get('default', 0)) # was 5 ##e note: i suspect this default 0 should come from something that knows this desc grammar. suffix = param.options.get('suffix', '') if suffix: self.chirality_N_spinbox.setSuffix(QtGui.QApplication.translate(self.__class__.__name__, suffix)) else: self.chirality_N_spinbox.setSuffix(QString.null) # probably not needed nt_parameters_body_layout.addWidget(self.chirality_N_spinbox,thisrow,1) hidethese.append(self.chirality_N_spinbox) getter = self.chirality_N_spinbox.value # note: it also has .text, which includes suffix else: print "didn't match:",param ###e improve this # things done the same way for all kinds of param-editing widgets if 1: #bruce 060703 moved this down here, as bugfix # set tooltip (same one for editfield and label) tooltip = param.options.get('tooltip', '') ###e do it for more kinds of params; share the code somehow; do it in controller, or setup-aid? ###k QToolTip appropriateness; tooltip option might be entirely untested if tooltip and label: QToolTip.add(label, QtGui.QApplication.translate(self.__class__.__name__, tooltip)) if tooltip and editfield: QToolTip.add(editfield, QtGui.QApplication.translate(self.__class__.__name__, tooltip)) ##k ok?? review once not all params have same-row labels. if getter and paramname and paramname != '?': self.param_getters[paramname] = getter ### also bind these params to actions... continue # next param header_refs.extend( [self.parameters_grpbox, self.nt_parameters_grpbtn, self.parameters_grpbox_label] ) # now create the logic/control object for the group group = CollapsibleGroupController_Qt(self, group_desc, header_refs, hidethese, self.nt_parameters_grpbtn) ### maybe ask env for the class to use for this? self.groups.append(group) ### needed?? only for scanning the params, AFAIK -- oh, and to maintain a python refcount. # from languageChange: if 1: # i don't know if these are needed: self.parameters_grpbox.setTitle(QString.null) self.nt_parameters_grpbtn.setText(QString.null) self.parameters_grpbox_label.setText(QtGui.QApplication.translate(self.__class__.__name__, group_desc.args[0])) # was "Nanotube Parameters" ##e note that it's questionable in the syntax design for this property of a group (overall group label) # to be in that position (desc arg 0). # == end its kids parameters_grpboxLayout.addLayout(nt_parameters_body_layout) body_frameLayout.addWidget(self.parameters_grpbox) # == end parameters groupbox continue # next group nanotube_dialogLayout.addWidget(self.body_frame) spacer14 = QSpacerItem(20,20,QSizePolicy.Minimum,QSizePolicy.Expanding) nanotube_dialogLayout.addItem(spacer14) layout42 = QHBoxLayout(None,4,6,"layout42") btm_spacer = QSpacerItem(59,20,QSizePolicy.Expanding,QSizePolicy.Minimum) layout42.addItem(btm_spacer) self.cancel_btn = QPushButton(self,"cancel_btn") self.cancel_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.cancel_btn) self.ok_btn = QPushButton(self,"ok_btn") self.ok_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.ok_btn) nanotube_dialogLayout.addLayout(layout42) self.languageChange() self.resize(QSize(246,618).expandedTo(self.minimumSizeHint())) ### this size will need to be adjusted (guess -- it's only place overall size is set) qt4todo('self.clearWState(Qt.WState_Polished)') ## self.connect(self.nt_parameters_grpbtn,SIGNAL("clicked()"),self.toggle_nt_parameters_grpbtn) #### # new: for button, methodname in ((self.sponsor_btn, 'do_sponsor_btn'), #e generalize to more than one sponsor button (self.done_btn, 'do_done_btn'), (self.abort_btn, 'do_abort_btn'), (self.preview_btn, 'do_preview_btn'), (self.whatsthis_btn, 'do_whatsthis_btn'), (self.cancel_btn, 'do_cancel_btn'), (self.ok_btn, 'do_ok_btn')): if hasattr(self, methodname): self.connect(button, SIGNAL("clicked()"), getattr(self, methodname)) return
def ui_MMKit_GroupBox(self, MMKitDialog): #Start MMKit groupbox (includes atom, clipboard and library tabs) self.MMKit_groupBox = QtGui.QGroupBox(MMKitDialog) self.MMKit_groupBox.setObjectName("MMKit_groupBox") self.MMKit_groupBox.setAutoFillBackground(True) palette = MMKitDialog.getGroupBoxPalette() self.MMKit_groupBox.setPalette(palette) styleSheet = MMKitDialog.getGroupBoxStyleSheet() self.MMKit_groupBox.setStyleSheet(styleSheet) self.MMKitGrpBox_VBoxLayout = QtGui.QVBoxLayout(self.MMKit_groupBox) self.MMKitGrpBox_VBoxLayout.setMargin(pmGrpBoxVboxLayoutMargin) self.MMKitGrpBox_VBoxLayout.setSpacing(pmGrpBoxVboxLayoutSpacing) self.MMKitGrpBox_VBoxLayout.setObjectName("MMKitGrpBox_VBoxLayout") self.MMKitGrpBox_TitleButton = MMKitDialog.getGroupBoxTitleButton( "MMKit", self.MMKit_groupBox) self.MMKitGrpBox_VBoxLayout.addWidget(self.MMKitGrpBox_TitleButton) self.mmkit_tab = QtGui.QTabWidget(self.MMKit_groupBox) self.mmkit_tab.setEnabled(True) # Height is fixed. Mark 2007-05-29. self.mmkit_tab.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Preferred), QSizePolicy.Policy(QSizePolicy.Fixed))) self.mmkit_tab.setObjectName("mmkit_tab") self.atomsPage = QtGui.QWidget() self.atomsPage.setObjectName("atomsPage") self.mmkit_tab.addTab(self.atomsPage, "") self.atomsPageFrame = QtGui.QFrame(self.atomsPage) # atomsPageFrame needs to be reviewed carefully. Mark 2007-06-20 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Policy(3), QtGui.QSizePolicy.Policy(1)) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.atomsPageFrame.sizePolicy().hasHeightForWidth()) self.atomsPageFrame.setSizePolicy(sizePolicy) self.atomsPageFrame.setFrameShape(QtGui.QFrame.NoFrame) self.atomsPageFrame.setFrameShadow(QtGui.QFrame.Plain) self.atomsPageFrame.setMinimumSize(QtCore.QSize(100, 100)) self.atomsPageFrame.setObjectName("atomsPageFrame") self.atomsPage_VBoxLayout = QtGui.QVBoxLayout(self.atomsPageFrame) self.atomsPage_VBoxLayout.setMargin( pmMMKitPageMargin) # Was 4. Mark 2007-05-30 self.atomsPage_VBoxLayout.setSpacing(2) # Element Button GroupBox begins here. ##################### self.elementButtonGroup = QtGui.QGroupBox(self.atomsPageFrame) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) self.elementButtonGroup.setSizePolicy(sizePolicy) self.elementButtonGroup.setMinimumSize( QtCore.QSize(pmMMKitButtonWidth * 4, pmMMKitButtonHeight * 4 + 4)) self.elementButtonGroup.setObjectName("elementButtonGroup") self.MMKit_GridLayout = QtGui.QGridLayout(self.elementButtonGroup) self.MMKit_GridLayout.setMargin(1) # Was 0. Mark 2007-05-30 self.MMKit_GridLayout.setSpacing(0) self.MMKit_GridLayout.setObjectName("MMKit_GridLayout") # Font for toolbuttons. font = QFont(self.atomsPageFrame.font()) font.setFamily(pmMMKitButtonFont) font.setPointSize(pmMMKitButtonFontPointSize) font.setBold(pmMMKitButtonFontBold) #font.setWeight(75) #font.setItalic(False) #font.setUnderline(False) #font.setStrikeOut(False) # All this would be much nicer using a dictionary in a loop. # Later, when time permits. Mark 2007-05-30. self.toolButton1 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton1.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton1.setCheckable(True) self.toolButton1.setFont(font) self.toolButton1.setObjectName("toolButton1") self.MMKit_GridLayout.addWidget(self.toolButton1, 0, 4, 1, 1) self.toolButton2 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton2.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton2.setCheckable(True) self.toolButton2.setFont(font) self.toolButton2.setObjectName("toolButton2") self.MMKit_GridLayout.addWidget(self.toolButton2, 0, 5, 1, 1) self.toolButton6 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton6.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton6.setCheckable(True) self.toolButton6.setFont(font) self.toolButton6.setObjectName("toolButton6") self.MMKit_GridLayout.addWidget(self.toolButton6, 1, 1, 1, 1) self.toolButton7 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton7.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton7.setCheckable(True) self.toolButton7.setFont(font) self.toolButton7.setObjectName("toolButton7") self.MMKit_GridLayout.addWidget(self.toolButton7, 1, 2, 1, 1) self.toolButton8 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton8.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton8.setCheckable(True) self.toolButton8.setFont(font) self.toolButton8.setObjectName("toolButton8") self.MMKit_GridLayout.addWidget(self.toolButton8, 1, 3, 1, 1) self.toolButton10 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton10.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton10.setCheckable(True) self.toolButton10.setFont(font) self.toolButton10.setObjectName("toolButton10") self.MMKit_GridLayout.addWidget(self.toolButton10, 1, 5, 1, 1) self.toolButton9 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton9.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton9.setCheckable(True) self.toolButton9.setFont(font) self.toolButton9.setObjectName("toolButton9") self.MMKit_GridLayout.addWidget(self.toolButton9, 1, 4, 1, 1) self.toolButton13 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton13.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton13.setCheckable(True) self.toolButton13.setFont(font) self.toolButton13.setObjectName("toolButton13") self.MMKit_GridLayout.addWidget(self.toolButton13, 2, 0, 1, 1) self.toolButton17 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton17.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton17.setCheckable(True) self.toolButton17.setFont(font) self.toolButton17.setObjectName("toolButton17") self.MMKit_GridLayout.addWidget(self.toolButton17, 2, 4, 1, 1) self.toolButton5 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton5.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton5.setCheckable(True) self.toolButton5.setFont(font) self.toolButton5.setObjectName("toolButton5") self.MMKit_GridLayout.addWidget(self.toolButton5, 1, 0, 1, 1) self.toolButton10_2 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton10_2.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton10_2.setCheckable(True) self.toolButton10_2.setFont(font) self.toolButton10_2.setObjectName("toolButton10_2") self.MMKit_GridLayout.addWidget(self.toolButton10_2, 2, 5, 1, 1) self.toolButton15 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton15.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton15.setCheckable(True) self.toolButton15.setFont(font) self.toolButton15.setObjectName("toolButton15") self.MMKit_GridLayout.addWidget(self.toolButton15, 2, 2, 1, 1) self.toolButton16 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton16.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton16.setCheckable(True) self.toolButton16.setFont(font) self.toolButton16.setObjectName("toolButton16") self.MMKit_GridLayout.addWidget(self.toolButton16, 2, 3, 1, 1) self.toolButton14 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton14.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton14.setCheckable(True) self.toolButton14.setFont(font) self.toolButton14.setObjectName("toolButton14") self.MMKit_GridLayout.addWidget(self.toolButton14, 2, 1, 1, 1) self.toolButton33 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton33.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton33.setCheckable(True) self.toolButton33.setFont(font) self.toolButton33.setObjectName("toolButton33") self.MMKit_GridLayout.addWidget(self.toolButton33, 3, 2, 1, 1) self.toolButton34 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton34.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton34.setCheckable(True) self.toolButton34.setFont(font) self.toolButton34.setObjectName("toolButton34") self.MMKit_GridLayout.addWidget(self.toolButton34, 3, 3, 1, 1) self.toolButton35 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton35.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton35.setCheckable(True) self.toolButton35.setFont(font) self.toolButton35.setObjectName("toolButton35") self.MMKit_GridLayout.addWidget(self.toolButton35, 3, 4, 1, 1) self.toolButton32 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton32.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton32.setCheckable(True) self.toolButton32.setFont(font) self.toolButton32.setObjectName("toolButton32") self.MMKit_GridLayout.addWidget(self.toolButton32, 3, 1, 1, 1) self.toolButton36 = QtGui.QToolButton(self.elementButtonGroup) self.toolButton36.setFixedSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.toolButton36.setCheckable(True) self.toolButton36.setFont(font) self.toolButton36.setObjectName("toolButton36") self.MMKit_GridLayout.addWidget(self.toolButton36, 3, 5, 1, 1) self.atomsPage_VBoxLayout.addWidget(self.elementButtonGroup) # Height is fixed (i.e. locked). Mark 2007-05-29. self.elementButtonGroup.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Preferred), QSizePolicy.Policy(QSizePolicy.Fixed))) # Atomic Hybrid label self.atomic_hybrids_label = QtGui.QLabel(self.atomsPageFrame) self.atomic_hybrids_label.setText("Atomic Hybrids :") self.atomsPage_VBoxLayout.addWidget(self.atomic_hybrids_label) # Elements Button GroupBox ends here. ####################### # This special HBoxLayout contains both the hybrid button group and a # vert spacer (width = 0) to keep the Qt layout working properly # in certain situations like that described in bug 2407. # Mark 2007-06-20. self.special_HBoxLayout = QtGui.QHBoxLayout() self.special_HBoxLayout.setMargin(0) self.special_HBoxLayout.setSpacing(6) self.special_HBoxLayout.setObjectName("special_HBoxLayout") self.atomsPage_VBoxLayout.addLayout(self.special_HBoxLayout) # Hybrid GroupBox begins here ############################### self.hybrid_btngrp = QtGui.QGroupBox(self.atomsPageFrame) self.hybrid_btngrp.setObjectName("hybrid_btngrp") self.special_HBoxLayout.addWidget(self.hybrid_btngrp) self.hybridBtns_HBoxLayout = QtGui.QHBoxLayout(self.hybrid_btngrp) self.hybridBtns_HBoxLayout.setMargin(2) self.hybridBtns_HBoxLayout.setSpacing(0) self.hybridBtns_HBoxLayout.setObjectName("hybridBtns_HBoxLayout") self.sp3_btn = QtGui.QToolButton(self.hybrid_btngrp) self.sp3_btn.setMinimumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.sp3_btn.setMaximumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.sp3_btn.setCheckable(True) self.sp3_btn.setObjectName("sp3_btn") self.hybridBtns_HBoxLayout.addWidget(self.sp3_btn) self.sp2_btn = QtGui.QToolButton(self.hybrid_btngrp) self.sp2_btn.setMinimumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.sp2_btn.setMaximumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.sp2_btn.setCheckable(True) self.sp2_btn.setObjectName("sp2_btn") self.hybridBtns_HBoxLayout.addWidget(self.sp2_btn) self.sp_btn = QtGui.QToolButton(self.hybrid_btngrp) self.sp_btn.setMinimumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.sp_btn.setMaximumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.sp_btn.setCheckable(True) self.sp_btn.setObjectName("sp_btn") self.hybridBtns_HBoxLayout.addWidget(self.sp_btn) self.graphitic_btn = QtGui.QToolButton(self.hybrid_btngrp) self.graphitic_btn.setMinimumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.graphitic_btn.setMaximumSize( QtCore.QSize(pmMMKitButtonWidth, pmMMKitButtonHeight)) self.graphitic_btn.setCheckable(True) self.graphitic_btn.setObjectName("graphitic_btn") self.hybridBtns_HBoxLayout.addWidget(self.graphitic_btn) # This VSpacer is needed to help (but not completely) fix bug 2407. It # maintains the height of the layout(s) containing the hybrid button # group when it is hidden using hide(). Without this spacer the layout # gets screwed up in special situations like that described in bug 2407. # The + 10 below is needed to account for the margin (4 pixels) and # the additional 6 just help (I have a theory that the height of the # frame containing the label above the hybrid group box shrinks when # the label is hidden by inserting a space character). I'm # not going to worry about this now. +10 works well enough. # Mark 2007-06-20. VSpacer = QtGui.QSpacerItem(0, pmMMKitButtonHeight + 10, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) #self.hybridBtns_HBoxLayout.addItem(VSpacer) self.special_HBoxLayout.addItem(VSpacer) self.hybridBtns_HBoxLayout.addStretch(0) # Height is fixed. Mark 2007-05-29. self.hybrid_btngrp.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.MinimumExpanding), QSizePolicy.Policy(QSizePolicy.Fixed))) # This spacer keeps the MMKit button grid compressed when # the hybrid button group is hidden. self.atomsPageBottomVSpacer = \ QtGui.QSpacerItem(5, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.MinimumExpanding) self.atomsPage_VBoxLayout.addItem(self.atomsPageBottomVSpacer) # Clipboard page begins here ############################################ self.clipboardPage = QtGui.QWidget() self.clipboardPage.setObjectName("clipboardPage") self.gridlayout3 = QtGui.QGridLayout(self.clipboardPage) self.gridlayout3.setMargin(pmMMKitPageMargin) # Was 4. Mark 2007-05-30 self.gridlayout3.setSpacing(2) self.gridlayout3.setObjectName("gridlayout3") self.chunkListBox = QtGui.QListWidget(self.clipboardPage) self.chunkListBox.setMinimumSize(QtCore.QSize(100, 100)) # Height is fixed. Mark 2007-05-29. self.chunkListBox.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.MinimumExpanding), QSizePolicy.Policy(QSizePolicy.Fixed))) self.chunkListBox.setObjectName("chunkListBox") self.gridlayout3.addWidget(self.chunkListBox, 0, 0, 1, 1) self.mmkit_tab.addTab(self.clipboardPage, "") self.libraryPage = QtGui.QWidget() #self.libraryPage = QtGui.QScrollArea() #self.libraryPageWidget = QtGui.QWidget() #self.libraryPage.setWidget(self.libraryPageWidget) self.libraryPage.setObjectName("libraryPage") self.mmkit_tab.addTab(self.libraryPage, "") self.MMKitGrpBox_VBoxLayout.addWidget(self.mmkit_tab) self.transmuteAtomsAction = QtGui.QWidgetAction(self.w) self.transmuteAtomsAction.setText("Transmute Atoms") self.transmuteAtomsAction.setIcon( geticon('ui/actions/Toolbars/Smart/Transmute_Atoms')) self.transmuteAtomsAction.setCheckable(False) transmuteBtn_HBoxLayout = QtGui.QHBoxLayout() self.transmuteBtn = QtGui.QToolButton(self.MMKit_groupBox) self.transmuteBtn.setDefaultAction(self.transmuteAtomsAction) self.transmuteBtn.setFixedSize(QtCore.QSize(36, 36)) self.transmuteBtn.setIconSize(QtCore.QSize(22, 22)) transmuteBtn_HBoxLayout.addWidget(self.transmuteBtn) self.browseButton = QtGui.QPushButton(MMKitDialog) transmuteBtn_HBoxLayout.addWidget(self.browseButton) self.defaultPartLibButton = QtGui.QPushButton(MMKitDialog) transmuteBtn_HBoxLayout.addWidget(self.defaultPartLibButton) self.atomsPageSpacer = QtGui.QSpacerItem(0, 5, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) transmuteBtn_HBoxLayout.addItem(self.atomsPageSpacer) self.MMKitGrpBox_VBoxLayout.addLayout(transmuteBtn_HBoxLayout) self.transmuteCB = QtGui.QCheckBox(" Force to Keep Bonds", self.MMKit_groupBox) self.MMKitGrpBox_VBoxLayout.addWidget(self.transmuteCB) #End MMKit groupbox self.pmVBoxLayout.addWidget(self.MMKit_groupBox) # This line is important. Without it, the MMKit groupbox is # too wide by default and causes a horizontal scrollbar # to be displayed at the bottom of the PropMgr. Mark 2007-05-30 self.MMKit_groupBox.setMinimumWidth(200) # Height is fixed. Mark 2007-05-29. self.MMKit_groupBox.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.MinimumExpanding), QSizePolicy.Policy(QSizePolicy.Fixed)))
class TextEdit(PlainTextEdit): def __init__(self, parent=None): PlainTextEdit.__init__(self, parent) self.saved_matches = {} 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) self.syntax = None @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.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.setTabStopWidth(prefs["editor_tab_stop_width"] * 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(100 * w.averageCharWidth(), 50 * w.height()) self.highlight_color = theme_color(theme, "HighlightRegion", "bg") self.highlight_cursor_line() # }}} def load_text(self, text, syntax="html", process_template=False): self.syntax = syntax self.highlighter = get_highlighter(syntax)() self.highlighter.apply_theme(self.theme) self.highlighter.set_document(self.document()) sclass = {"html": HTMLSmarts, "xml": HTMLSmarts, "css": CSSSmarts}.get(syntax, None) if sclass is not None: self.smarts = sclass(self) self.setPlainText(unicodedata.normalize("NFC", text)) if process_template and QPlainTextEdit.find(self, "%CURSOR%"): c = self.textCursor() c.insertText("") 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): c = 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: sel.extend(self.smarts.get_extra_selections(self)) 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: raw, count = pat.subn(template, raw) if count > 0: c.setKeepPositionOnInsert(True) c.insertText(raw) c.setKeepPositionOnInsert(False) self.update_extra_selections() return count 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_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.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).toPyObject() is not None: 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 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.setViewportMargins(self.line_number_area_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 event(self, ev): if ev.type() == ev.ToolTip: self.show_tooltip(ev) return True if ev.type() == ev.ShortcutOverride: if ev in ( # Let the global cut/copy/paste shortcuts work,this avoids the nbsp # problem as well, since they use the overridden copy() method # instead of the one from Qt QKeySequence.Copy, QKeySequence.Cut, QKeySequence.Paste, ) or ( # This is used to convert typed hex codes into unicode # characters ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier ): ev.ignore() return False return QPlainTextEdit.event(self, ev) 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(): x = r.format.property(SPELL_PROPERTY).toPyObject() if x is not None and word == x[0]: self.highlighter.reformat_block(block) break block = block.next() # Tooltips {{{ def syntax_format_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).toBool(): return r.format 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)) QToolTip.hideText() ev.ignore() # }}} 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 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): 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) alt = _("Image") template = '<img alt="{0}" src="%s" />'.format(alt) href = prepare_string_for_xml(href, True) text = template % href c.insertText(text) if self.syntax == "html": 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 QPlainTextEdit.keyPressEvent(self, ev) if ( (ev.key() == Qt.Key_Semicolon or ";" in unicode(ev.text())) and tprefs["replace_entities_as_typed"] and self.syntax == "html" ): self.replace_possible_entity() 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 replace_possible_entity(self): c = self.textCursor() c.setPosition(c.position() - min(c.positionInBlock(), 10), c.KeepAnchor) text = unicode(c.selectedText()).rstrip("\0") m = entity_pat.search(text) if m is None: return ent = m.group() repl = xml_entity_to_unicode(m) if repl != ent: c.setPosition(c.position() + m.start(), c.KeepAnchor) c.insertText(repl) 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): return self.smarts.cursor_position_with_sourceline(self.textCursor()) 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.smart.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)
class TextEdit(QPlainTextEdit): def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) self.highlighter = SyntaxHighlighter(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) self.line_number_area = LineNumbers(self) @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): # {{{ prefs = prefs or tprefs self.setLineWrapMode(QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = THEMES.get(prefs['editor_theme'], None) if theme is None: theme = THEMES[default_theme()] self.apply_theme(theme) 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')) 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(100 * w.averageCharWidth(), 50 * w.height()) # }}} def load_text(self, text, syntax='html'): self.highlighter = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)(self) self.highlighter.apply_theme(self.theme) self.highlighter.setDocument(self.document()) self.setPlainText(text) def replace_text(self, text): c = self.textCursor() pos = c.position() c.beginEditBlock() c.clearSelection() c.select(c.Document) c.insertText(text) c.endEditBlock() c.setPosition(min(pos, len(text))) self.setTextCursor(c) self.ensureCursorVisible() # 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.setExtraSelections([sel]) # 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.setViewportMargins(self.line_number_area_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 # }}} # Tooltips {{{ def event(self, ev): if ev.type() == ev.ToolTip: self.show_tooltip(ev) return True return QPlainTextEdit.event(self, ev) def syntax_format_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: return r.format 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)) QToolTip.hideText() ev.ignore()
class MyBlockingBusy(QDialog): # {{{ all_done = pyqtSignal() 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 accept(self): pass def reject(self): pass def on_all_done(self): if not self.error: # The cc widgets can only be accessed in the GUI thread try: for w in self.cc_widgets: w.commit(self.ids) except Exception as err: import traceback self.error = (err, traceback.format_exc()) self.pi.stopAnimation() QDialog.accept(self) def exec_(self): self.thread = Thread(target=self.do_it) self.thread.start() self.pi.startAnimation() return QDialog.exec_(self) def do_it(self): try: self.do_all() except Exception as err: import traceback try: err = unicode(err) except: err = repr(err) self.error = (err, traceback.format_exc()) self.all_done.emit() def do_all(self): cache = self.db.new_api args = self.args # Title and authors if args.do_swap_ta: title_map = cache.all_field_for('title', self.ids) authors_map = cache.all_field_for('authors', self.ids) def new_title(authors): ans = authors_to_string(authors) return titlecase(ans) if args.do_title_case else ans new_title_map = {bid:new_title(authors) for bid, authors in authors_map.iteritems()} new_authors_map = {bid:string_to_authors(title) for bid, title in title_map.iteritems()} cache.set_field('authors', new_authors_map) cache.set_field('title', new_title_map) if args.do_title_case and not args.do_swap_ta: title_map = cache.all_field_for('title', self.ids) cache.set_field('title', {bid:titlecase(title) for bid, title in title_map.iteritems()}) if args.do_title_sort: lang_map = cache.all_field_for('languages', self.ids) title_map = cache.all_field_for('title', self.ids) def get_sort(book_id): if args.languages: lang = args.languages[0] else: try: lang = lang_map[book_id][0] except (KeyError, IndexError, TypeError, AttributeError): lang = 'eng' return title_sort(title_map[book_id], lang=lang) cache.set_field('sort', {bid:get_sort(bid) for bid in self.ids}) if args.au: authors = string_to_authors(args.au) cache.set_field('authors', {bid:authors for bid in self.ids}) if args.do_auto_author: aus_map = cache.author_sort_strings_for_books(self.ids) cache.set_field('author_sort', {book_id:' & '.join(aus_map[book_id]) for book_id in aus_map}) if args.aus and args.do_aus: cache.set_field('author_sort', {bid:args.aus for bid in self.ids}) # Covers if args.cover_action == 'remove': cache.set_cover({bid:None for bid in self.ids}) elif args.cover_action == 'generate': from calibre.ebooks import calibre_cover from calibre.ebooks.metadata import fmt_sidx from calibre.gui2 import config for book_id in self.ids: mi = self.db.get_metadata(book_id, index_is_id=True) series_string = None if mi.series: series_string = _('Book %(sidx)s of %(series)s')%dict( sidx=fmt_sidx(mi.series_index, use_roman=config['use_roman_numerals_for_series_number']), series=mi.series) cdata = calibre_cover(mi.title, mi.format_field('authors')[-1], series_string=series_string) cache.set_cover({book_id:cdata}) elif args.cover_action == 'fromfmt': for book_id in self.ids: fmts = cache.formats(book_id, verify_formats=False) if fmts: covers = [] for fmt in fmts: fmtf = cache.format(book_id, fmt, as_file=True) if fmtf is None: continue cdata, area = get_cover_data(fmtf, fmt) if cdata: covers.append((cdata, area)) covers.sort(key=lambda x: x[1]) if covers: cache.set_cover({book_id:covers[-1][0]}) elif args.cover_action == 'trim': from calibre.utils.magick import Image for book_id in self.ids: cdata = cache.cover(book_id) if cdata: im = Image() im.load(cdata) im.trim(tweaks['cover_trim_fuzz_value']) cdata = im.export('jpg') cache.set_cover({book_id:cdata}) elif args.cover_action == 'clone': cdata = None for book_id in self.ids: cdata = cache.cover(book_id) if cdata: break if cdata: cache.set_cover({bid:cdata for bid in self.ids if bid != book_id}) # Formats if args.do_remove_format: cache.remove_formats({bid:(args.remove_format,) for bid in self.ids}) if args.restore_original: for book_id in self.ids: formats = cache.formats(book_id) originals = tuple(x.upper() for x in formats if x.upper().startswith('ORIGINAL_')) for ofmt in originals: cache.restore_original_format(book_id, ofmt) # Various fields if args.rating != -1: cache.set_field('rating', {bid:args.rating*2 for bid in self.ids}) if args.clear_pub: cache.set_field('publisher', {bid:'' for bid in self.ids}) if args.pub: cache.set_field('publisher', {bid:args.pub for bid in self.ids}) if args.clear_series: cache.set_field('series', {bid:'' for bid in self.ids}) if args.pubdate is not None: cache.set_field('pubdate', {bid:args.pubdate for bid in self.ids}) if args.adddate is not None: cache.set_field('timestamp', {bid:args.adddate for bid in self.ids}) if args.do_series: sval = args.series_start_value if args.do_series_restart else cache.get_next_series_num_for(args.series, current_indices=True) cache.set_field('series', {bid:args.series for bid in self.ids}) if not args.series: cache.set_field('series_index', {bid:1.0 for bid in self.ids}) else: def next_series_num(bid, i): if args.do_series_restart: return sval + i next_num = _get_next_series_num_for_list(sorted(sval.itervalues()), unwrap=False) sval[bid] = next_num return next_num smap = {bid:next_series_num(bid, i) for i, bid in enumerate(self.ids)} if args.do_autonumber: cache.set_field('series_index', smap) elif tweaks['series_index_auto_increment'] != 'no_change': cache.set_field('series_index', {bid:1.0 for bid in self.ids}) if args.comments is not null: cache.set_field('comments', {bid:args.comments for bid in self.ids}) if args.do_remove_conv: cache.delete_conversion_options(self.ids) if args.clear_languages: cache.set_field('languages', {bid:() for bid in self.ids}) elif args.languages: cache.set_field('languages', {bid:args.languages for bid in self.ids}) if args.remove_all: cache.set_field('tags', {bid:() for bid in self.ids}) if args.add or args.remove: self.db.bulk_modify_tags(self.ids, add=args.add, remove=args.remove) if self.do_sr: for book_id in self.ids: self.s_r_func(book_id) if self.sr_calls: for field, book_id_val_map in self.sr_calls.iteritems(): self.refresh_books.update(self.db.new_api.set_field(field, book_id_val_map))
def do_size_hint(self, option, index): text = index.data(Qt.DisplayRole).toString() font = QFont(option.font) font.setPointSize(QFontInfo(font).pointSize() * 1.5) m = QFontMetrics(font) return QSize(m.width(text), m.height())
class TextEdit(PlainTextEdit): def __init__(self, parent=None): PlainTextEdit.__init__(self, parent) self.saved_matches = {} 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) self.syntax = None @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.setLineWrapMode( QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = THEMES.get(prefs['editor_theme'], None) if theme is None: theme = THEMES[default_theme()] self.apply_theme(theme) w = self.fontMetrics() self.space_width = w.width(' ') self.setTabStopWidth(prefs['editor_tab_stop_width'] * 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(100 * w.averageCharWidth(), 50 * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line() # }}} def load_text(self, text, syntax='html', process_template=False): self.syntax = syntax self.highlighter = get_highlighter(syntax)() self.highlighter.apply_theme(self.theme) self.highlighter.set_document(self.document()) sclass = {'html': HTMLSmarts, 'xml': HTMLSmarts}.get(syntax, None) if sclass is not None: self.smarts = sclass(self) self.setPlainText(unicodedata.normalize('NFC', text)) if process_template and QPlainTextEdit.find(self, '%CURSOR%'): c = self.textCursor() c.insertText('') 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 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: sel.extend(self.smarts.get_extra_selections(self)) 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: raw, count = pat.subn(template, raw) if count > 0: c.setKeepPositionOnInsert(True) c.insertText(raw) c.setKeepPositionOnInsert(False) self.update_extra_selections() return count 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_spell_word(self, original_words, lang, from_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.setTextCursor(c) self.centerCursor() return True c.setPosition(c.position()) c.movePosition(c.End, c.KeepAnchor) 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 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.setViewportMargins(self.line_number_area_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 event(self, ev): if ev.type() == ev.ToolTip: self.show_tooltip(ev) return True if ev.type() == ev.ShortcutOverride: if ev in ( # Let the global cut/copy/paste shortcuts work,this avoids the nbsp # problem as well, since they use the overridden copy() method # instead of the one from Qt QKeySequence.Copy, QKeySequence.Cut, QKeySequence.Paste, ) or ( # This is used to convert typed hex codes into unicode # characters ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier): ev.ignore() return False return QPlainTextEdit.event(self, ev) # Tooltips {{{ def syntax_format_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).toBool(): return r.format 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)) QToolTip.hideText() ev.ignore() # }}} 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 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): 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) alt = _('Image') template = '<img alt="{0}" src="%s" />'.format(alt) href = prepare_string_for_xml(href, True) text = template % href c.insertText(text) if self.syntax == 'html': 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 QPlainTextEdit.keyPressEvent(self, ev) if (ev.key() == Qt.Key_Semicolon or ';' in unicode(ev.text( ))) and tprefs['replace_entities_as_typed'] and self.syntax == 'html': self.replace_possible_entity() 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 replace_possible_entity(self): c = self.textCursor() c.setPosition(c.position() - min(c.positionInBlock(), 10), c.KeepAnchor) text = unicode(c.selectedText()).rstrip('\0') m = entity_pat.search(text) if m is None: return ent = m.group() repl = xml_entity_to_unicode(m) if repl != ent: c.setPosition(c.position() + m.start(), c.KeepAnchor) c.insertText(repl) 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)
class MyBlockingBusy(QDialog): NORMAL = 0 REQUESTED = 1 ACKNOWLEDGED = 2 def __init__(self, gui, msg, size=100, window_title='Marvin XD', show_cancel=False, on_top=False): flags = Qt.FramelessWindowHint if on_top: flags = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint QDialog.__init__(self, gui, flags) self._layout = QVBoxLayout() self.setLayout(self._layout) self.cancel_status = 0 self.is_running = False # Add the spinner self.pi = ProgressIndicator(self) self.pi.setDisplaySize(size) self._layout.addSpacing(15) self._layout.addWidget(self.pi, 0, Qt.AlignHCenter) self._layout.addSpacing(15) # Fiddle with the message self.msg = QLabel(msg) #self.msg.setWordWrap(True) self.font = QFont() self.font.setPointSize(self.font.pointSize() + 2) self.msg.setFont(self.font) self._layout.addWidget(self.msg, 0, Qt.AlignHCenter) sp = QSizePolicy() sp.setHorizontalStretch(True) sp.setVerticalStretch(False) sp.setHeightForWidth(False) self.msg.setSizePolicy(sp) self.msg.setMinimumHeight(self.font.pointSize() + 8) self._layout.addSpacing(15) if show_cancel: self.bb = QDialogButtonBox() self.cancel_button = QPushButton(QIcon(I('window-close.png')), 'Cancel') self.bb.addButton(self.cancel_button, self.bb.RejectRole) self.bb.clicked.connect(self.button_handler) self._layout.addWidget(self.bb) self.setWindowTitle(window_title) self.resize(self.sizeHint()) def accept(self): self.stop() return QDialog.accept(self) def button_handler(self, button): ''' Only change cancel_status from NORMAL to REQUESTED ''' if self.bb.buttonRole(button) == QDialogButtonBox.RejectRole: if self.cancel_status == self.NORMAL: self.cancel_status = self.REQUESTED self.cancel_button.setEnabled(False) def reject(self): ''' Cannot cancel this dialog manually ''' pass def set_text(self, text): self.msg.setText(text) def start(self): self.is_running = True self.pi.startAnimation() def stop(self): self.is_running = False self.pi.stopAnimation()
def __init__(self, parent=None, desc=None, name=None, modal=0, fl=0, env=None, type="QDialog"): if env is None: import foundation.env as env # this is a little weird... probably it'll be ok, and logically it seems correct. self.desc = desc self.typ = type if type == "QDialog": QDialog.__init__(self, parent, name, modal, fl) elif type == "QTextEdit": QTextEdit.__init__(self, parent, name) elif type == "QFrame": QFrame.__init__(self, parent, name) else: print "don't know about type == %r" % (type, ) self.image1 = QPixmap() self.image1.loadFromData(image1_data, "PNG") # should be: title_icon #### self.image3 = QPixmap() self.image3.loadFromData(image3_data, "PNG") self.image4 = QPixmap() self.image4.loadFromData(image4_data, "PNG") self.image5 = QPixmap() self.image5.loadFromData(image5_data, "PNG") self.image6 = QPixmap() self.image6.loadFromData(image6_data, "PNG") self.image7 = QPixmap() self.image7.loadFromData(image7_data, "PNG") self.image0 = QPixmap(image0_data) # should be: border_icon #### self.image2 = QPixmap(image2_data) # should be: sponsor_pixmap #### try: ####@@@@ title_icon_name = self.desc.options.get('title_icon') border_icon_name = self.desc.options.get('border_icon') if title_icon_name: self.image1 = imagename_to_pixmap( title_icon_name) ###@@@ pass icon_path ###@@@ import imagename_to_pixmap or use env function # or let that func itself be an arg, or have an env arg for it ###e rename it icon_name_to_pixmap, or find_icon? (the latter only if it's ok if it returns an iconset) ###e use iconset instead? if border_icon_name: self.image0 = imagename_to_pixmap(border_icon_name) except: print_compact_traceback( "bug in icon-setting code, using fallback icons: ") pass if not name: self.setName("parameter_dialog_or_frame") ### ###k guess this will need: if type == 'QDialog' self.setIcon(self.image0) # should be: border_icon #### nanotube_dialogLayout = QVBoxLayout(self, 0, 0, "nanotube_dialogLayout") self.heading_frame = QFrame(self, "heading_frame") self.heading_frame.setPaletteBackgroundColor(QColor(122, 122, 122)) self.heading_frame.setFrameShape(QFrame.NoFrame) self.heading_frame.setFrameShadow(QFrame.Plain) heading_frameLayout = QHBoxLayout(self.heading_frame, 0, 3, "heading_frameLayout") self.heading_pixmap = QLabel(self.heading_frame, "heading_pixmap") self.heading_pixmap.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed, 0, 0, self.heading_pixmap.sizePolicy().hasHeightForWidth())) self.heading_pixmap.setPixmap( self.image1) # should be: title_icon #### self.heading_pixmap.setScaledContents(1) heading_frameLayout.addWidget(self.heading_pixmap) self.heading_label = QLabel(self.heading_frame, "heading_label") self.heading_label.setPaletteForegroundColor(QColor(255, 255, 255)) heading_label_font = QFont(self.heading_label.font()) heading_label_font.setPointSize(12) heading_label_font.setBold(1) self.heading_label.setFont(heading_label_font) heading_frameLayout.addWidget(self.heading_label) nanotube_dialogLayout.addWidget(self.heading_frame) self.body_frame = QFrame(self, "body_frame") self.body_frame.setFrameShape(QFrame.StyledPanel) self.body_frame.setFrameShadow(QFrame.Raised) body_frameLayout = QVBoxLayout(self.body_frame, 3, 3, "body_frameLayout") self.sponsor_frame = QFrame(self.body_frame, "sponsor_frame") self.sponsor_frame.setPaletteBackgroundColor(QColor(255, 255, 255)) self.sponsor_frame.setFrameShape(QFrame.StyledPanel) self.sponsor_frame.setFrameShadow(QFrame.Raised) sponsor_frameLayout = QHBoxLayout(self.sponsor_frame, 0, 0, "sponsor_frameLayout") self.sponsor_btn = QPushButton(self.sponsor_frame, "sponsor_btn") self.sponsor_btn.setAutoDefault(0) #bruce 060703 bugfix self.sponsor_btn.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred, 0, 0, self.sponsor_btn.sizePolicy().hasHeightForWidth())) self.sponsor_btn.setPaletteBackgroundColor(QColor(255, 255, 255)) self.sponsor_btn.setPixmap( self.image2 ) # should be: sponsor_pixmap #### [also we'll need to support >1 sponsor] self.sponsor_btn.setFlat(1) sponsor_frameLayout.addWidget(self.sponsor_btn) body_frameLayout.addWidget(self.sponsor_frame) layout59 = QHBoxLayout(None, 0, 6, "layout59") left_spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) layout59.addItem(left_spacer) self.done_btn = QToolButton(self.body_frame, "done_btn") self.done_btn.setIcon(QIcon(self.image3)) layout59.addWidget(self.done_btn) self.abort_btn = QToolButton(self.body_frame, "abort_btn") self.abort_btn.setIcon(QIcon(self.image4)) layout59.addWidget(self.abort_btn) self.preview_btn = QToolButton(self.body_frame, "preview_btn") self.preview_btn.setIcon(QIcon(self.image5)) layout59.addWidget(self.preview_btn) self.whatsthis_btn = QToolButton(self.body_frame, "whatsthis_btn") self.whatsthis_btn.setIcon(QIcon(self.image6)) layout59.addWidget(self.whatsthis_btn) right_spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) layout59.addItem(right_spacer) body_frameLayout.addLayout(layout59) self.groups = [] self.param_getters = { } # map from param name to get-function (which gets current value out of its widget or controller) for group_desc in self.desc.kids('group'): # == start parameters_grpbox ### this will differ for Windows style header_refs = [ ] # keep python refcounted refs to all objects we make (at least the ones pyuic stored in self attrs) self.parameters_grpbox = QGroupBox(self.body_frame, "parameters_grpbox") self.parameters_grpbox.setFrameShape(QGroupBox.StyledPanel) self.parameters_grpbox.setFrameShadow(QGroupBox.Sunken) self.parameters_grpbox.setMargin(0) self.parameters_grpbox.setColumnLayout(0, Qt.Vertical) self.parameters_grpbox.layout().setSpacing(1) self.parameters_grpbox.layout().setMargin(4) parameters_grpboxLayout = QVBoxLayout( self.parameters_grpbox.layout()) parameters_grpboxLayout.setAlignment(Qt.AlignTop) layout20 = QHBoxLayout(None, 0, 6, "layout20") self.nt_parameters_grpbtn = QPushButton(self.parameters_grpbox, "nt_parameters_grpbtn") self.nt_parameters_grpbtn.setSizePolicy( QSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed, 0, 0, self.nt_parameters_grpbtn.sizePolicy().hasHeightForWidth()) ) self.nt_parameters_grpbtn.setMaximumSize(QSize(16, 16)) self.nt_parameters_grpbtn.setAutoDefault(0) self.nt_parameters_grpbtn.setIcon(QIcon( self.image7)) ### not always right, but doesn't matter self.nt_parameters_grpbtn.setFlat(1) layout20.addWidget(self.nt_parameters_grpbtn) self.parameters_grpbox_label = QLabel(self.parameters_grpbox, "parameters_grpbox_label") self.parameters_grpbox_label.setSizePolicy( QSizePolicy( QSizePolicy.Preferred, QSizePolicy.Minimum, 0, 0, self.parameters_grpbox_label.sizePolicy(). hasHeightForWidth())) self.parameters_grpbox_label.setAlignment(QLabel.AlignVCenter) layout20.addWidget(self.parameters_grpbox_label) gbx_spacer1 = QSpacerItem(67, 16, QSizePolicy.Expanding, QSizePolicy.Minimum) layout20.addItem(gbx_spacer1) parameters_grpboxLayout.addLayout(layout20) nt_parameters_body_layout = QGridLayout( None, 1, 1, 0, 6, "nt_parameters_body_layout" ) ### what is 6 -- is it related to number of items??? # is it 6 in all the ones we got, but that could be a designer error so i better look it up sometime. # == start its kids # will use from above: self.parameters_grpbox, nt_parameters_body_layout nextrow = 0 # which row of the QGridLayout to start filling next (loop variable) hidethese = [ ] # set of objects to hide or show, when this group is closed or opened for param in group_desc.kids('parameter'): # param (a group subobj desc) is always a parameter, but we already plan to extend this beyond that, # so we redundantly test for this here. getter = None paramname = None # set these for use by uniform code at the end (e.g. for tooltips) editfield = None label = None if param.isa('parameter'): label = QLabel(self.parameters_grpbox, "members_label") label.setAlignment(QLabel.AlignVCenter | QLabel.AlignRight) nt_parameters_body_layout.addWidget(label, nextrow, 0) hidethese.append(label) thisrow = nextrow nextrow += 1 #e following should be known in a place that knows the input language, not here paramname = param.options.get('name') or ( param.args and param.args[0]) or "?" paramlabel = param.options.get( 'label' ) or paramname ##e wrong, label "" or none ought to be possible # QtGui.QApplication.translate(self.__class__.__name__, "xyz") label.setText( QtGui.QApplication.translate(self.__class__.__name__, paramlabel)) if param.isa('parameter', widget='combobox', type=('str', None)): self.members_combox = QComboBox( 0, self.parameters_grpbox, "members_combox") ###k what's 0? editfield = self.members_combox #### it probably needs a handler class, and then that could do this setup self.members_combox.clear() default = param.options.get( 'default', None) # None is not equal to any string thewidgetkid = param.kids( 'widget' )[-1] # kluge; need to think what the desc method for this should be for item in thewidgetkid.kids('item'): itemval = item.args[0] itemtext = itemval self.members_combox.insertItem( QtGui.QApplication.translate( self.__class__.__name__, itemtext)) #k __tr ok?? if itemval == default: #k or itemtext? pass ##k i find no setItem in our py code, so not sure yet what to do for this. nt_parameters_body_layout.addWidget( self.members_combox, thisrow, 1) hidethese.append(self.members_combox) getter = (lambda combobox=self.members_combox: str( combobox.currentText())) ##e due to __tr or non-str values, it might be better to use currentIndex and look it up in a table # (though whether __tr is good here might depend on what it's used for) elif param.isa('parameter', widget=('lineedit', None), type=('str', None)): # this covers explicit str|lineedit, and 3 default cases str, lineedit, neither. # (i.e. if you say parameter and nothing else, it's str lineedit by default.) self.length_linedit = QLineEdit(self.parameters_grpbox, "length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget( self.length_linedit, thisrow, 1) hidethese.append(self.length_linedit) default = str(param.options.get('default', "")) self.length_linedit.setText( QtGui.QApplication.translate(self.__class__.__name__, default)) # __tr ok? getter = (lambda lineedit=self.length_linedit: str( lineedit.text())) elif param.isa('parameter', widget=('lineedit', None), type='float'): self.length_linedit = QLineEdit(self.parameters_grpbox, "length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget( self.length_linedit, thisrow, 1) hidethese.append(self.length_linedit) controller = FloatLineeditController_Qt( self, param, self.length_linedit) header_refs.append(controller) getter = controller.get_value elif param.isa('parameter', widget = ('spinbox', None), type = 'int') or \ param.isa('parameter', widget = ('spinbox'), type = None): self.chirality_N_spinbox = QSpinBox( self.parameters_grpbox, "chirality_N_spinbox" ) # was chirality_m_spinbox, now chirality_N_spinbox editfield = self.chirality_N_spinbox ### seems like Qt defaults for min and max are 0,100 -- way too small a range! if param.options.has_key('min') or 1: self.chirality_N_spinbox.setMinimum( param.options.get('min', -999999999)) # was 0 if param.options.has_key('max') or 1: self.chirality_N_spinbox.setMaximum( param.options.get( 'max', +999999999)) # wasn't in egcode, but needed self.chirality_N_spinbox.setValue( param.options.get('default', 0)) # was 5 ##e note: i suspect this default 0 should come from something that knows this desc grammar. suffix = param.options.get('suffix', '') if suffix: self.chirality_N_spinbox.setSuffix( QtGui.QApplication.translate( self.__class__.__name__, suffix)) else: self.chirality_N_spinbox.setSuffix( QString.null) # probably not needed nt_parameters_body_layout.addWidget( self.chirality_N_spinbox, thisrow, 1) hidethese.append(self.chirality_N_spinbox) getter = self.chirality_N_spinbox.value # note: it also has .text, which includes suffix else: print "didn't match:", param ###e improve this # things done the same way for all kinds of param-editing widgets if 1: #bruce 060703 moved this down here, as bugfix # set tooltip (same one for editfield and label) tooltip = param.options.get('tooltip', '') ###e do it for more kinds of params; share the code somehow; do it in controller, or setup-aid? ###k QToolTip appropriateness; tooltip option might be entirely untested if tooltip and label: QToolTip.add( label, QtGui.QApplication.translate( self.__class__.__name__, tooltip)) if tooltip and editfield: QToolTip.add( editfield, QtGui.QApplication.translate( self.__class__.__name__, tooltip) ) ##k ok?? review once not all params have same-row labels. if getter and paramname and paramname != '?': self.param_getters[paramname] = getter ### also bind these params to actions... continue # next param header_refs.extend([ self.parameters_grpbox, self.nt_parameters_grpbtn, self.parameters_grpbox_label ]) # now create the logic/control object for the group group = CollapsibleGroupController_Qt(self, group_desc, header_refs, hidethese, self.nt_parameters_grpbtn) ### maybe ask env for the class to use for this? self.groups.append( group ) ### needed?? only for scanning the params, AFAIK -- oh, and to maintain a python refcount. # from languageChange: if 1: # i don't know if these are needed: self.parameters_grpbox.setTitle(QString.null) self.nt_parameters_grpbtn.setText(QString.null) self.parameters_grpbox_label.setText( QtGui.QApplication.translate( self.__class__.__name__, group_desc.args[0])) # was "Nanotube Parameters" ##e note that it's questionable in the syntax design for this property of a group (overall group label) # to be in that position (desc arg 0). # == end its kids parameters_grpboxLayout.addLayout(nt_parameters_body_layout) body_frameLayout.addWidget(self.parameters_grpbox) # == end parameters groupbox continue # next group nanotube_dialogLayout.addWidget(self.body_frame) spacer14 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) nanotube_dialogLayout.addItem(spacer14) layout42 = QHBoxLayout(None, 4, 6, "layout42") btm_spacer = QSpacerItem(59, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) layout42.addItem(btm_spacer) self.cancel_btn = QPushButton(self, "cancel_btn") self.cancel_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.cancel_btn) self.ok_btn = QPushButton(self, "ok_btn") self.ok_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.ok_btn) nanotube_dialogLayout.addLayout(layout42) self.languageChange() self.resize( QSize(246, 618).expandedTo(self.minimumSizeHint()) ) ### this size will need to be adjusted (guess -- it's only place overall size is set) qt4todo('self.clearWState(Qt.WState_Polished)') ## self.connect(self.nt_parameters_grpbtn,SIGNAL("clicked()"),self.toggle_nt_parameters_grpbtn) #### # new: for button, methodname in ( (self.sponsor_btn, 'do_sponsor_btn'), #e generalize to more than one sponsor button (self.done_btn, 'do_done_btn'), (self.abort_btn, 'do_abort_btn'), (self.preview_btn, 'do_preview_btn'), (self.whatsthis_btn, 'do_whatsthis_btn'), (self.cancel_btn, 'do_cancel_btn'), (self.ok_btn, 'do_ok_btn')): if hasattr(self, methodname): self.connect(button, SIGNAL("clicked()"), getattr(self, methodname)) return
class MyBlockingBusy(QDialog): NORMAL = 0 REQUESTED = 1 ACKNOWLEDGED = 2 def __init__(self, gui, msg, size=100, window_title="Marvin XD", show_cancel=False, on_top=False): flags = Qt.FramelessWindowHint if on_top: flags = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint QDialog.__init__(self, gui, flags) self._layout = QVBoxLayout() self.setLayout(self._layout) self.cancel_status = 0 self.is_running = False # Add the spinner self.pi = ProgressIndicator(self) self.pi.setDisplaySize(size) self._layout.addSpacing(15) self._layout.addWidget(self.pi, 0, Qt.AlignHCenter) self._layout.addSpacing(15) # Fiddle with the message self.msg = QLabel(msg) # self.msg.setWordWrap(True) self.font = QFont() self.font.setPointSize(self.font.pointSize() + 2) self.msg.setFont(self.font) self._layout.addWidget(self.msg, 0, Qt.AlignHCenter) sp = QSizePolicy() sp.setHorizontalStretch(True) sp.setVerticalStretch(False) sp.setHeightForWidth(False) self.msg.setSizePolicy(sp) self.msg.setMinimumHeight(self.font.pointSize() + 8) self._layout.addSpacing(15) if show_cancel: self.bb = QDialogButtonBox() self.cancel_button = QPushButton(QIcon(I("window-close.png")), "Cancel") self.bb.addButton(self.cancel_button, self.bb.RejectRole) self.bb.clicked.connect(self.button_handler) self._layout.addWidget(self.bb) self.setWindowTitle(window_title) self.resize(self.sizeHint()) def accept(self): self.stop() return QDialog.accept(self) def button_handler(self, button): """ Only change cancel_status from NORMAL to REQUESTED """ if self.bb.buttonRole(button) == QDialogButtonBox.RejectRole: if self.cancel_status == self.NORMAL: self.cancel_status = self.REQUESTED self.cancel_button.setEnabled(False) def reject(self): """ Cannot cancel this dialog manually """ pass def set_text(self, text): self.msg.setText(text) def start(self): self.is_running = True self.pi.startAnimation() def stop(self): self.is_running = False self.pi.stopAnimation()
class TextEdit(QPlainTextEdit): def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) self.current_cursor_line = None self.current_search_mark = None self.highlighter = SyntaxHighlighter(self) 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.selectionChanged.connect(self.selection_changed) self.updateRequest.connect(self.update_line_number_area) self.syntax = None @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) @property def selected_text(self): return unicodedata.normalize('NFC', unicode(self.textCursor().selectedText()).replace(PARAGRAPH_SEPARATOR, '\n')) def sizeHint(self): return self.size_hint def apply_settings(self, prefs=None): # {{{ prefs = prefs or tprefs self.setLineWrapMode(QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = THEMES.get(prefs['editor_theme'], None) if theme is None: theme = THEMES[default_theme()] self.apply_theme(theme) w = self.fontMetrics() self.space_width = w.width(' ') self.setTabStopWidth(prefs['editor_tab_stop_width'] * self.space_width) 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')) 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(100 * w.averageCharWidth(), 50 * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line() # }}} def toPlainText(self): # QPlainTextEdit's toPlainText implementation replaces nbsp with normal # space, so we re-implement it using QTextCursor, which does not do # that c = self.textCursor() c.clearSelection() c.movePosition(c.Start) c.movePosition(c.End, c.KeepAnchor) return c.selectedText().replace(PARAGRAPH_SEPARATOR, '\n') @pyqtSlot() def copy(self): # Workaround Qt replacing nbsp with normal spaces on copy c = self.textCursor() if not c.hasSelection(): return md = QMimeData() md.setText(self.selected_text) QApplication.clipboard().setMimeData(md) @pyqtSlot() def cut(self): # Workaround Qt replacing nbsp with normal spaces on copy self.copy() self.textCursor().removeSelectedText() def selection_changed(self): # Workaround Qt replacing nbsp with normal spaces on copy clipboard = QApplication.clipboard() if clipboard.supportsSelection() and self.textCursor().hasSelection(): md = QMimeData() md.setText(self.selected_text) clipboard.setMimeData(md, clipboard.Selection) def load_text(self, text, syntax='html', process_template=False): self.highlighter = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)(self) self.highlighter.apply_theme(self.theme) self.highlighter.setDocument(self.document()) self.setPlainText(unicodedata.normalize('NFC', text)) if process_template and QPlainTextEdit.find(self, '%CURSOR%'): c = self.textCursor() c.insertText('') 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 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()) 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): 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) 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): 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') 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) 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') if template is None: count = len(pat.findall(raw)) else: raw, count = pat.subn(template, raw) if count > 0: c.setKeepPositionOnInsert(True) c.insertText(raw) c.setKeepPositionOnInsert(False) self.update_extra_selections() return count def find(self, pat, wrap=False, marked=False, complete=False): if marked: return self.find_in_marked(pat, wrap=wrap) 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') 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) return True def replace(self, pat, template): c = self.textCursor() raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n') m = pat.fullmatch(raw) if m is None: return False 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() # 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.setViewportMargins(self.line_number_area_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 event(self, ev): if ev.type() == ev.ToolTip: self.show_tooltip(ev) return True if ev.type() == ev.ShortcutOverride and ev in ( QKeySequence.Copy, QKeySequence.Cut, QKeySequence.Paste): # Let the global cut/copy/paste shortcuts work,this avoids the nbsp # problem as well, since they use the overridden copy() method # instead of the one from Qt ev.ignore() return False return QPlainTextEdit.event(self, ev) # Tooltips {{{ def syntax_format_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).toBool(): return r.format 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)) QToolTip.hideText() ev.ignore()
class MyBlockingBusy(QDialog): # {{{ do_one_signal = pyqtSignal() phases = ['', _('Title/Author'), _('Standard metadata'), _('Custom metadata'), _('Search/Replace'), ] def __init__(self, msg, args, db, ids, cc_widgets, s_r_func, parent=None, window_title=_('Working')): QDialog.__init__(self, parent) self._layout = QVBoxLayout() self.setLayout(self._layout) self.msg_text = msg self.msg = QLabel(msg+' ') # Ensure dialog is wide enough #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.setWindowTitle(window_title) self.resize(self.sizeHint()) self.start() self.args = args self.series_start_value = None self.db = db self.ids = ids self.error = None self.cc_widgets = cc_widgets self.s_r_func = s_r_func self.do_one_signal.connect(self.do_one_safe, Qt.QueuedConnection) def start(self): self.pi.startAnimation() def stop(self): self.pi.stopAnimation() def accept(self): self.stop() return QDialog.accept(self) def exec_(self): self.current_index = 0 self.current_phase = 1 self.do_one_signal.emit() return QDialog.exec_(self) def do_one_safe(self): try: if self.current_index >= len(self.ids): self.current_phase += 1 self.current_index = 0 if self.current_phase > 4: self.db.commit() return self.accept() id = self.ids[self.current_index] percent = int((self.current_index*100)/float(len(self.ids))) self.msg.setText(self.msg_text.format(self.phases[self.current_phase], percent)) self.do_one(id) except Exception as err: import traceback try: err = unicode(err) except: err = repr(err) self.error = (err, traceback.format_exc()) return self.accept() def do_one(self, id): remove_all, remove, add, au, aus, do_aus, rating, pub, do_series, \ do_autonumber, do_remove_format, remove_format, do_swap_ta, \ do_remove_conv, do_auto_author, series, do_series_restart, \ series_start_value, do_title_case, cover_action, clear_series, \ pubdate, adddate, do_title_sort, languages, clear_languages, \ restore_original = self.args # first loop: All changes that modify the filesystem and commit # immediately. We want to # try hard to keep the DB and the file system in sync, even in the face # of exceptions or forced exits. if self.current_phase == 1: title_set = False if do_swap_ta: title = self.db.title(id, index_is_id=True) aum = self.db.authors(id, index_is_id=True) if aum: aum = [a.strip().replace('|', ',') for a in aum.split(',')] new_title = authors_to_string(aum) if do_title_case: new_title = titlecase(new_title) self.db.set_title(id, new_title, notify=False) title_set = True if title: new_authors = string_to_authors(title) self.db.set_authors(id, new_authors, notify=False) if do_title_case and not title_set: title = self.db.title(id, index_is_id=True) self.db.set_title(id, titlecase(title), notify=False) if do_title_sort: title = self.db.title(id, index_is_id=True) if languages: lang = languages[0] else: lang = self.db.languages(id, index_is_id=True) if lang: lang = lang.partition(',')[0] self.db.set_title_sort(id, title_sort(title, lang=lang), notify=False) if au: self.db.set_authors(id, string_to_authors(au), notify=False) if cover_action == 'remove': self.db.remove_cover(id) elif cover_action == 'generate': from calibre.ebooks import calibre_cover from calibre.ebooks.metadata import fmt_sidx from calibre.gui2 import config mi = self.db.get_metadata(id, index_is_id=True) series_string = None if mi.series: series_string = _('Book %(sidx)s of %(series)s')%dict( sidx=fmt_sidx(mi.series_index, use_roman=config['use_roman_numerals_for_series_number']), series=mi.series) cdata = calibre_cover(mi.title, mi.format_field('authors')[-1], series_string=series_string) self.db.set_cover(id, cdata) elif cover_action == 'fromfmt': fmts = self.db.formats(id, index_is_id=True, verify_formats=False) if fmts: covers = [] for fmt in fmts.split(','): fmtf = self.db.format(id, fmt, index_is_id=True, as_file=True) if fmtf is None: continue cdata, area = get_cover_data(fmtf, fmt) if cdata: covers.append((cdata, area)) covers.sort(key=lambda x: x[1]) if covers: self.db.set_cover(id, covers[-1][0]) covers = [] if do_remove_format: self.db.remove_format(id, remove_format, index_is_id=True, notify=False, commit=True) if restore_original: formats = self.db.formats(id, index_is_id=True) formats = formats.split(',') if formats else [] originals = [x.upper() for x in formats if x.upper().startswith('ORIGINAL_')] for ofmt in originals: fmt = ofmt.replace('ORIGINAL_', '') with SpooledTemporaryFile(SPOOL_SIZE) as stream: self.db.copy_format_to(id, ofmt, stream, index_is_id=True) stream.seek(0) self.db.add_format(id, fmt, stream, index_is_id=True, notify=False) self.db.remove_format(id, ofmt, index_is_id=True, notify=False, commit=True) elif self.current_phase == 2: # All of these just affect the DB, so we can tolerate a total rollback if do_auto_author: x = self.db.author_sort_from_book(id, index_is_id=True) if x: self.db.set_author_sort(id, x, notify=False, commit=False) if aus and do_aus: self.db.set_author_sort(id, aus, notify=False, commit=False) if rating != -1: self.db.set_rating(id, 2*rating, notify=False, commit=False) if pub: self.db.set_publisher(id, pub, notify=False, commit=False) if clear_series: self.db.set_series(id, '', notify=False, commit=False) if pubdate is not None: self.db.set_pubdate(id, pubdate, notify=False, commit=False) if adddate is not None: self.db.set_timestamp(id, adddate, notify=False, commit=False) if do_series: if do_series_restart: if self.series_start_value is None: self.series_start_value = series_start_value next = self.series_start_value self.series_start_value += 1 else: next = self.db.get_next_series_num_for(series) self.db.set_series(id, series, notify=False, commit=False) if not series: self.db.set_series_index(id, 1.0, notify=False, commit=False) elif do_autonumber: # is True if do_series_restart is True self.db.set_series_index(id, next, notify=False, commit=False) elif tweaks['series_index_auto_increment'] != 'no_change': self.db.set_series_index(id, 1.0, notify=False, commit=False) if do_remove_conv: self.db.delete_conversion_options(id, 'PIPE', commit=False) if clear_languages: self.db.set_languages(id, [], notify=False, commit=False) elif languages: self.db.set_languages(id, languages, notify=False, commit=False) elif self.current_phase == 3: # both of these are fast enough to just do them all for w in self.cc_widgets: w.commit(self.ids) if remove_all: self.db.remove_all_tags(self.ids) self.db.bulk_modify_tags(self.ids, add=add, remove=remove, notify=False) self.current_index = len(self.ids) elif self.current_phase == 4: self.s_r_func(id) # do the next one self.current_index += 1 self.do_one_signal.emit()
class TextEdit(QPlainTextEdit): def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) self.current_cursor_line = None self.current_search_mark = None self.highlighter = SyntaxHighlighter(self) 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) self.syntax = None @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) @property def selected_text(self): return unicodedata.normalize('NFC', unicode(self.textCursor().selectedText())) def sizeHint(self): return self.size_hint def apply_settings(self, prefs=None): # {{{ prefs = prefs or tprefs self.setLineWrapMode(QPlainTextEdit.WidgetWidth if prefs['editor_line_wrap'] else QPlainTextEdit.NoWrap) theme = THEMES.get(prefs['editor_theme'], None) if theme is None: theme = THEMES[default_theme()] self.apply_theme(theme) w = self.fontMetrics() self.space_width = w.width(' ') self.setTabStopWidth(prefs['editor_tab_stop_width'] * self.space_width) 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')) 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(100 * w.averageCharWidth(), 50 * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line() # }}} def load_text(self, text, syntax='html', process_template=False): self.highlighter = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter)(self) self.highlighter.apply_theme(self.theme) self.highlighter.setDocument(self.document()) self.setPlainText(unicodedata.normalize('NFC', text)) if process_template and QPlainTextEdit.find(self, '%CURSOR%'): c = self.textCursor() c.insertText('') 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 go_to_line(self, lnum): 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()) c.movePosition(c.StartOfLine) lt = text.lstrip() if text and lt and lt != text: c.movePosition(c.NextWord) self.setTextCursor(c) self.ensureCursorVisible() def update_extra_selections(self): 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) 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): 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') 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) 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') if template is None: count = len(pat.findall(raw)) else: raw, count = pat.subn(template, raw) if count > 0: c.setKeepPositionOnInsert(True) c.insertText(raw) c.setKeepPositionOnInsert(False) self.update_extra_selections() return count def find(self, pat, wrap=False, marked=False, complete=False): if marked: return self.find_in_marked(pat, wrap=wrap) 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') 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) return True def replace(self, pat, template): c = self.textCursor() raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n') m = pat.fullmatch(raw) if m is None: return False text = m.expand(template) c.insertText(text) return True # }}} # 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() # 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.setViewportMargins(self.line_number_area_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 # }}} # Tooltips {{{ def event(self, ev): if ev.type() == ev.ToolTip: self.show_tooltip(ev) return True return QPlainTextEdit.event(self, ev) def syntax_format_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).toBool(): return r.format 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)) QToolTip.hideText() ev.ignore()
class MainView(QMainWindow): # Signals startPressed = pyqtSignal(bool) resetPressed = pyqtSignal() broadcastPressed = pyqtSignal(bool) def __init__(self): super(MainView, self).__init__() self.initUI() # Private variables self._running = False # ADE7753 instance self.meter = ade.ADE7753() # Connect signals and slots self.startPressed.connect(self.meter.startStopSlot) self.resetPressed.connect(self.meter.resetSlot) self.broadcastPressed.connect(self.meter.broadcastSlot) self.meter.varData.connect(self.updateData) self.meter.varPower.connect(self.updatePower) self.meter.varEnergy.connect(self.updateEnergy) # Slots # Update Vrms, Irms and Frequency @pyqtSlot(float, float, float, float) def updateData(self, vrms, irms, frequency, period): vrms = '{:.2f}'.format(vrms) irms = '{:.3f}'.format(irms) frequency = '{:.2f}'.format(frequency) self.voltageLabel.setText(vrms) self.currentLabel.setText(irms) self.frequencyLabel.setText(frequency) # Update energy @pyqtSlot(float, float, float) def updateEnergy(self, activeEnergy, apparentEnergy, reactiveEnergy): activeEnergy = '{:.3f}'.format(activeEnergy) apparentEnergy = '{:.3f}'.format(apparentEnergy) reactiveEnergy = '{:.3f}'.format(reactiveEnergy) self.activeEnergyLabel.setText(activeEnergy) self.apparentEnergyLabel.setText(apparentEnergy) self.reactiveEnergyLabel.setText(reactiveEnergy) # Update power @pyqtSlot(float, float, float) def updatePower(self, activePower, apparentPower, reactivePower): activePower = '{:.3f}'.format(activePower) apparentPower = '{:.3f}'.format(apparentPower) reactivePower = '{:.3f}'.format(reactivePower) self.activePowerLabel.setText(activePower) self.apparentPowerLabel.setText(apparentPower) self.reactivePowerLabel.setText(reactivePower) def initUI(self): # Set central widget centralWidget = QWidget() self.setCentralWidget(centralWidget) # Load LCD fonts lcdNumbersFontID = QFontDatabase().addApplicationFont("lcdmn.ttf") #fontNames = QFontDatabase().applicationFontFamilies(lcdNumbersFontID) # Change font size and use italic for strings #self.lcdNumbersFont = QFont(fontNames[0]) self.lcdNumbersFont = QFont() self.lcdNumbersFont.setPointSize(40) #self.lcdStringFont = QFont(fontNames[0]) self.lcdStringFont = QFont() self.lcdStringFont.setPointSize(35) self.lcdStringFont.setItalic(True) # self.lcdBoldFont = QFont(fontNames[0]) self.lcdBoldFont = QFont() self.lcdBoldFont.setPointSize(50) self.lcdBoldFont.setBold(True) # Labels dataLabel = QLabel("DATA") dataLabel.setStyleSheet(""" QLabel {color:red} """) energyLabel = QLabel("ENERGY") energyLabel.setStyleSheet(""" QLabel {color:red} """) powerLabel = QLabel("POWER") powerLabel.setStyleSheet(""" QLabel {color:red} """) dataLabel.setFont(self.lcdBoldFont) powerLabel.setFont(self.lcdBoldFont) energyLabel.setFont(self.lcdBoldFont) self.voltageLabel = QLabel("0.00") self.voltageLabel.setFont(self.lcdNumbersFont) self.voltageString = QLabel("V") self.voltageString.setFont(self.lcdStringFont) self.currentLabel = QLabel("0.00") self.currentLabel.setFont(self.lcdNumbersFont) self.currentString = QLabel("A") self.currentString.setFont(self.lcdStringFont) self.frequencyLabel = QLabel("0.00") self.frequencyLabel.setFont(self.lcdNumbersFont) self.frequencyString = QLabel("Hz") self.frequencyString.setFont(self.lcdStringFont) self.activeEnergyLabel = QLabel("0.00") self.activeEnergyLabel.setFont(self.lcdNumbersFont) self.activeEnergyString = QLabel("Wh") self.activeEnergyString.setFont(self.lcdStringFont) self.apparentEnergyLabel = QLabel("0.00") self.apparentEnergyLabel.setFont(self.lcdNumbersFont) self.apparentEnergyString = QLabel("VAh") self.apparentEnergyString.setFont(self.lcdStringFont) self.reactiveEnergyLabel = QLabel("0.00") self.reactiveEnergyLabel.setFont(self.lcdNumbersFont) self.reactiveEnergyString = QLabel("VARh") self.reactiveEnergyString.setFont(self.lcdStringFont) self.activePowerLabel = QLabel("0.00") self.activePowerLabel.setFont(self.lcdNumbersFont) self.activePowerString = QLabel("W") self.activePowerString.setFont(self.lcdStringFont) self.apparentPowerLabel = QLabel("0.00") self.apparentPowerLabel.setFont(self.lcdNumbersFont) self.apparentPowerString = QLabel("VA") self.apparentPowerString.setFont(self.lcdStringFont) self.reactivePowerLabel = QLabel("0.00") self.reactivePowerLabel.setFont(self.lcdNumbersFont) self.reactivePowerString = QLabel("VAR") self.reactivePowerString.setFont(self.lcdStringFont) # Horizontal lines hline1 = self.HLine() hline2 = self.HLine() hline3 = self.HLine() hline4 = self.HLine() # Vertical lines vline1 = self.VLine() vline2 = self.VLine() vline3 = self.VLine() # Central grid layout for central widget self.centralGridLayout = QGridLayout(self.centralWidget()) self.centralGridLayout.setHorizontalSpacing(20) self.centralGridLayout.setVerticalSpacing(5) # Add labels self.centralGridLayout.addWidget(dataLabel,0,0,1,2, QtCore.Qt.AlignCenter) self.centralGridLayout.addWidget(energyLabel,0,3,1,2, QtCore.Qt.AlignCenter) self.centralGridLayout.addWidget(powerLabel,0,6,1,2, QtCore.Qt.AlignCenter) self.centralGridLayout.addWidget(hline1, 1, 0, 1, -1) self.centralGridLayout.addWidget(self.voltageLabel, 2, 0, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.voltageString, 2, 1, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(hline2, 3, 0, 1, -1) self.centralGridLayout.addWidget(self.currentLabel, 4, 0, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.currentString, 4, 1, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(hline3, 5, 0, 1, -1) self.centralGridLayout.addWidget(self.frequencyLabel, 6, 0, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.frequencyString, 6, 1, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(hline4, 7, 0, 1, -1) self.centralGridLayout.addWidget(vline1, 0, 2, -1, 1) self.centralGridLayout.addWidget(self.activeEnergyLabel, 2, 3, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.activeEnergyString, 2, 4, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.apparentEnergyLabel, 4, 3, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.apparentEnergyString, 4, 4, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.reactiveEnergyLabel, 6, 3, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.reactiveEnergyString, 6, 4, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(vline2, 0, 5, -1, 1) self.centralGridLayout.addWidget(self.activePowerLabel, 2, 6, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.activePowerString, 2, 7, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.apparentPowerLabel, 4, 6, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.apparentPowerString, 4, 7, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.reactivePowerLabel, 6, 6, 1, 1, QtCore.Qt.AlignLeft) self.centralGridLayout.addWidget(self.reactivePowerString, 6, 7, 1, 1, QtCore.Qt.AlignLeft) # self.centralGridLayout.addWidget(vline3, 0, 8, -1, 1) # Buttons self.startStopButton = QPushButton("START") self.startStopButton.setFont(self.lcdStringFont) buttonPolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.startStopButton.setSizePolicy(buttonPolicy) self.startStopButton.clicked.connect(self.startStopClicked) self.resetButton = QPushButton("RESET") self.resetButton.setFont(self.lcdStringFont) self.resetButton.setSizePolicy(buttonPolicy) self.resetButton.clicked.connect(self.resetButtonClicked) self.broadcastButton = QPushButton("BROADCAST") self.broadcastButton.setFont(self.lcdStringFont) self.broadcastButton.setSizePolicy(buttonPolicy) self.broadcastButton.setCheckable(True) self.broadcastButton.toggled.connect(self.broadcastButtonClicked) self.centralGridLayout.addWidget(self.startStopButton, 8, 0, 1, 2) self.centralGridLayout.addWidget(self.resetButton, 8, 3, 1, 2) self.centralGridLayout.addWidget(self.broadcastButton, 8, 6, 1, 2) # Status bar and show window self.statusBar().showMessage('Ready') self.setWindowTitle("ADE7753 Power Meter") self.show() # Button slots def startStopClicked(self): if(self._running): self._running = False self.startStopButton.setText("START") self.startPressed.emit(False) else: self._running = True self.startStopButton.setText("STOP") self.startPressed.emit(True) def resetButtonClicked(self): self.resetPressed.emit() def broadcastButtonClicked(self, s): self.broadcastPressed.emit(s) # Helper functions def HLine(self): line = QFrame() line.setFrameStyle(QFrame.HLine) line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) return line def VLine(self): line = QFrame() line.setFrameStyle(QFrame.VLine) line.setFrameShape(QFrame.VLine) line.setFrameShadow(QFrame.Sunken) return line