def read_settings_API_1(stream, settings): """ Write all settings such as fonts for labels and legend. NOTE: This function doesn't return anything and changes the settings directly.""" labelFont = QFont() legendFont = QFont() stream >> labelFont labelInterval = stream.readInt32() labelState = get_row_label_identifier(labelInterval) stream >> legendFont gridCellWidth = stream.readInt32() gridCellHeight = stream.readInt32() # make sure we parsed something sensible before # touching the settings if labelFont.family(): settings.labelFont.value = labelFont if labelState: settings.rowLabelMode.value = labelState if legendFont.family(): settings.legendFont.value = legendFont if gridCellWidth != 0: settings.gridCellWidth.value = gridCellWidth if gridCellHeight != 0: settings.gridCellHeight.value = gridCellHeight
def read_settings_API_1(stream, settings): """ Write all settings such as fonts for labels and legend. NOTE: This function doesn't return anything and changes the settings directly.""" labelFont = QFont() legendFont = QFont() stream >> labelFont labelInterval = stream.readInt32() labelState = get_row_label_identifier(labelInterval) stream >> legendFont gridCellWidth = stream.readInt32() gridCellHeight = stream.readInt32() # make sure we parsed something sensible before # touching the settings if labelFont.family(): settings.labelFont.value = labelFont if labelState: settings.rowLabelMode.value = labelState if legendFont.family(): settings.legendFont.value = legendFont if gridCellWidth != 0: settings.gridCellWidth.value = gridCellWidth if gridCellHeight != 0: settings.gridCellHeight.value = gridCellHeight
def applySettings(self): """Apply editor and lexer settings """ if self.qscilexer is None: return # Apply fonts and colors config = core.config()["Editor"] defaultFont = QFont(config["DefaultFont"], config["DefaultFontSize"]) self.qscilexer.setDefaultFont(defaultFont) for i in range(128): if self.qscilexer.description(i): font = self.qscilexer.font(i) font.setFamily(defaultFont.family()) font.setPointSize(defaultFont.pointSize()) self.qscilexer.setFont(font, i) # lexer->setColor(lexer->defaultColor(i), i); # TODO configure lexer colors # lexer->setEolFill(lexer->defaultEolFill(i), i); # lexer->setPaper(lexer->defaultPaper(i), i); if config["DefaultDocumentColours"]: self.qscilexer.setPaper(QColor(config["DefaultDocumentPaper"]), i) self.qscilexer.setEolFill(True, i) else: self.qscilexer.setPaper(self.qscilexer.defaultPaper(i), i) self.qscilexer.setEolFill(self.qscilexer.defaultEolFill(i), i) if config["DefaultDocumentColours"]: self.qscilexer.setColor(QColor(config["DefaultDocumentPen"]), 0) else: self.qscilexer.setColor(self.qscilexer.defaultColor(0), 0)
def getMonoFont(fontsize=12, msg=False): monofont = QFont() monofont.setStyleStrategy(QFont.PreferAntialias+QFont.PreferMatch) monofont.setStyleHint(QFont.Courier) monofont.setFamily(IMC.fontFamily) monofont.setFixedPitch(True) # probably unnecessary monofont.setPointSize(fontsize) if msg and (monofont.family() != IMC.fontFamily): infoMsg("Font {0} not available, using {1}".format( IMC.fontFamily, monoinf.family()) ) return monofont
def test_1(self): font = QFont("Arial", 88) def continueFunc(dialog): page = dialog._pageForItem["Editor/Font"] page.lFont.setFont(font) QTest.keyClick(dialog, Qt.Key_Enter) self.openSettings(continueFunc) self.assertEqual(core.config()['Qutepart']['Font']['Family'], font.family()) self.assertEqual(core.config()['Qutepart']['Font']['Size'], font.pointSize()) self.assertEqual( core.workspace().currentDocument().qutepart.font().family(), font.family()) self.assertEqual( core.workspace().currentDocument().qutepart.font().pointSize(), font.pointSize())
def loadHistoryData(self): tabledata = self.history.toList() self.tablemodel = BeeralyzerTableModel(tabledata) self.historyTableView.setModel(self.tablemodel) self.historyTableView.setShowGrid(True) font = QFont(self.historTableFontFamily, self.historTableFontSize) self.displayHistoryTableFont(font.family(), font.pointSize()) self.historyTableView.setFont(font) vh = self.historyTableView.verticalHeader() vh.setVisible(True) hh = self.historyTableView.horizontalHeader() hh.setStretchLastSection(True) self.historyTableView.setSortingEnabled(True) self.historyTableView.resizeColumnsToContents()
def getFontFromSetting(setting_data, default_font=None): import hex.utils as utils font = default_font or QFont() if isinstance(setting_data, str): stored_font = QFont() ok = stored_font.fromString(setting_data) return stored_font if ok else font elif isinstance(setting_data, (tuple, list)): for font_data in setting_data: if isinstance(font_data, str): stored_font = QFont() ok = stored_font.fromString(font_data) if ok and utils.isFontInstalled(stored_font.family()): return stored_font return font
def test_1(self): font = QFont("Arial", 88) def continueFunc(dialog): page = dialog._pageForItem["Editor/Font"] page.lFont.setFont(font) QTest.keyClick(dialog, Qt.Key_Enter) self.openSettings(continueFunc) self.assertEqual(core.config()['Qutepart']['Font']['Family'], font.family()) self.assertEqual(core.config()['Qutepart']['Font']['Size'], font.pointSize()) self.assertEqual(core.workspace().currentDocument().qutepart.font(), font)
class CharMap(QWidget): """A widget displaying a table of characters.""" characterSelected = pyqtSignal(str) characterClicked = pyqtSignal(str) def __init__(self, parent=None): super(CharMap, self).__init__(parent) self._showToolTips = True self._showWhatsThis = True self._selected = -1 self._column_count = 32 self._square = 24 self._range = (0, 0) self._font = QFont() def setRange(self, first, last): self._range = (first, last) self._selected = -1 self.adjustSize() self.update() def range(self): return self._range def square(self): """Returns the width of one item (determined by font size).""" return self._square def select(self, charcode): """Selects the specified character (int or str).""" if not isinstance(charcode, int): charcode = ord(charcode) if not self._range[0] <= charcode <= self._range[1]: charcode = -1 if self._selected != charcode: self._selected = charcode self.characterSelected.emit(chr(charcode)) self.update() def character(self): """Returns the currently selected character, if any.""" if self._selected != -1: return chr(self._selected) def setDisplayFont(self, font): self._font.setFamily(font.family()) self.update() def displayFont(self): return QFont(self._font) def setDisplayFontSize(self, size): self._font.setPointSize(size) self._square = max(24, QFontMetrics(self._font).xHeight() * 3) self.adjustSize() self.update() def displayFontSize(self): return self._font.pointSize() def setDisplayFontSizeF(self, size): self._font.setPointSizeF(size) self._square = max(24, QFontMetrics(self._font).xHeight() * 3) self.adjustSize() self.update() def displayFontSizeF(self): return self._font.pointSizeF() def setColumnCount(self, count): """Sets how many columns should be used.""" count = max(1, count) self._column_count = count self.adjustSize() self.update() def columnCount(self): return self._column_count def sizeHint(self): return self.sizeForColumnCount(self._column_count) def paintEvent(self, ev): rect = ev.rect() s = self._square rows = range(rect.top() // s, rect.bottom() // s + 1) cols = range(rect.left() // s, rect.right() // s + 1) painter = QPainter(self) painter.setPen(QPen(self.palette().color(QPalette.Window))) painter.setFont(self._font) metrics = QFontMetrics(self._font) # draw characters on white tiles tile = self.palette().color(QPalette.Base) selected_tile = self.palette().color(QPalette.Highlight) selected_tile.setAlpha(96) selected_box = self.palette().color(QPalette.Highlight) text_pen = QPen(self.palette().text()) disabled_pen = QPen(self.palette().color(QPalette.Disabled, QPalette.Text)) selection_pen = QPen(selected_box) for row in rows: for col in cols: char = row * self._column_count + col + self._range[0] if char > self._range[1]: break printable = self.isprint(char) painter.setClipRect(col * s, row * s, s, s) if char == self._selected: painter.fillRect(col * s + 1, row * s + 1, s - 2, s - 2, selected_tile) painter.setPen(selection_pen) painter.drawRect(col * s, row * s, s - 1, s - 1) elif printable: painter.fillRect(col * s + 1, row * s + 1, s - 2, s - 2, tile) painter.setPen(text_pen if printable else disabled_pen) t = chr(char) x = col * s + s // 2 - metrics.width(t) // 2 y = row * s + 4 + metrics.ascent() painter.drawText(x, y, t) else: continue break def sizeForColumnCount(self, count): """Returns the size the widget would have in a certain column count. This can be used in e.g. a resizable scroll area. """ first, last = self._range rows = ((last - first) // count) + 1 return QSize(count, rows) * self._square def columnCountForWidth(self, width): """Returns the number of columns that would fit into the given width.""" return width // self._square def mousePressEvent(self, ev): charcode = self.charcodeAt(ev.pos()) if charcode != -1 and self.isprint(charcode): self.select(charcode) if ev.button() != Qt.RightButton: self.characterClicked.emit(chr(charcode)) def charcodeRect(self, charcode): """Returns the rectangular box around the given charcode, if any.""" if self._range[0] <= charcode <= self._range[1]: row, col = divmod(charcode - self._range[0], self._column_count) s = self._square return QRect(col * s, row * s, s, s) def charcodeAt(self, position): row = position.y() // self._square col = position.x() // self._square if col <= self._column_count: charcode = self._range[0] + row * self._column_count + col if charcode <= self._range[1]: return charcode return -1 def event(self, ev): if ev.type() == QEvent.ToolTip: if self._showToolTips: c = self.charcodeAt(ev.pos()) if c: text = self.getToolTipText(c) if text: rect = self.charcodeRect(c) QToolTip.showText(ev.globalPos(), text, self, rect) ev.accept() return True elif ev.type() == QEvent.QueryWhatsThis: if self._showWhatsThis: ev.accept() return True elif ev.type() == QEvent.WhatsThis: ev.accept() if self._showWhatsThis: c = self.charcodeAt(ev.pos()) text = self.getWhatsThisText(c) if c else None if text: QWhatsThis.showText(ev.globalPos(), text, self) else: QWhatsThis.leaveWhatsThisMode() return True return super(CharMap, self).event(ev) def getToolTipText(self, charcode): try: return unicodedata.name(chr(charcode)) except ValueError: pass def getWhatsThisText(self, charcode): try: name = unicodedata.name(chr(charcode)) except ValueError: return return whatsthis_html.format(self._font.family(), chr(charcode), name, charcode) def setShowToolTips(self, enabled): self._showToolTips = bool(enabled) def showToolTips(self): return self._showToolTips def setShowWhatsThis(self, enabled): self._showWhatsThis = bool(enabled) def showWhatsThis(self): return self._showWhatsThis def isprint(self, charcode): """Returns True if the given charcode is printable.""" return isprint(charcode)
class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin): """Text edit base widget""" BRACE_MATCHING_SCOPE = ('sof', 'eof') CELL_SEPARATORS = ('#%%', '# %%', '# <codecell>') def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) BaseEditMixin.__init__(self) self.setAttribute(Qt.WA_DeleteOnClose) self.extra_selections_dict = {} self.connect(self, SIGNAL('textChanged()'), self.changed) self.connect(self, SIGNAL('cursorPositionChanged()'), self.cursor_position_changed) self.indent_chars = " " * 4 # Code completion / calltips if parent is not None: mainwin = parent while not isinstance(mainwin, QMainWindow): mainwin = mainwin.parent() if mainwin is None: break if mainwin is not None: parent = mainwin self.completion_widget = CompletionWidget(self, parent) self.codecompletion_auto = False self.codecompletion_case = True self.codecompletion_single = False self.codecompletion_enter = False self.calltips = True self.calltip_position = None self.calltip_size = 600 self.calltip_font = QFont() self.completion_text = "" # Highlight current line color self.currentline_color = QColor(Qt.red).lighter(190) # Brace matching self.bracepos = None self.matched_p_color = QColor(Qt.green) self.unmatched_p_color = QColor(Qt.red) def setup_calltips(self, size=None, font=None): self.calltip_size = size self.calltip_font = font def setup_completion(self, size=None, font=None): self.completion_widget.setup_appearance(size, font) def set_indent_chars(self, indent_chars): self.indent_chars = indent_chars def set_palette(self, background, foreground): """ Set text editor palette colors: background color and caret (text cursor) color """ palette = QPalette() palette.setColor(QPalette.Base, background) palette.setColor(QPalette.Text, foreground) self.setPalette(palette) #------Line number area def get_linenumberarea_width(self): """Return line number area width""" # Implemented in CodeEditor, but needed here for completion widget return 0 #------Extra selections def get_extra_selections(self, key): return self.extra_selections_dict.get(key, []) def set_extra_selections(self, key, extra_selections): self.extra_selections_dict[key] = extra_selections def update_extra_selections(self): extra_selections = [] for key, extra in list(self.extra_selections_dict.items()): if key == 'current_line': # Python 3 compatibility (weird): current line has to be # highlighted first extra_selections = extra + extra_selections else: extra_selections += extra self.setExtraSelections(extra_selections) def clear_extra_selections(self, key): self.extra_selections_dict[key] = [] self.update_extra_selections() def changed(self): """Emit changed signal""" self.emit(SIGNAL('modificationChanged(bool)'), self.document().isModified()) #------Highlight current line def highlight_current_line(self): """Highlight current line""" selection = QTextEdit.ExtraSelection() # selection.format.setProperty(QTextFormat.FullWidthSelection, # QVariant(True)) selection.format.setBackground(self.currentline_color) selection.cursor = self.textCursor() selection.cursor.clearSelection() self.set_extra_selections('current_line', [selection]) self.update_extra_selections() def unhighlight_current_line(self): """Unhighlight current line""" self.clear_extra_selections('current_line') #------Brace matching def find_brace_match(self, position, brace, forward): start_pos, end_pos = self.BRACE_MATCHING_SCOPE if forward: bracemap = {'(': ')', '[': ']', '{': '}'} text = self.get_text(position, end_pos) i_start_open = 1 i_start_close = 1 else: bracemap = {')': '(', ']': '[', '}': '{'} text = self.get_text(start_pos, position) i_start_open = len(text) - 1 i_start_close = len(text) - 1 while True: if forward: i_close = text.find(bracemap[brace], i_start_close) else: i_close = text.rfind(bracemap[brace], 0, i_start_close + 1) if i_close > -1: if forward: i_start_close = i_close + 1 i_open = text.find(brace, i_start_open, i_close) else: i_start_close = i_close - 1 i_open = text.rfind(brace, i_close, i_start_open + 1) if i_open > -1: if forward: i_start_open = i_open + 1 else: i_start_open = i_open - 1 else: # found matching brace if forward: return position + i_close else: return position - (len(text) - i_close) else: # no matching brace return def __highlight(self, positions, color=None, cancel=False): if cancel: self.clear_extra_selections('brace_matching') return extra_selections = [] for position in positions: if position > self.get_position('eof'): return selection = QTextEdit.ExtraSelection() selection.format.setBackground(color) selection.cursor = self.textCursor() selection.cursor.clearSelection() selection.cursor.setPosition(position) selection.cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) extra_selections.append(selection) self.set_extra_selections('brace_matching', extra_selections) self.update_extra_selections() def cursor_position_changed(self): """Brace matching""" if self.bracepos is not None: self.__highlight(self.bracepos, cancel=True) self.bracepos = None cursor = self.textCursor() if cursor.position() == 0: return cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) text = to_text_string(cursor.selectedText()) pos1 = cursor.position() if text in (')', ']', '}'): pos2 = self.find_brace_match(pos1, text, forward=False) elif text in ('(', '[', '{'): pos2 = self.find_brace_match(pos1, text, forward=True) else: return if pos2 is not None: self.bracepos = (pos1, pos2) self.__highlight(self.bracepos, color=self.matched_p_color) else: self.bracepos = (pos1, ) self.__highlight(self.bracepos, color=self.unmatched_p_color) #-----Widget setup and options def set_codecompletion_auto(self, state): """Set code completion state""" self.codecompletion_auto = state def set_codecompletion_case(self, state): """Case sensitive completion""" self.codecompletion_case = state self.completion_widget.case_sensitive = state def set_codecompletion_single(self, state): """Show single completion""" self.codecompletion_single = state self.completion_widget.show_single = state def set_codecompletion_enter(self, state): """Enable Enter key to select completion""" self.codecompletion_enter = state self.completion_widget.enter_select = state def set_calltips(self, state): """Set calltips state""" self.calltips = state def set_wrap_mode(self, mode=None): """ Set wrap mode Valid *mode* values: None, 'word', 'character' """ if mode == 'word': wrap_mode = QTextOption.WrapAtWordBoundaryOrAnywhere elif mode == 'character': wrap_mode = QTextOption.WrapAnywhere else: wrap_mode = QTextOption.NoWrap self.setWordWrapMode(wrap_mode) #------Reimplementing Qt methods def copy(self): """ Reimplement Qt method Copy text to clipboard with correct EOL chars """ QApplication.clipboard().setText(self.get_selected_text()) #------Text: get, set, ... def get_selection_as_executable_code(self): """Return selected text as a processed text, to be executable in a Python/IPython interpreter""" ls = self.get_line_separator() _indent = lambda line: len(line) - len(line.lstrip()) line_from, line_to = self.get_selection_bounds() text = self.get_selected_text() if not text: return lines = text.split(ls) if len(lines) > 1: # Multiline selection -> eventually fixing indentation original_indent = _indent(self.get_text_line(line_from)) text = (" " * (original_indent - _indent(lines[0]))) + text # If there is a common indent to all lines, find it. # Moving from bottom line to top line ensures that blank # lines inherit the indent of the line *below* it, # which is the desired behavior. min_indent = 999 current_indent = 0 lines = text.split(ls) for i in range(len(lines) - 1, -1, -1): line = lines[i] if line.strip(): current_indent = _indent(line) min_indent = min(current_indent, min_indent) else: lines[i] = ' ' * current_indent if min_indent: lines = [line[min_indent:] for line in lines] # Remove any leading whitespace or comment lines # since they confuse the reserved word detector that follows below first_line = lines[0].lstrip() while first_line == '' or first_line[0] == '#': lines.pop(0) first_line = lines[0].lstrip() # Add an EOL character after indentation blocks that start with some # Python reserved words, so that it gets evaluated automatically # by the console varname = re.compile('[a-zA-Z0-9_]*') # matches valid variable names maybe = False nextexcept = () for n, line in enumerate(lines): if not _indent(line): word = varname.match(line).group() if maybe and word not in nextexcept: lines[n - 1] += ls maybe = False if word: if word in ('def', 'for', 'while', 'with', 'class'): maybe = True nextexcept = () elif word == 'if': maybe = True nextexcept = ('elif', 'else') elif word == 'try': maybe = True nextexcept = ('except', 'finally') if maybe: if lines[-1].strip() == '': lines[-1] += ls else: lines.append(ls) return ls.join(lines) def get_cell_as_executable_code(self): """Return cell contents as executable code""" start_pos, end_pos = self.__save_selection() self.select_current_cell() text = self.get_selection_as_executable_code() self.__restore_selection(start_pos, end_pos) return text def is_cell_separator(self, cursor=None, block=None): """Return True if cursor (or text block) is on a block separator""" assert cursor is not None or block is not None if cursor is not None: cursor0 = QTextCursor(cursor) cursor0.select(QTextCursor.BlockUnderCursor) text = to_text_string(cursor0.selectedText()) else: text = to_text_string(block.text()) return text.lstrip().startswith(self.CELL_SEPARATORS) def select_current_cell(self): """Select cell under cursor cell = group of lines separated by CELL_SEPARATORS""" cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfBlock) cur_pos = prev_pos = cursor.position() # Moving to the next line that is not a separator, if we are # exactly at one of them while self.is_cell_separator(cursor): cursor.movePosition(QTextCursor.NextBlock) prev_pos = cur_pos cur_pos = cursor.position() if cur_pos == prev_pos: return # If not, move backwards to find the previous separator while not self.is_cell_separator(cursor): cursor.movePosition(QTextCursor.PreviousBlock) prev_pos = cur_pos cur_pos = cursor.position() if cur_pos == prev_pos: if self.is_cell_separator(cursor): return else: break cursor.setPosition(prev_pos) cell_at_file_start = cursor.atStart() # Once we find it (or reach the beginning of the file) # move to the next separator (or the end of the file) # so we can grab the cell contents while not self.is_cell_separator(cursor): cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) cur_pos = cursor.position() if cur_pos == prev_pos: cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) break prev_pos = cur_pos # Do nothing if we moved from beginning to end without # finding a separator if cell_at_file_start and cursor.atEnd(): return self.setTextCursor(cursor) def go_to_next_cell(self): """Go to the next cell of lines""" cursor = self.textCursor() cursor.movePosition(QTextCursor.NextBlock) cur_pos = prev_pos = cursor.position() while not self.is_cell_separator(cursor): # Moving to the next code cell cursor.movePosition(QTextCursor.NextBlock) prev_pos = cur_pos cur_pos = cursor.position() if cur_pos == prev_pos: return self.setTextCursor(cursor) def get_line_count(self): """Return document total line number""" return self.blockCount() def __save_selection(self): """Save current cursor selection and return position bounds""" cursor = self.textCursor() return cursor.selectionStart(), cursor.selectionEnd() def __restore_selection(self, start_pos, end_pos): """Restore cursor selection from position bounds""" cursor = self.textCursor() cursor.setPosition(start_pos) cursor.setPosition(end_pos, QTextCursor.KeepAnchor) self.setTextCursor(cursor) def __duplicate_line_or_selection(self, after_current_line=True): """Duplicate current line or selected text""" cursor = self.textCursor() cursor.beginEditBlock() start_pos, end_pos = self.__save_selection() if to_text_string(cursor.selectedText()): cursor.setPosition(end_pos) # Check if end_pos is at the start of a block: if so, starting # changes from the previous block cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor) if not to_text_string(cursor.selectedText()): cursor.movePosition(QTextCursor.PreviousBlock) end_pos = cursor.position() cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) while cursor.position() <= end_pos: cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) if cursor.atEnd(): cursor_temp = QTextCursor(cursor) cursor_temp.clearSelection() cursor_temp.insertText(self.get_line_separator()) break cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) text = cursor.selectedText() cursor.clearSelection() if not after_current_line: # Moving cursor before current line/selected text cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) start_pos += len(text) end_pos += len(text) cursor.insertText(text) cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos) def duplicate_line(self): """ Duplicate current line or selected text Paste the duplicated text *after* the current line/selected text """ self.__duplicate_line_or_selection(after_current_line=True) def copy_line(self): """ Copy current line or selected text Paste the duplicated text *before* the current line/selected text """ self.__duplicate_line_or_selection(after_current_line=False) def __move_line_or_selection(self, after_current_line=True): """Move current line or selected text""" cursor = self.textCursor() cursor.beginEditBlock() start_pos, end_pos = self.__save_selection() if to_text_string(cursor.selectedText()): # Check if start_pos is at the start of a block cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) start_pos = cursor.position() cursor.setPosition(end_pos) # Check if end_pos is at the start of a block: if so, starting # changes from the previous block cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor) if to_text_string(cursor.selectedText()): cursor.movePosition(QTextCursor.NextBlock) end_pos = cursor.position() else: cursor.movePosition(QTextCursor.StartOfBlock) start_pos = cursor.position() cursor.movePosition(QTextCursor.NextBlock) end_pos = cursor.position() cursor.setPosition(start_pos) cursor.setPosition(end_pos, QTextCursor.KeepAnchor) sel_text = to_text_string(cursor.selectedText()) cursor.removeSelectedText() if after_current_line: text = to_text_string(cursor.block().text()) start_pos += len(text) + 1 end_pos += len(text) + 1 cursor.movePosition(QTextCursor.NextBlock) else: cursor.movePosition(QTextCursor.PreviousBlock) text = to_text_string(cursor.block().text()) start_pos -= len(text) + 1 end_pos -= len(text) + 1 cursor.insertText(sel_text) cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos) def move_line_up(self): """Move up current line or selected text""" self.__move_line_or_selection(after_current_line=False) def move_line_down(self): """Move down current line or selected text""" self.__move_line_or_selection(after_current_line=True) def extend_selection_to_complete_lines(self): """Extend current selection to complete lines""" cursor = self.textCursor() start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() cursor.setPosition(start_pos) cursor.setPosition(end_pos, QTextCursor.KeepAnchor) if cursor.atBlockStart(): cursor.movePosition(QTextCursor.PreviousBlock, QTextCursor.KeepAnchor) cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) self.setTextCursor(cursor) def delete_line(self): """Delete current line""" cursor = self.textCursor() if self.has_selected_text(): self.extend_selection_to_complete_lines() start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() cursor.setPosition(start_pos) else: start_pos = end_pos = cursor.position() cursor.beginEditBlock() cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) while cursor.position() <= end_pos: cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) if cursor.atEnd(): break cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.endEditBlock() self.ensureCursorVisible() #------Code completion / Calltips def show_calltip(self, title, text, color='#2D62FF', at_line=None, at_position=None): """Show calltip""" if text is None or len(text) == 0: return # Saving cursor position: if at_position is None: at_position = self.get_position('cursor') self.calltip_position = at_position # Preparing text: weight = 'bold' if self.calltip_font.bold() else 'normal' size = self.calltip_font.pointSize() family = self.calltip_font.family() format1 = '<div style=\'font-size: %spt; color: %s\'>' % (size, color) format2 = '<hr><div style=\'font-family: "%s"; font-size: %spt; font-weight: %s\'>' % ( family, size, weight) if isinstance(text, list): text = "\n ".join(text) text = text.replace('\n', '<br>') if len(text) > self.calltip_size: text = text[:self.calltip_size] + " ..." tiptext = format1 + ('<b>%s</b></div>' % title) \ + format2 + text + "</div>" # Showing tooltip at cursor position: cx, cy = self.get_coordinates('cursor') if at_line is not None: cx = 5 cursor = QTextCursor(self.document().findBlockByNumber(at_line - 1)) cy = self.cursorRect(cursor).top() point = self.mapToGlobal(QPoint(cx, cy)) point.setX(point.x() + self.get_linenumberarea_width()) QToolTip.showText(point, tiptext) def hide_tooltip_if_necessary(self, key): """Hide calltip when necessary""" try: calltip_char = self.get_character(self.calltip_position) before = self.is_cursor_before(self.calltip_position, char_offset=1) other = key in (Qt.Key_ParenRight, Qt.Key_Period, Qt.Key_Tab) if calltip_char not in ('?', '(') or before or other: QToolTip.hideText() except (IndexError, TypeError): QToolTip.hideText() def show_completion_widget(self, textlist, automatic=True): """Show completion widget""" self.completion_widget.show_list(textlist, automatic=automatic) def hide_completion_widget(self): """Hide completion widget""" self.completion_widget.hide() def show_completion_list(self, completions, completion_text="", automatic=True): """Display the possible completions""" if len(completions) == 0 or completions == [completion_text]: return self.completion_text = completion_text # Sorting completion list (entries starting with underscore are # put at the end of the list): underscore = set( [comp for comp in completions if comp.startswith('_')]) completions = sorted(set(completions)-underscore, key=str_lower)+\ sorted(underscore, key=str_lower) self.show_completion_widget(completions, automatic=automatic) def select_completion_list(self): """Completion list is active, Enter was just pressed""" self.completion_widget.item_selected() def insert_completion(self, text): if text: cursor = self.textCursor() cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor, len(self.completion_text)) cursor.removeSelectedText() self.insert_text(text) def is_completion_widget_visible(self): """Return True is completion list widget is visible""" return self.completion_widget.isVisible() #------Standard keys def stdkey_clear(self): if not self.has_selected_text(): self.moveCursor(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) self.remove_selected_text() def stdkey_backspace(self): if not self.has_selected_text(): self.moveCursor(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) self.remove_selected_text() def __get_move_mode(self, shift): return QTextCursor.KeepAnchor if shift else QTextCursor.MoveAnchor def stdkey_up(self, shift): self.moveCursor(QTextCursor.Up, self.__get_move_mode(shift)) def stdkey_down(self, shift): self.moveCursor(QTextCursor.Down, self.__get_move_mode(shift)) def stdkey_tab(self): self.insert_text(self.indent_chars) def stdkey_home(self, shift, ctrl, prompt_pos=None): """Smart HOME feature: cursor is first moved at indentation position, then at the start of the line""" move_mode = self.__get_move_mode(shift) if ctrl: self.moveCursor(QTextCursor.Start, move_mode) else: cursor = self.textCursor() if prompt_pos is None: start_position = self.get_position('sol') else: start_position = self.get_position(prompt_pos) text = self.get_text(start_position, 'eol') indent_pos = start_position + len(text) - len(text.lstrip()) if cursor.position() != indent_pos: cursor.setPosition(indent_pos, move_mode) else: cursor.setPosition(start_position, move_mode) self.setTextCursor(cursor) def stdkey_end(self, shift, ctrl): move_mode = self.__get_move_mode(shift) if ctrl: self.moveCursor(QTextCursor.End, move_mode) else: self.moveCursor(QTextCursor.EndOfBlock, move_mode) def stdkey_pageup(self): pass def stdkey_pagedown(self): pass def stdkey_escape(self): pass #----Qt Events def mousePressEvent(self, event): """Reimplement Qt method""" if os.name != 'posix' and event.button() == Qt.MidButton: self.setFocus() event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QPlainTextEdit.mousePressEvent(self, event) QPlainTextEdit.mouseReleaseEvent(self, event) self.paste() else: QPlainTextEdit.mousePressEvent(self, event) def focusInEvent(self, event): """Reimplemented to handle focus""" self.emit(SIGNAL("focus_changed()")) self.emit(SIGNAL("focus_in()")) QPlainTextEdit.focusInEvent(self, event) def focusOutEvent(self, event): """Reimplemented to handle focus""" self.emit(SIGNAL("focus_changed()")) QPlainTextEdit.focusOutEvent(self, event) def wheelEvent(self, event): """Reimplemented to emit zoom in/out signals when Ctrl is pressed""" # This feature is disabled on MacOS, see Issue 1510: # http://code.google.com/p/spyderlib/issues/detail?id=1510 if sys.platform != 'darwin': if event.modifiers() & Qt.ControlModifier: if event.delta() < 0: self.emit(SIGNAL("zoom_out()")) elif event.delta() > 0: self.emit(SIGNAL("zoom_in()")) return QPlainTextEdit.wheelEvent(self, event)
class ConfigDialog(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self._layout = QVBoxLayout(self) self._tabdialog = QTabWidget(self) self._layout.addWidget(self._tabdialog) w = _config_dialog.Ui_config_dialog() w.setupUi(self._tabdialog) self.widgets = w self._buttonbox = QDialogButtonBox(QDialogButtonBox.Save|QDialogButtonBox.Cancel) self._buttonbox.setParent(self) signal_connect(self._buttonbox, SIGNAL("accepted()"), self.accept) signal_connect(self._buttonbox, SIGNAL("rejected()"), self.reject) self._layout.addWidget(self._buttonbox) self._layout.setContentsMargins(3,3,3,3) self.font = QFont() self.color = QColor() self.config = yobot_interfaces.component_registry.get_component("client-config") self.account_items = {} signal_connect(w.account_add, SIGNAL("clicked()"), lambda: self.add_modify_account(add=True)) signal_connect(w.account_edit, SIGNAL("clicked()"), lambda: self.add_modify_account(add=False)) signal_connect(w.account_del, SIGNAL("clicked()"), self.remove_account) signal_connect(w.select_color, SIGNAL("clicked()"), lambda: self.change_formatting(color=True)) signal_connect(w.select_font, SIGNAL("clicked()"), lambda: self.change_formatting(font=True)) signal_connect(w.agent_address, SIGNAL("editingFinished()"), self.change_agent) self.connect_global_bool(w.html_relsize, "appearance", "use_html_relsize") self.connect_global_bool(w.show_joinpart, "appearance", "show_joinpart") self.input_validated = True self.setWindowTitle("Yobot Configuration") def connect_global_bool(self, widget, dictname, optname, default=False): signal_connect(widget, SIGNAL("toggled(bool)"), lambda b: self.config.globals.setdefault(dictname, {}).__setitem__(optname, b)) def load_settings(self): w = self.widgets if not self.config: log_warn("config object not available! bailing") return #for font.. appearance = self.config.globals.setdefault("appearance", {}) family = appearance.get("font_family", None) size = appearance.get("font_size", None) color = appearance.get("font_color", None) if family: self.font.setFamily(family) if size: self.font.setPointSize(size) if color: self.color.setNamedColor(color) bold = appearance.get("font_bold", False) italic = appearance.get("font_italic", False) underline = appearance.get("font_underline", False) html_relsize = appearance.get("use_html_relsize", False) show_joinpart = appearance.get("show_joinpart", False) self.font.setBold(bold) self.font.setItalic(italic) self.font.setUnderline(underline) w.html_relsize.setChecked(html_relsize) w.show_joinpart.setChecked(show_joinpart) self.change_formatting() #for the agent... agent = self.config.globals.get("agent_address", None) if agent: w.agent_address.setText(agent) self.change_agent() #for accounts: for a in self.config.accounts: log_warn("got account", a) if a.get("name", None) and a.get("improto", None): #get name and icon name, icon = getProtoIconAndName(getattr(yobotproto, a["improto"], "")) log_debug(icon, name) i = QTreeWidgetItem((a["name"], name)) i.setIcon(1, icon) i.setData(0, ITEM_PLACEHOLDER_ROLE, a) self.account_items[i] = a self.widgets.accounts.addTopLevelItem(i) def remove_account(self): #get current item: w = self.widgets item = w.accounts.currentItem() #get the index (ugh.. this is tedious) itemindex = w.accounts.indexOfTopLevelItem(item) if itemindex == -1: log_err("couldn't get index!") return account = self.account_items[item] #remove the item from the widget: w.accounts.takeTopLevelItem(itemindex) #find the account in our global config list index = -1 for i in xrange(0, len(self.config.accounts)): a = self.config.accounts[i] if str(a["name"]) == str(account["name"]) and str(a["improto"]) == str(account["improto"]): index = i break else: pass if index >= 0: log_debug("index:", index) self.config.accounts.pop(index) #finally, remove it from the mapping self.account_items.pop(item) def add_modify_account(self, add=False): dlg = AccountSettingsDialog(self) if not add: item = self.widgets.accounts.currentItem() if not item: return account = self.account_items.get(item) if not account: return #account = item.data(0, ITEM_PLACEHOLDER_ROLE).toPyObject() dlg.fill_from(account) else: item = QTreeWidgetItem() result = dlg.exec_() if not result == QDialog.Accepted: return new_a = dlg.values item.setText(0, new_a["name"]) #get icon and name... name, icon = getProtoIconAndName(getattr(yobotproto, new_a["improto"], -1)) item.setText(1, name) item.setIcon(1, icon) if add: if self.account_exists(new_a): print "account already exists.. not adding" return item.setData(0, ITEM_PLACEHOLDER_ROLE, new_a) self.widgets.accounts.addTopLevelItem(item) self.config.accounts.append(new_a) else: account.update(new_a) def account_exists(self, d): for a in self.config.accounts: if d["name"] == a["name"] and d["improto"] == a["improto"]: return True return False def change_formatting(self, color=False, font=False): if color: _color = QColorDialog.getColor(self.color, self, "Select Color") if _color.isValid(): self.color = _color elif font: self.font, _ = QFontDialog.getFont(self.font, self, "Select Font") widgetformatter(self.widgets.sample_text, self.font, self.color, klass="QPlainTextEdit") #now, change the config objects.. fmt = self.config.globals["appearance"] fmt.update({ "font_family":str(self.font.family()), "font_size":int(self.font.pointSize()), "font_color":str(self.color.name()), "font_bold":bool(self.font.bold()), "font_italic":bool(self.font.italic()), "font_underline":bool(self.font.underline()) }) def change_agent(self): #bah.. the same boring thing as always s = str(self.widgets.agent_address.text()) if len(s.rsplit(":", 1)) == 2 and not str.isdigit(s.rsplit(":",1)[1]): self.input_validated = False self.widgets.agent_address.setStyleSheet("background-color:red;") else: self.widgets.agent_address.setStyleSheet("background-color:green") if s: self.config.globals["agent_address"] = str(s) self.input_validated = True def accept(self): #do some stuff first, like save if self.input_validated: self.config.save() QDialog.accept(self) else: QErrorMessage(self).showMessage("Bad input.. (somewhere?)")
def read_settings_API_3(stream, settings): """ Write all settings such as fonts for labels and legend. NOTE: This function doesn't return anything and changes the settings directly.""" labelFont = QFont() legendFont = QFont() stream >> labelFont labelInterval = stream.readInt32() labelState = get_row_label_identifier(labelInterval) stream >> legendFont gridCellWidth = stream.readInt32() gridCellHeight = stream.readInt32() # make sure we parsed something sensible before # touching the settings if labelFont.family(): settings.labelFont.value = labelFont if labelState: settings.rowLabelMode.value = labelState if legendFont.family(): settings.legendFont.value = legendFont if gridCellWidth != 0: settings.gridCellWidth.value = gridCellWidth if gridCellHeight != 0: settings.gridCellHeight.value = gridCellHeight # new stuff in API_VERSION 2 and onward labelStart = stream.readInt32() settings.rowLabelStart.value = labelStart evenRowLabelLocation = stream.readInt32() settings.evenRowLabelLocation.value = \ get_row_label_location_string(evenRowLabelLocation) highlightRows = stream.readInt32() settings.highlightRows.value = highlightRows highlightRowsOpacity = stream.readInt32() settings.highlightRowsOpacity.value = highlightRowsOpacity highlightRowsStart = stream.readInt32() settings.highlightRowsStart.value = highlightRowsStart highlightRowsColor = stream.readQString() if highlightRowsColor: settings.highlightRowsColor.value = highlightRowsColor # write rest of row/column settings # NOTE: The row settings aren't combined with the rest to # remain backward compatible. oddRowLabelLocation = stream.readInt32() settings.oddRowLabelLocation.value = \ get_row_label_location_string(oddRowLabelLocation) rowLabelsShowInterval = stream.readInt32() settings.rowLabelsShowInterval.value = rowLabelsShowInterval rowLabelsShowIntervalStart = stream.readInt32() settings.rowLabelsShowIntervalStart.value = \ rowLabelsShowIntervalStart columnLabelMode = stream.readInt32() settings.columnLabelMode.value = \ get_column_label_identifier(columnLabelMode) columnLabelsShowInterval = stream.readInt32() settings.columnLabelsShowInterval.value = \ columnLabelsShowInterval columnLabelsShowIntervalStart = stream.readInt32() settings.columnLabelsShowIntervalStart.value = \ columnLabelsShowIntervalStart rowLabelsEditable = stream.readInt32() settings.rowLabelsEditable.value = rowLabelsEditable columnLabelsEditable = stream.readInt32() settings.columnLabelsEditable.value = columnLabelsEditable
class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin): """Text edit base widget""" BRACE_MATCHING_SCOPE = ('sof', 'eof') CELL_SEPARATORS = ('#%%', '# %%', '# <codecell>') def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) BaseEditMixin.__init__(self) self.setAttribute(Qt.WA_DeleteOnClose) self.extra_selections_dict = {} self.connect(self, SIGNAL('textChanged()'), self.changed) self.connect(self, SIGNAL('cursorPositionChanged()'), self.cursor_position_changed) self.indent_chars = " "*4 # Code completion / calltips if parent is not None: mainwin = parent while not isinstance(mainwin, QMainWindow): mainwin = mainwin.parent() if mainwin is None: break if mainwin is not None: parent = mainwin self.completion_widget = CompletionWidget(self, parent) self.codecompletion_auto = False self.codecompletion_case = True self.codecompletion_single = False self.codecompletion_enter = False self.calltips = True self.calltip_position = None self.calltip_size = 600 self.calltip_font = QFont() self.completion_text = "" # Highlight current line color self.currentline_color = QColor(Qt.red).lighter(190) # Brace matching self.bracepos = None self.matched_p_color = QColor(Qt.green) self.unmatched_p_color = QColor(Qt.red) def setup_calltips(self, size=None, font=None): self.calltip_size = size self.calltip_font = font def setup_completion(self, size=None, font=None): self.completion_widget.setup_appearance(size, font) def set_indent_chars(self, indent_chars): self.indent_chars = indent_chars def set_palette(self, background, foreground): """ Set text editor palette colors: background color and caret (text cursor) color """ palette = QPalette() palette.setColor(QPalette.Base, background) palette.setColor(QPalette.Text, foreground) self.setPalette(palette) #------Line number area def get_linenumberarea_width(self): """Return line number area width""" # Implemented in CodeEditor, but needed here for completion widget return 0 #------Extra selections def get_extra_selections(self, key): return self.extra_selections_dict.get(key, []) def set_extra_selections(self, key, extra_selections): self.extra_selections_dict[key] = extra_selections def update_extra_selections(self): extra_selections = [] for key, extra in list(self.extra_selections_dict.items()): if key == 'current_line': # Python 3 compatibility (weird): current line has to be # highlighted first extra_selections = extra + extra_selections else: extra_selections += extra self.setExtraSelections(extra_selections) def clear_extra_selections(self, key): self.extra_selections_dict[key] = [] self.update_extra_selections() def changed(self): """Emit changed signal""" self.emit(SIGNAL('modificationChanged(bool)'), self.document().isModified()) #------Highlight current line def highlight_current_line(self): """Highlight current line""" selection = QTextEdit.ExtraSelection() # selection.format.setProperty(QTextFormat.FullWidthSelection, # QVariant(True)) selection.format.setBackground(self.currentline_color) selection.cursor = self.textCursor() selection.cursor.clearSelection() self.set_extra_selections('current_line', [selection]) self.update_extra_selections() def unhighlight_current_line(self): """Unhighlight current line""" self.clear_extra_selections('current_line') #------Brace matching def find_brace_match(self, position, brace, forward): start_pos, end_pos = self.BRACE_MATCHING_SCOPE if forward: bracemap = {'(': ')', '[': ']', '{': '}'} text = self.get_text(position, end_pos) i_start_open = 1 i_start_close = 1 else: bracemap = {')': '(', ']': '[', '}': '{'} text = self.get_text(start_pos, position) i_start_open = len(text)-1 i_start_close = len(text)-1 while True: if forward: i_close = text.find(bracemap[brace], i_start_close) else: i_close = text.rfind(bracemap[brace], 0, i_start_close+1) if i_close > -1: if forward: i_start_close = i_close+1 i_open = text.find(brace, i_start_open, i_close) else: i_start_close = i_close-1 i_open = text.rfind(brace, i_close, i_start_open+1) if i_open > -1: if forward: i_start_open = i_open+1 else: i_start_open = i_open-1 else: # found matching brace if forward: return position+i_close else: return position-(len(text)-i_close) else: # no matching brace return def __highlight(self, positions, color=None, cancel=False): if cancel: self.clear_extra_selections('brace_matching') return extra_selections = [] for position in positions: if position > self.get_position('eof'): return selection = QTextEdit.ExtraSelection() selection.format.setBackground(color) selection.cursor = self.textCursor() selection.cursor.clearSelection() selection.cursor.setPosition(position) selection.cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) extra_selections.append(selection) self.set_extra_selections('brace_matching', extra_selections) self.update_extra_selections() def cursor_position_changed(self): """Brace matching""" if self.bracepos is not None: self.__highlight(self.bracepos, cancel=True) self.bracepos = None cursor = self.textCursor() if cursor.position() == 0: return cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) text = to_text_string(cursor.selectedText()) pos1 = cursor.position() if text in (')', ']', '}'): pos2 = self.find_brace_match(pos1, text, forward=False) elif text in ('(', '[', '{'): pos2 = self.find_brace_match(pos1, text, forward=True) else: return if pos2 is not None: self.bracepos = (pos1, pos2) self.__highlight(self.bracepos, color=self.matched_p_color) else: self.bracepos = (pos1,) self.__highlight(self.bracepos, color=self.unmatched_p_color) #-----Widget setup and options def set_codecompletion_auto(self, state): """Set code completion state""" self.codecompletion_auto = state def set_codecompletion_case(self, state): """Case sensitive completion""" self.codecompletion_case = state self.completion_widget.case_sensitive = state def set_codecompletion_single(self, state): """Show single completion""" self.codecompletion_single = state self.completion_widget.show_single = state def set_codecompletion_enter(self, state): """Enable Enter key to select completion""" self.codecompletion_enter = state self.completion_widget.enter_select = state def set_calltips(self, state): """Set calltips state""" self.calltips = state def set_wrap_mode(self, mode=None): """ Set wrap mode Valid *mode* values: None, 'word', 'character' """ if mode == 'word': wrap_mode = QTextOption.WrapAtWordBoundaryOrAnywhere elif mode == 'character': wrap_mode = QTextOption.WrapAnywhere else: wrap_mode = QTextOption.NoWrap self.setWordWrapMode(wrap_mode) #------Reimplementing Qt methods def copy(self): """ Reimplement Qt method Copy text to clipboard with correct EOL chars """ QApplication.clipboard().setText(self.get_selected_text()) #------Text: get, set, ... def get_selection_as_executable_code(self): """Return selected text as a processed text, to be executable in a Python/IPython interpreter""" ls = self.get_line_separator() _indent = lambda line: len(line)-len(line.lstrip()) line_from, line_to = self.get_selection_bounds() text = self.get_selected_text() if not text: return lines = text.split(ls) if len(lines) > 1: # Multiline selection -> eventually fixing indentation original_indent = _indent(self.get_text_line(line_from)) text = (" "*(original_indent-_indent(lines[0])))+text # If there is a common indent to all lines, find it. # Moving from bottom line to top line ensures that blank # lines inherit the indent of the line *below* it, # which is the desired behavior. min_indent = 999 current_indent = 0 lines = text.split(ls) for i in range(len(lines)-1, -1, -1): line = lines[i] if line.strip(): current_indent = _indent(line) min_indent = min(current_indent, min_indent) else: lines[i] = ' ' * current_indent if min_indent: lines = [line[min_indent:] for line in lines] # Remove any leading whitespace or comment lines # since they confuse the reserved word detector that follows below first_line = lines[0].lstrip() while first_line == '' or first_line[0] == '#': lines.pop(0) first_line = lines[0].lstrip() # Add an EOL character after indentation blocks that start with some # Python reserved words, so that it gets evaluated automatically # by the console varname = re.compile('[a-zA-Z0-9_]*') # matches valid variable names maybe = False nextexcept = () for n, line in enumerate(lines): if not _indent(line): word = varname.match(line).group() if maybe and word not in nextexcept: lines[n-1] += ls maybe = False if word: if word in ('def', 'for', 'while', 'with', 'class'): maybe = True nextexcept = () elif word == 'if': maybe = True nextexcept = ('elif', 'else') elif word == 'try': maybe = True nextexcept = ('except', 'finally') if maybe: if lines[-1].strip() == '': lines[-1] += ls else: lines.append(ls) return ls.join(lines) def get_cell_as_executable_code(self): """Return cell contents as executable code""" start_pos, end_pos = self.__save_selection() self.select_current_cell() text = self.get_selection_as_executable_code() self.__restore_selection(start_pos, end_pos) return text def is_cell_separator(self, cursor=None, block=None): """Return True if cursor (or text block) is on a block separator""" assert cursor is not None or block is not None if cursor is not None: cursor0 = QTextCursor(cursor) cursor0.select(QTextCursor.BlockUnderCursor) text = to_text_string(cursor0.selectedText()) else: text = to_text_string(block.text()) return text.lstrip().startswith(self.CELL_SEPARATORS) def select_current_cell(self): """Select cell under cursor cell = group of lines separated by CELL_SEPARATORS""" cursor = self.textCursor() cursor.movePosition(QTextCursor.StartOfBlock) cur_pos = prev_pos = cursor.position() # Moving to the next line that is not a separator, if we are # exactly at one of them while self.is_cell_separator(cursor): cursor.movePosition(QTextCursor.NextBlock) prev_pos = cur_pos cur_pos = cursor.position() if cur_pos == prev_pos: return # If not, move backwards to find the previous separator while not self.is_cell_separator(cursor): cursor.movePosition(QTextCursor.PreviousBlock) prev_pos = cur_pos cur_pos = cursor.position() if cur_pos == prev_pos: if self.is_cell_separator(cursor): return else: break cursor.setPosition(prev_pos) cell_at_file_start = cursor.atStart() # Once we find it (or reach the beginning of the file) # move to the next separator (or the end of the file) # so we can grab the cell contents while not self.is_cell_separator(cursor): cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) cur_pos = cursor.position() if cur_pos == prev_pos: cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) break prev_pos = cur_pos # Do nothing if we moved from beginning to end without # finding a separator if cell_at_file_start and cursor.atEnd(): return self.setTextCursor(cursor) def go_to_next_cell(self): """Go to the next cell of lines""" cursor = self.textCursor() cursor.movePosition(QTextCursor.NextBlock) cur_pos = prev_pos = cursor.position() while not self.is_cell_separator(cursor): # Moving to the next code cell cursor.movePosition(QTextCursor.NextBlock) prev_pos = cur_pos cur_pos = cursor.position() if cur_pos == prev_pos: return self.setTextCursor(cursor) def get_line_count(self): """Return document total line number""" return self.blockCount() def __save_selection(self): """Save current cursor selection and return position bounds""" cursor = self.textCursor() return cursor.selectionStart(), cursor.selectionEnd() def __restore_selection(self, start_pos, end_pos): """Restore cursor selection from position bounds""" cursor = self.textCursor() cursor.setPosition(start_pos) cursor.setPosition(end_pos, QTextCursor.KeepAnchor) self.setTextCursor(cursor) def __duplicate_line_or_selection(self, after_current_line=True): """Duplicate current line or selected text""" cursor = self.textCursor() cursor.beginEditBlock() start_pos, end_pos = self.__save_selection() if to_text_string(cursor.selectedText()): cursor.setPosition(end_pos) # Check if end_pos is at the start of a block: if so, starting # changes from the previous block cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor) if not to_text_string(cursor.selectedText()): cursor.movePosition(QTextCursor.PreviousBlock) end_pos = cursor.position() cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) while cursor.position() <= end_pos: cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) if cursor.atEnd(): cursor_temp = QTextCursor(cursor) cursor_temp.clearSelection() cursor_temp.insertText(self.get_line_separator()) break cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) text = cursor.selectedText() cursor.clearSelection() if not after_current_line: # Moving cursor before current line/selected text cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) start_pos += len(text) end_pos += len(text) cursor.insertText(text) cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos) def duplicate_line(self): """ Duplicate current line or selected text Paste the duplicated text *after* the current line/selected text """ self.__duplicate_line_or_selection(after_current_line=True) def copy_line(self): """ Copy current line or selected text Paste the duplicated text *before* the current line/selected text """ self.__duplicate_line_or_selection(after_current_line=False) def __move_line_or_selection(self, after_current_line=True): """Move current line or selected text""" cursor = self.textCursor() cursor.beginEditBlock() start_pos, end_pos = self.__save_selection() if to_text_string(cursor.selectedText()): # Check if start_pos is at the start of a block cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) start_pos = cursor.position() cursor.setPosition(end_pos) # Check if end_pos is at the start of a block: if so, starting # changes from the previous block cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor) if to_text_string(cursor.selectedText()): cursor.movePosition(QTextCursor.NextBlock) end_pos = cursor.position() else: cursor.movePosition(QTextCursor.StartOfBlock) start_pos = cursor.position() cursor.movePosition(QTextCursor.NextBlock) end_pos = cursor.position() cursor.setPosition(start_pos) cursor.setPosition(end_pos, QTextCursor.KeepAnchor) sel_text = to_text_string(cursor.selectedText()) cursor.removeSelectedText() if after_current_line: text = to_text_string(cursor.block().text()) start_pos += len(text)+1 end_pos += len(text)+1 cursor.movePosition(QTextCursor.NextBlock) else: cursor.movePosition(QTextCursor.PreviousBlock) text = to_text_string(cursor.block().text()) start_pos -= len(text)+1 end_pos -= len(text)+1 cursor.insertText(sel_text) cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos) def move_line_up(self): """Move up current line or selected text""" self.__move_line_or_selection(after_current_line=False) def move_line_down(self): """Move down current line or selected text""" self.__move_line_or_selection(after_current_line=True) def extend_selection_to_complete_lines(self): """Extend current selection to complete lines""" cursor = self.textCursor() start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() cursor.setPosition(start_pos) cursor.setPosition(end_pos, QTextCursor.KeepAnchor) if cursor.atBlockStart(): cursor.movePosition(QTextCursor.PreviousBlock, QTextCursor.KeepAnchor) cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) self.setTextCursor(cursor) def delete_line(self): """Delete current line""" cursor = self.textCursor() if self.has_selected_text(): self.extend_selection_to_complete_lines() start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() cursor.setPosition(start_pos) else: start_pos = end_pos = cursor.position() cursor.beginEditBlock() cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) while cursor.position() <= end_pos: cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) if cursor.atEnd(): break cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.endEditBlock() self.ensureCursorVisible() #------Code completion / Calltips def show_calltip(self, title, text, color='#2D62FF', at_line=None, at_position=None): """Show calltip""" if text is None or len(text) == 0: return # Saving cursor position: if at_position is None: at_position = self.get_position('cursor') self.calltip_position = at_position # Preparing text: weight = 'bold' if self.calltip_font.bold() else 'normal' size = self.calltip_font.pointSize() family = self.calltip_font.family() format1 = '<div style=\'font-size: %spt; color: %s\'>' % (size, color) format2 = '<hr><div style=\'font-family: "%s"; font-size: %spt; font-weight: %s\'>' % (family, size, weight) if isinstance(text, list): text = "\n ".join(text) text = text.replace('\n', '<br>') if len(text) > self.calltip_size: text = text[:self.calltip_size] + " ..." tiptext = format1 + ('<b>%s</b></div>' % title) \ + format2 + text + "</div>" # Showing tooltip at cursor position: cx, cy = self.get_coordinates('cursor') if at_line is not None: cx = 5 cursor = QTextCursor(self.document().findBlockByNumber(at_line-1)) cy = self.cursorRect(cursor).top() point = self.mapToGlobal(QPoint(cx, cy)) point.setX(point.x()+self.get_linenumberarea_width()) QToolTip.showText(point, tiptext) def hide_tooltip_if_necessary(self, key): """Hide calltip when necessary""" try: calltip_char = self.get_character(self.calltip_position) before = self.is_cursor_before(self.calltip_position, char_offset=1) other = key in (Qt.Key_ParenRight, Qt.Key_Period, Qt.Key_Tab) if calltip_char not in ('?', '(') or before or other: QToolTip.hideText() except (IndexError, TypeError): QToolTip.hideText() def show_completion_widget(self, textlist, automatic=True): """Show completion widget""" self.completion_widget.show_list(textlist, automatic=automatic) def hide_completion_widget(self): """Hide completion widget""" self.completion_widget.hide() def show_completion_list(self, completions, completion_text="", automatic=True): """Display the possible completions""" if len(completions) == 0 or completions == [completion_text]: return self.completion_text = completion_text # Sorting completion list (entries starting with underscore are # put at the end of the list): underscore = set([comp for comp in completions if comp.startswith('_')]) completions = sorted(set(completions)-underscore, key=str_lower)+\ sorted(underscore, key=str_lower) self.show_completion_widget(completions, automatic=automatic) def select_completion_list(self): """Completion list is active, Enter was just pressed""" self.completion_widget.item_selected() def insert_completion(self, text): if text: cursor = self.textCursor() cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor, len(self.completion_text)) cursor.removeSelectedText() self.insert_text(text) def is_completion_widget_visible(self): """Return True is completion list widget is visible""" return self.completion_widget.isVisible() #------Standard keys def stdkey_clear(self): if not self.has_selected_text(): self.moveCursor(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) self.remove_selected_text() def stdkey_backspace(self): if not self.has_selected_text(): self.moveCursor(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) self.remove_selected_text() def __get_move_mode(self, shift): return QTextCursor.KeepAnchor if shift else QTextCursor.MoveAnchor def stdkey_up(self, shift): self.moveCursor(QTextCursor.Up, self.__get_move_mode(shift)) def stdkey_down(self, shift): self.moveCursor(QTextCursor.Down, self.__get_move_mode(shift)) def stdkey_tab(self): self.insert_text(self.indent_chars) def stdkey_home(self, shift, ctrl, prompt_pos=None): """Smart HOME feature: cursor is first moved at indentation position, then at the start of the line""" move_mode = self.__get_move_mode(shift) if ctrl: self.moveCursor(QTextCursor.Start, move_mode) else: cursor = self.textCursor() if prompt_pos is None: start_position = self.get_position('sol') else: start_position = self.get_position(prompt_pos) text = self.get_text(start_position, 'eol') indent_pos = start_position+len(text)-len(text.lstrip()) if cursor.position() != indent_pos: cursor.setPosition(indent_pos, move_mode) else: cursor.setPosition(start_position, move_mode) self.setTextCursor(cursor) def stdkey_end(self, shift, ctrl): move_mode = self.__get_move_mode(shift) if ctrl: self.moveCursor(QTextCursor.End, move_mode) else: self.moveCursor(QTextCursor.EndOfBlock, move_mode) def stdkey_pageup(self): pass def stdkey_pagedown(self): pass def stdkey_escape(self): pass #----Qt Events def mousePressEvent(self, event): """Reimplement Qt method""" if os.name != 'posix' and event.button() == Qt.MidButton: self.setFocus() event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QPlainTextEdit.mousePressEvent(self, event) QPlainTextEdit.mouseReleaseEvent(self, event) self.paste() else: QPlainTextEdit.mousePressEvent(self, event) def focusInEvent(self, event): """Reimplemented to handle focus""" self.emit(SIGNAL("focus_changed()")) self.emit(SIGNAL("focus_in()")) QPlainTextEdit.focusInEvent(self, event) def focusOutEvent(self, event): """Reimplemented to handle focus""" self.emit(SIGNAL("focus_changed()")) QPlainTextEdit.focusOutEvent(self, event) def wheelEvent(self, event): """Reimplemented to emit zoom in/out signals when Ctrl is pressed""" # This feature is disabled on MacOS, see Issue 1510: # http://code.google.com/p/spyderlib/issues/detail?id=1510 if sys.platform != 'darwin': if event.modifiers() & Qt.ControlModifier: if event.delta() < 0: self.emit(SIGNAL("zoom_out()")) elif event.delta() > 0: self.emit(SIGNAL("zoom_in()")) return QPlainTextEdit.wheelEvent(self, event)
def read_settings_API_3(stream, settings): """ Write all settings such as fonts for labels and legend. NOTE: This function doesn't return anything and changes the settings directly.""" labelFont = QFont() legendFont = QFont() stream >> labelFont labelInterval = stream.readInt32() labelState = get_row_label_identifier(labelInterval) stream >> legendFont gridCellWidth = stream.readInt32() gridCellHeight = stream.readInt32() # make sure we parsed something sensible before # touching the settings if labelFont.family(): settings.labelFont.value = labelFont if labelState: settings.rowLabelMode.value = labelState if legendFont.family(): settings.legendFont.value = legendFont if gridCellWidth != 0: settings.gridCellWidth.value = gridCellWidth if gridCellHeight != 0: settings.gridCellHeight.value = gridCellHeight # new stuff in API_VERSION 2 and onward labelStart = stream.readInt32() settings.rowLabelStart.value = labelStart evenRowLabelLocation = stream.readInt32() settings.evenRowLabelLocation.value = \ get_row_label_location_string(evenRowLabelLocation) highlightRows = stream.readInt32() settings.highlightRows.value = highlightRows highlightRowsOpacity = stream.readInt32() settings.highlightRowsOpacity.value = highlightRowsOpacity highlightRowsStart = stream.readInt32() settings.highlightRowsStart.value = highlightRowsStart highlightRowsColor = stream.readQString() if highlightRowsColor: settings.highlightRowsColor.value = highlightRowsColor # write rest of row/column settings # NOTE: The row settings aren't combined with the rest to # remain backward compatible. oddRowLabelLocation = stream.readInt32() settings.oddRowLabelLocation.value = \ get_row_label_location_string(oddRowLabelLocation) rowLabelsShowInterval = stream.readInt32() settings.rowLabelsShowInterval.value = rowLabelsShowInterval rowLabelsShowIntervalStart = stream.readInt32() settings.rowLabelsShowIntervalStart.value = \ rowLabelsShowIntervalStart columnLabelMode = stream.readInt32() settings.columnLabelMode.value = \ get_column_label_identifier(columnLabelMode) columnLabelsShowInterval = stream.readInt32() settings.columnLabelsShowInterval.value = \ columnLabelsShowInterval columnLabelsShowIntervalStart = stream.readInt32() settings.columnLabelsShowIntervalStart.value = \ columnLabelsShowIntervalStart rowLabelsEditable = stream.readInt32() settings.rowLabelsEditable.value = rowLabelsEditable columnLabelsEditable = stream.readInt32() settings.columnLabelsEditable.value = columnLabelsEditable
class CharMap(QWidget): """A widget displaying a table of characters.""" characterSelected = pyqtSignal(str) characterClicked = pyqtSignal(str) def __init__(self, parent=None): super(CharMap, self).__init__(parent) self._showToolTips = True self._showWhatsThis = True self._selected = -1 self._column_count = 32 self._square = 24 self._range = (0, 0) self._font = QFont() def setRange(self, first, last): self._range = (first, last) self._selected = -1 self.adjustSize() self.update() def range(self): return self._range def square(self): """Returns the width of one item (determined by font size).""" return self._square def select(self, charcode): """Selects the specified character (int or str).""" if not isinstance(charcode, int): charcode = ord(charcode) if not self._range[0] <= charcode <= self._range[1]: charcode = -1 if self._selected != charcode: self._selected = charcode self.characterSelected.emit(chr(charcode)) self.update() def character(self): """Returns the currently selected character, if any.""" if self._selected != -1: return chr(self._selected) def setDisplayFont(self, font): self._font.setFamily(font.family()) self.update() def displayFont(self): return QFont(self._font) def setDisplayFontSize(self, size): self._font.setPointSize(size) self._square = max(24, QFontMetrics(self._font).xHeight() * 3) self.adjustSize() self.update() def displayFontSize(self): return self._font.pointSize() def setDisplayFontSizeF(self, size): self._font.setPointSizeF(size) self._square = max(24, QFontMetrics(self._font).xHeight() * 3) self.adjustSize() self.update() def displayFontSizeF(self): return self._font.pointSizeF() def setColumnCount(self, count): """Sets how many columns should be used.""" count = max(1, count) self._column_count = count self.adjustSize() self.update() def columnCount(self): return self._column_count def sizeHint(self): return self.sizeForColumnCount(self._column_count) def paintEvent(self, ev): rect = ev.rect() s = self._square rows = range(rect.top() // s, rect.bottom() // s + 1) cols = range(rect.left() // s, rect.right() // s + 1) painter = QPainter(self) painter.setPen(QPen(self.palette().color(QPalette.Window))) painter.setFont(self._font) metrics = QFontMetrics(self._font) # draw characters on white tiles tile = self.palette().color(QPalette.Base) selected_tile = self.palette().color(QPalette.Highlight) selected_tile.setAlpha(96) selected_box = self.palette().color(QPalette.Highlight) text_pen = QPen(self.palette().text()) disabled_pen = QPen(self.palette().color(QPalette.Disabled, QPalette.Text)) selection_pen = QPen(selected_box) for row in rows: for col in cols: char = row * self._column_count + col + self._range[0] if char > self._range[1]: break printable = self.isprint(char) painter.setClipRect(col * s, row * s, s, s) if char == self._selected: painter.fillRect(col * s + 1, row * s + 1, s - 2, s - 2, selected_tile) painter.setPen(selection_pen) painter.drawRect(col * s, row * s, s - 1, s - 1) elif printable: painter.fillRect(col * s + 1, row * s + 1, s - 2, s - 2, tile) painter.setPen(text_pen if printable else disabled_pen) t = chr(char) x = col * s + s // 2 - metrics.width(t) // 2 y = row * s + 4 + metrics.ascent() painter.drawText(x, y, t) else: continue break def sizeForColumnCount(self, count): """Returns the size the widget would have in a certain column count. This can be used in e.g. a resizable scroll area. """ first, last = self._range rows = ((last - first) // count) + 1 return QSize(count, rows) * self._square def columnCountForWidth(self, width): """Returns the number of columns that would fit into the given width.""" return width // self._square def mousePressEvent(self, ev): charcode = self.charcodeAt(ev.pos()) if charcode != -1 and self.isprint(charcode): self.select(charcode) if ev.button() != Qt.RightButton: self.characterClicked.emit(chr(charcode)) def charcodeRect(self, charcode): """Returns the rectangular box around the given charcode, if any.""" if self._range[0] <= charcode <= self._range[1]: row, col = divmod(charcode - self._range[0], self._column_count) s = self._square return QRect(col * s, row * s, s, s) def charcodeAt(self, position): row = position.y() // self._square col = position.x() // self._square if col <= self._column_count: charcode = self._range[0] + row * self._column_count + col if charcode <= self._range[1]: return charcode return -1 def event(self, ev): if ev.type() == QEvent.ToolTip: if self._showToolTips: c = self.charcodeAt(ev.pos()) if c: text = self.getToolTipText(c) if text: rect = self.charcodeRect(c) QToolTip.showText(ev.globalPos(), text, self, rect) ev.accept() return True elif ev.type() == QEvent.QueryWhatsThis: if self._showWhatsThis: ev.accept() return True elif ev.type() == QEvent.WhatsThis: ev.accept() if self._showWhatsThis: c = self.charcodeAt(ev.pos()) text = self.getWhatsThisText(c) if c else None if text: QWhatsThis.showText(ev.globalPos(), text, self) else: QWhatsThis.leaveWhatsThisMode() return True return super(CharMap, self).event(ev) def getToolTipText(self, charcode): try: return unicodedata.name(chr(charcode)) except ValueError: pass def getWhatsThisText(self, charcode): try: name = unicodedata.name(chr(charcode)) except ValueError: return return whatsthis_html.format( self._font.family(), chr(charcode), name, charcode) def setShowToolTips(self, enabled): self._showToolTips = bool(enabled) def showToolTips(self): return self._showToolTips def setShowWhatsThis(self, enabled): self._showWhatsThis = bool(enabled) def showWhatsThis(self): return self._showWhatsThis def isprint(self, charcode): """Returns True if the given charcode is printable.""" return isprint(charcode)
class TextFormatData(object): """Encapsulates all settings in the Fonts & Colors page for a scheme.""" def __init__(self, scheme): """Loads the data from scheme.""" self.font = None self.baseColors = {} self.defaultStyles = {} self.allStyles = {} self.load(scheme) def load(self, scheme): s = QSettings() s.beginGroup("fontscolors/" + scheme) # load font self.font = QFont(s.value("fontfamily", "monospace")) self.font.setPointSizeF(float(s.value("fontsize", 10.0))) # load base colors s.beginGroup("basecolors") for name in baseColors: if s.contains(name): self.baseColors[name] = QColor(s.value(name)) else: self.baseColors[name] = baseColorDefaults[name]() s.endGroup() # load default styles s.beginGroup("defaultstyles") for name in defaultStyles: self.defaultStyles[name] = f = QTextCharFormat(defaultStyleDefaults[name]) s.beginGroup(name) loadTextFormat(f, s) s.endGroup() s.endGroup() # load specific styles s.beginGroup("allstyles") for group, styles in allStyles: self.allStyles[group]= {} s.beginGroup(group) for name in styles: default = allStyleDefaults[group].get(name) self.allStyles[group][name] = f = QTextCharFormat(default) if default else QTextCharFormat() s.beginGroup(name) loadTextFormat(f, s) s.endGroup() s.endGroup() s.endGroup() def save(self, scheme): s = QSettings() s.beginGroup("fontscolors/" + scheme) # save font s.setValue("fontfamily", self.font.family()) s.setValue("fontsize", self.font.pointSizeF()) # save base colors for name in baseColors: s.setValue("basecolors/"+name, self.baseColors[name].name()) # save default styles s.beginGroup("defaultstyles") for name in defaultStyles: s.beginGroup(name) saveTextFormat(self.defaultStyles[name], s) s.endGroup() s.endGroup() # save all specific styles s.beginGroup("allstyles") for group, styles in allStyles: s.beginGroup(group) for name in styles: s.beginGroup(name) saveTextFormat(self.allStyles[group][name], s) s.endGroup() s.endGroup() s.endGroup() def textFormat(self, group, name): inherit = inherits[group].get(name) f = QTextCharFormat(self.defaultStyles[inherit]) if inherit else QTextCharFormat() f.merge(self.allStyles[group][name]) return f def palette(self): """Returns a basic palette with text, background, selection and selection background filled in.""" p = QApplication.palette() p.setColor(QPalette.Text, self.baseColors['text']) p.setColor(QPalette.Base, self.baseColors['background']) p.setColor(QPalette.HighlightedText, self.baseColors['selectiontext']) p.setColor(QPalette.Highlight, self.baseColors['selectionbackground']) return p
class ZTextWidget(QWidget): width = 80 height = 24 cur_fg = 10 cur_bg = 2 cur_style = 0 max_char = 0 start_pos = 0 #cursor_char = 0x258f #cursor_char = 0x005f cursor_char = unichr(0x2017) #cursor_char = 0x2582 input_buf = [] just_scrolled = False reading_line = False reverse_video = False _cursor_visible = False _ostream = None _input_buffer_printing = False _input_cursor_pos = 0 returnPressed = pyqtSignal(QString) keyPressed = pyqtSignal(int) pbuffer = [None]*8 pbuffer_painter = [None]*8 game_area = QImage(640, 480, QImage.Format_RGB32) game_area_painter = QPainter(game_area) chartimer = None linetimer = None brush = QBrush(Qt.SolidPattern) ztoq_color = dict({2:Qt.black, 3:Qt.red, 4:Qt.green, 5:Qt.yellow, 6:Qt.blue, 7:Qt.magenta, 8:Qt.cyan, 9:Qt.white, 10:Qt.lightGray, 11:Qt.gray, 12:Qt.darkGray}) def __init__(self,parent = None,flags = Qt.Widget): super(ZTextWidget,self).__init__(parent,flags) sp = QSizePolicy() sp.setHorizontalPolicy(QSizePolicy.Fixed) sp.setVerticalPolicy(QSizePolicy.Fixed) self.setSizePolicy(sp) self.setFocusPolicy(Qt.StrongFocus) self.pbuffer[0] = QImage(640,480,QImage.Format_RGB32) self.pbuffer[0].fill(0) font = self.font() self.normal_font = font self.fixed_font = QFont(font) self.fixed_font.setStyleHint(QFont.Monospace) self.fixed_font.setFamily(self.fixed_font.defaultFamily()) self.fixed_font.setPointSize(9) print self.fixed_font.family() #self.setFont(self.normal_font) self.setFont(self.fixed_font) self.pbuffer_painter[0] = QPainter(self.pbuffer[0]) self.pbuffer_painter[0].setFont(self.fixed_font) self.font_metrics = self.pbuffer_painter[0].fontMetrics() self.linesize = self.font_metrics.height() self.avgwidth = self.font_metrics.averageCharWidth() print self.font_metrics.averageCharWidth(), self.linesize, self.avgwidth print self.font_metrics.height() self.width = (self.pbuffer[0].width() - 4) / self.font_metrics.averageCharWidth() self.height = self.pbuffer[0].height() / self.linesize self.pbuffer_painter[0].setFont(self.normal_font) self.set_text_colour(self.cur_fg, 0) self.set_text_background_colour(self.cur_bg, 0) def paintEvent(self,e): painter = QPainter(self) painter.drawImage(0,0,self.game_area) def update_game_area(self): for i in xrange(8): if (self.pbuffer[i] != None): self.game_area_painter.drawImage(0,0,self.pbuffer[i]) self.update() def scroll(self,painter): part = self.pbuffer[0].copy(0,self.linesize,self.pbuffer[0].width(),self.pbuffer[0].height()-self.linesize) #print 'Part height:', part.height(), 'width:', part.width() self.pbuffer[0].fill(self.ztoq_color[self.cur_bg]) #print 'pbuffer[0] height:', self.pbuffer[0].height(), 'width:', self.pbuffer[0].width() painter.drawImage(0,0,part) #print 'pbuffer[0] height:', self.pbuffer[0].height(), 'width:', self.pbuffer[0].width() if (self.reading_line): self.just_scrolled = True def sizeHint(self): size = QSize() size.setWidth(640) size.setHeight(480) return size def set_max_input(self,m): self.max_char = m def show_cursor(self,window): self.lastwindow = window #self._input_cursor_pos = 0 #print self._input_cursor_pos self.insert_pos = window.cursor self.insert_real_pos = window.cursor_real_pos if (self._cursor_visible != True): # If the cursor is already visible avoid multiplying it... self.input_buf.insert(self._input_cursor_pos, self.cursor_char) self._cursor_visible = True self.clean_input_buffer_from_screen() self.draw_input_buffer() #self.draw_cursor(window,True) def hide_cursor(self,window): self._cursor_visible = False del self.input_buf[self._input_cursor_pos] #self.draw_cursor(window,False) def keyPressEvent(self,e): if e.key() == Qt.Key_Left: if self._input_cursor_pos>0: c = self.input_buf.pop(self._input_cursor_pos) self._input_cursor_pos -= 1 self.input_buf.insert(self._input_cursor_pos, c) self.clean_input_buffer_from_screen() self.draw_input_buffer() e.accept() self.keyPressed.emit(131) elif e.key() == Qt.Key_Right: if self._input_cursor_pos<(len(self.input_buf)-1): c = self.input_buf.pop(self._input_cursor_pos) self._input_cursor_pos += 1 self.input_buf.insert(self._input_cursor_pos, c) self.clean_input_buffer_from_screen() self.draw_input_buffer() e.accept() self.keyPressed.emit(132) elif e.key() == Qt.Key_Up: # TODO: Up in history e.accept() self.keyPressed.emit(129) pass elif e.key() == Qt.Key_Down: # TODO: Down in history e.accept() self.keyPressed.emit(130) pass elif e.key() == Qt.Key_Backspace: if len(self.input_buf)>1: # If there IS something to delete self.clean_input_buffer_from_screen() del self.input_buf[self._input_cursor_pos-1] self._input_cursor_pos -= 1 self.draw_input_buffer() # self.keyPressed.emit() # No keycode available for zscii e.accept() elif e.key() == Qt.Key_Delete: if self._input_cursor_pos < (len(self.input_buf) - 1): self.clean_input_buffer_from_screen() del self.input_buf[self._input_cursor_pos+1] self.draw_input_buffer() e.accept() self.keyPressed.emit(8) elif (e.key() == Qt.Key_Return) or (e.key() == Qt.Key_Enter): self.clean_input_buffer_from_screen() if (self._cursor_visible == True): self.hide_cursor(self.lastwindow) if (self.reading_line == True): self.draw_input_buffer() text = '' for i in self.input_buf: text += i #print text self.draw_text('\n', 1, self.lastwindow) self.keyPressed.emit(13) self._input_cursor_pos = 0 self.input_buf = [] self.returnPressed.emit(text) e.accept() elif ((e.key() >= Qt.Key_F1) and (e.key() <= Qt.Key_F12)): e.accept() self.keyPressed.emit(133 + e.key() - Qt.Key_F1) elif e.key() == Qt.Key_Escape: e.accept() self.keyPressed.emit(27) elif (e.text().isEmpty() == False): if (self.reading_line) and (len(self.input_buf) < self.max_char+1): self.clean_input_buffer_from_screen() self.input_buf.insert(self._input_cursor_pos, unicode(e.text())) self._input_cursor_pos += 1 self.draw_input_buffer() e.accept() t = ord(str(e.text()[0])) # TODO: Check if we can handle multiple events at once if ((t > 31) and (t < 127)) or ((t > 154) and (t <252)): self.keyPressed.emit(t) else: e.ignore() def draw_input_buffer(self): # Prepare for redraw by setting appropriate cursor position tmp_pos = self.lastwindow.cursor tmp_real_pos = self.lastwindow.cursor_real_pos self.lastwindow.set_cursor_position(self.insert_pos[0], self.insert_pos[1]) self.lastwindow.set_cursor_real_position(self.insert_real_pos[0], self.insert_real_pos[1]) self._input_buffer_printing = True self.prints(self.input_buf, self.lastwindow) self._input_buffer_printing = False if (self.just_scrolled): # A new line scroll // Is it really necessary? self.just_scrolled = False self.lastwindow.set_cursor_position(tmp_pos[0], tmp_pos[1]) self.lastwindow.set_cursor_real_position(tmp_real_pos[0], tmp_real_pos[1]) else: self.lastwindow.set_cursor_position(tmp_pos[0], tmp_pos[1]) self.lastwindow.set_cursor_real_position(tmp_real_pos[0], tmp_real_pos[1]) #self.draw_cursor(self.lastwindow, self._cursor_visible) #print self.input_buf, len(self.input_buf), self.max_char self.update_game_area() def set_text_colour(self,fg, win): self.cur_fg = fg if (self.pbuffer_painter[win] == None): self.pbuffer_painter[win] = QPainter(self.pbuffer[win]) painter = self.pbuffer_painter[win] painter.setPen(self.ztoq_color[self.cur_fg]) def set_text_background_colour(self,bg, win): self.cur_bg = bg if (self.pbuffer_painter[win] == None): self.pbuffer_painter[win] = QPainter(self.pbuffer[win]) painter = self.pbuffer_painter[win] self.brush.setColor(self.ztoq_color[self.cur_bg]) painter.setBackground(self.brush) def set_font_style(self,s,win): if s == 0: self.cur_style = 0 else: self.cur_style |= s # Now set the font accordingly newfont = self.font() # First reset the font newfont.setItalic(False) newfont.setFixedPitch(False) newfont.setBold(False) if (self.reverse_video == True): tmpbg = self.cur_bg self.set_text_background_colour(self.cur_fg,win) self.set_text_colour(tmpbg,win) self.reverse_video = False # And now check for extra style if ((self.cur_style & 1) == 1): # Reverse video self.reverse_video = True tmpbg = self.cur_bg self.set_text_background_colour(self.cur_fg,win) self.set_text_colour(tmpbg,win) if ((self.cur_style & 2) == 2): # Bold newfont.setBold(True) if ((self.cur_style & 4) == 4): # Italic newfont.setItalic(True) if ((self.cur_style & 8) == 8): # Fixed Pitch newfont.setFixedPitch(True) self.setFont(newfont) def read_line(self, window, callback, time, timeout_callback, reset): self.lastwindow = window #print reset if (reset == True): self.cur_pos = 0 self.reading_line = True self.update_game_area() self.callback_object = callback QObject.connect(self, SIGNAL("returnPressed(QString)"), self.read_line_callback) if (self.linetimer == None): self.linetimer = QTimer() self.linetimer.setSingleShot(True) if (time <> 0): self.timeout_callback_object = timeout_callback QObject.connect(self.linetimer, SIGNAL('timeout()'), self.read_line_timeout_callback) self.linetimer.start(time * 100) def read_line_callback(self, string): if (self.linetimer != None): self.linetimer.stop() self.returnPressed.disconnect() self.callback_object(string) def read_line_timeout_callback(self): self.linetimer.timeout.disconnect() self.timeout_callback_object() def disconnect_read_line(self, callback): self.reading_line = False QObject.disconnect(self, SIGNAL("returnPressed(QString)"), callback) def read_char(self, window, callback, time, timeout_callback): self.update_game_area() self.lastwindow = window self.callback_object = callback QObject.connect(self, SIGNAL("keyPressed(int)"), self.read_char_callback) #print 'Connect char' if (self.chartimer == None): self.chartimer = QTimer() self.chartimer.setSingleShot(True) if (time <> 0): self.timeout_callback_object = timeout_callback QObject.connect(self.chartimer, SIGNAL('timeout()'), self.read_char_timeout_callback) self.chartimer.start(time * 100) def read_char_callback(self, key): if (self.chartimer != None): self.chartimer.stop() self.keyPressed.disconnect() self.callback_object(key) def read_char_timeout_callback(self): self.chartimer.timeout.disconnect() self.timeout_callback_object() def disconnect_read_char(self, callback): QObject.disconnect(self, SIGNAL("keyPressed(int)"), callback) #print 'Disconnect char' def prints(self, txt, window): txtlen = len(txt) if (txtlen == 1): # print_char got us here... self.draw_text(txt[0],1,window) else: lastspace = 0 i = 0 textbuffer = '' tblen = 0 for w in txt: if (w == '\n' or w == self.cursor_char): if (tblen>0): # If there is something to print self.draw_text(textbuffer, tblen, window) textbuffer = '' tblen = 0 self.draw_text(w, 1, window) if (w == '\n'): # \n is whitespace :-) lastspace = i elif (w == ' '): # Space was found if (lastspace == i-1): # Continuous spaces textbuffer += w tblen += 1 else: self.draw_text(textbuffer, tblen, window) self.draw_text(' ', 1, window) textbuffer = '' tblen = 0 lastspace = i else: textbuffer += w tblen += 1 i += 1 if (textbuffer != ''): # Buffer not empty self.draw_text(textbuffer, tblen, window) def draw_text(self, txt, txtlen, window): if ((txtlen>0) and not ((txt == self.cursor_char) and (self._cursor_visible == False))): # If there IS something to print if (self.pbuffer_painter[window.id] == None): self.brush.setColor(self.ztoq_color[self.cur_bg]) self.pbuffer_painter[window.id] = QPainter(self.pbuffer[window.id]) self.pbuffer_painter[window.id].setPen(self.ztoq_color[self.cur_fg]) self.pbuffer_painter[window.id].setBackground(self.brush) painter = self.pbuffer_painter[window.id] # @type window ZWindow if (window.cursor == None): if (window.id == 0): # Main window window.set_cursor_position(1, self.height) window.set_cursor_real_position(2, self.height*(self.linesize-1)) else: window.set_cursor_position(1, 1) window.set_cursor_real_position(2, self.linesize-1) if (txt=='\n'): if (window.cursor[1]==self.height): if (window.scrolling): self.scroll(painter) window.set_cursor_position(1, window.cursor[1]) window.set_cursor_real_position(2, window.cursor_real_pos[1]) else: window.set_cursor_position(1, window.cursor[1]+1) window.set_cursor_real_position(2, window.cursor_real_pos[1]+self.linesize) else: rect = QRectF(window.cursor_real_pos[0], window.cursor_real_pos[1], self.pbuffer[window.id].width()-window.cursor_real_pos[0], self.linesize) painter.setFont(self.font()) #painter.setRenderHint(QPainter.TextAntialiasing) if (self._input_buffer_printing == False): painter.setBackgroundMode(Qt.OpaqueMode) else: painter.setBackgroundMode(Qt.TransparentMode) bounding_rect = painter.boundingRect(rect,txt) if (rect.contains(bounding_rect)): #print rect.x(), rect.y(), rect.width(),rect.height(), txt, bounding_rect painter.drawText(bounding_rect, txt) if txt != self.cursor_char: window.set_cursor_position(window.cursor[0]+txtlen, window.cursor[1]) window.set_cursor_real_position(rect.x()+bounding_rect.width(), rect.y()) else: # There is not enough space #print "Not enough space to print:", txt self.scroll(painter) window.set_cursor_position(1, self.height) window.set_cursor_real_position(2, self.height*(self.linesize-1)) rect.setX(2) rect.setY(window.cursor_real_pos[1]) rect.setWidth(self.pbuffer[window.id].width()-window.cursor_real_pos[0]) rect.setHeight(self.linesize) bounding_rect = painter.boundingRect(rect,txt) painter.drawText(bounding_rect, txt) if txt != self.cursor_char: window.set_cursor_position(window.cursor[0]+txtlen, window.cursor[1]) window.set_cursor_real_position(rect.x()+bounding_rect.width(), rect.y()) def buffered_string(self, txt, window): # @type window ZWindow if (window.buffering): rect = QRect() rect.setX(window.cursor_real_pos[0]) rect.setY(window.cursor_real_pos[1]) rect.setWidth(window.width-window.cursor_real_pos[0]) rect.setHeight(self.linesize) bounding_rect = painter.boundingRect(rect,txt) if (rect.contains(bounding_rect)): # string fits in this line return txt else: return txt def clean_input_buffer_from_screen(self): rect = QRectF() rect.setX(self.lastwindow.cursor_real_pos[0]) rect.setY(self.lastwindow.cursor_real_pos[1]) rect.setWidth(self.pbuffer[0].width()-self.lastwindow.cursor_real_pos[0]+1) rect.setHeight(self.linesize) txtbuffer = '' for w in self.input_buf: txtbuffer += w bounding_rect = self.pbuffer_painter[0].boundingRect(rect, txtbuffer) if (rect.contains(bounding_rect)): # string fits in this line self.pbuffer_painter[0].eraseRect(bounding_rect) #self.pbuffer_painter.drawRect(bounding_rect) #print 'Erasing rect', bounding_rect else: self.pbuffer_painter[0].eraseRect(rect) #print 'Erasing rect', rect # FIXME: clear next lines def clear(self): #print 'clearing...' self.game_area.fill(self.ztoq_color[self.cur_bg]) for i in xrange(8): if (self.pbuffer[i] != None): self.pbuffer[i].fill(self.ztoq_color[self.cur_bg]) def update_real_cursor_position(self, w): w.set_cursor_real_position(2+(w.cursor[0]-1)*self.avgwidth, (w.cursor[1]-1)*self.linesize) #print w.cursor, '->', w.cursor_real_pos def erase_window(self, w): if ((w.id >= 0) and (w.id < 8)): if (self.pbuffer_painter[w.id] == None): self.pbuffer_painter[w.id] = QPainter(self.pbuffer[w.id]) self.pbuffer_painter[w.id].setPen(self.ztoq_color[self.cur_fg]) self.brush.setColor(self.ztoq_color[self.cur_bg]) self.pbuffer_painter[w.id].setBackground(self.brush) self.pbuffer_painter[w.id].setBackgroundMode(Qt.OpaqueMode) self.pbuffer_painter[w.id].eraseRect(QRectF(0, 0, self.pbuffer[w.id].width(), w.line_count*self.linesize)) #print 2, 0, self.pbuffer[w.id].width()-2, w.line_count*self.linesize else: traceback.print_stack() print 'erase_window for window',w.id sys.exit() def split_window(self, lines, ver): #print 'Lines:', lines # Copy window 1 to window 0 if it already exists if (self.pbuffer[1] != None): self.pbuffer_painter[0].drawImage(0,0,self.pbuffer[1]) if (lines == 0): # Unsplit #self.pbuffer[1].fill(self.ztoq_color(self.cur_bg)) #del self.pbuffer_painter[1] #del self.pbuffer[1] self.pbuffer_painter[1] = None self.pbuffer[1] = None else: if (self.pbuffer[1] != None): # Window needs resizing tmp = self.pbuffer[1] #del self.pbuffer_painter[1] self.pbuffer_painter[1] = None self.pbuffer[1] = self.pbuffer[1].copy(0,0,self.pbuffer[1].width(),lines*self.linesize) #del tmp else: # New window self.pbuffer[1] = QImage(self.pbuffer[0].width(),lines*self.linesize,QImage.Format_RGB32) self.pbuffer[1].fill(0) if ver == 3: self.pbuffer[1].fill(self.ztoq_color[self.cur_bg]) def stop_line_timer(self): if (self.linetimer != None): self.linetimer.stop() def stop_char_timer(self): if (self.chartimer != None): self.chartimer.stop()
class TextFormatData(object): """Encapsulates all settings in the Fonts & Colors page for a scheme.""" def __init__(self, scheme): """Loads the data from scheme.""" self.font = None self.baseColors = {} self.defaultStyles = {} self.allStyles = {} self._inherits = {} self.load(scheme) def load(self, scheme): """Load the settings for the scheme. Called on init.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # load font defaultfont = "Lucida Console" if os.name == "nt" else "monospace" self.font = QFont(s.value("fontfamily", defaultfont, type(""))) self.font.setPointSizeF(s.value("fontsize", 10.0, float)) # load base colors s.beginGroup("basecolors") for name in baseColors: if s.contains(name): self.baseColors[name] = QColor(s.value(name, "", type(""))) else: self.baseColors[name] = baseColorDefaults[name]() s.endGroup() # get the list of supported styles from ly.colorize all_styles = ly.colorize.default_mapping() default_styles = set() for group, styles in all_styles: d = self._inherits[group] = {} for style in styles: if style.base: default_styles.add(style.base) d[style.name] = style.base default_scheme = ly.colorize.default_scheme # load default styles s.beginGroup("defaultstyles") for name in default_styles: self.defaultStyles[name] = f = QTextCharFormat() css = default_scheme[None].get(name) if css: css2fmt(css, f) s.beginGroup(name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() # load specific styles s.beginGroup("allstyles") for group, styles in all_styles: self.allStyles[group]= {} s.beginGroup(group) for style in styles: self.allStyles[group][style.name] = f = QTextCharFormat() css = default_scheme[group].get(style.name) if css: css2fmt(css, f) s.beginGroup(style.name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() s.endGroup() def save(self, scheme): """Save the settings to the scheme.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # save font s.setValue("fontfamily", self.font.family()) s.setValue("fontsize", self.font.pointSizeF()) # save base colors for name in baseColors: s.setValue("basecolors/"+name, self.baseColors[name].name()) # save default styles s.beginGroup("defaultstyles") for name in defaultStyles: s.beginGroup(name) self.saveTextFormat(self.defaultStyles[name], s) s.endGroup() s.endGroup() # save all specific styles s.beginGroup("allstyles") for group, styles in ly.colorize.default_mapping(): s.beginGroup(group) for style in styles: s.beginGroup(style.name) self.saveTextFormat(self.allStyles[group][style.name], s) s.endGroup() s.endGroup() s.endGroup() def textFormat(self, group, name): """Return a QTextCharFormat() for the specified group and name.""" inherit = self._inherits[group].get(name) f = QTextCharFormat(self.defaultStyles[inherit]) if inherit else QTextCharFormat() f.merge(self.allStyles[group][name]) return f def css_scheme(self): """Return a dictionary of css dictionaries representing this scheme. This can be fed to the ly.colorize.format_stylesheet() function. """ scheme = {} # base/default styles d = scheme[None] = {} for name, fmt in self.defaultStyles.items(): d[name] = fmt2css(fmt) # mode/group styles for mode, styles in self.allStyles.items(): d = scheme[mode] = {} for name, fmt in styles.items(): d[name] = fmt2css(fmt) return scheme def palette(self): """Return a basic palette with text, background, selection and selection background filled in.""" p = QApplication.palette() p.setColor(QPalette.Text, self.baseColors['text']) p.setColor(QPalette.Base, self.baseColors['background']) p.setColor(QPalette.HighlightedText, self.baseColors['selectiontext']) p.setColor(QPalette.Highlight, self.baseColors['selectionbackground']) return p def saveTextFormat(self, fmt, settings): """(Internal) Store one QTextCharFormat in the QSettings instance.""" if fmt.hasProperty(QTextFormat.FontWeight): settings.setValue('bold', fmt.fontWeight() >= 70) else: settings.remove('bold') if fmt.hasProperty(QTextFormat.FontItalic): settings.setValue('italic', fmt.fontItalic()) else: settings.remove('italic') if fmt.hasProperty(QTextFormat.TextUnderlineStyle): settings.setValue('underline', fmt.fontUnderline()) else: settings.remove('underline') if fmt.hasProperty(QTextFormat.ForegroundBrush): settings.setValue('textColor', fmt.foreground().color().name()) else: settings.remove('textColor') if fmt.hasProperty(QTextFormat.BackgroundBrush): settings.setValue('backgroundColor', fmt.background().color().name()) else: settings.remove('backgroundColor') if fmt.hasProperty(QTextFormat.TextUnderlineColor): settings.setValue('underlineColor', fmt.underlineColor().name()) else: settings.remove('underlineColor') def loadTextFormat(self, fmt, settings): """(Internal) Merge values from the QSettings instance into the QTextCharFormat.""" if settings.contains('bold'): fmt.setFontWeight(QFont.Bold if settings.value('bold', False, bool) else QFont.Normal) if settings.contains('italic'): fmt.setFontItalic(settings.value('italic', False, bool)) if settings.contains('underline'): fmt.setFontUnderline(settings.value('underline', False, bool)) if settings.contains('textColor'): fmt.setForeground(QColor(settings.value('textColor', '' , type("")))) if settings.contains('backgroundColor'): fmt.setBackground(QColor(settings.value('backgroundColor', '' , type("")))) if settings.contains('underlineColor'): fmt.setUnderlineColor(QColor(settings.value('underlineColor', '' , type(""))))
class TextFormatData(object): """Encapsulates all settings in the Fonts & Colors page for a scheme.""" def __init__(self, scheme): """Loads the data from scheme.""" self.font = None self.baseColors = {} self.defaultStyles = {} self.allStyles = {} self.load(scheme) def load(self, scheme): """Load the settings for the scheme. Called on init.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # load font self.font = QFont(s.value("fontfamily", "monospace")) self.font.setPointSizeF(float(s.value("fontsize", 10.0))) # load base colors s.beginGroup("basecolors") for name in baseColors: if s.contains(name): self.baseColors[name] = QColor(s.value(name)) else: self.baseColors[name] = baseColorDefaults[name]() s.endGroup() # load default styles s.beginGroup("defaultstyles") for name in defaultStyles: self.defaultStyles[name] = f = QTextCharFormat(defaultStyleDefaults[name]) s.beginGroup(name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() # load specific styles s.beginGroup("allstyles") for group, styles in allStyles: self.allStyles[group]= {} s.beginGroup(group) for name in styles: default = allStyleDefaults[group].get(name) self.allStyles[group][name] = f = QTextCharFormat(default) if default else QTextCharFormat() s.beginGroup(name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() s.endGroup() def save(self, scheme): """Save the settings to the scheme.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # save font s.setValue("fontfamily", self.font.family()) s.setValue("fontsize", self.font.pointSizeF()) # save base colors for name in baseColors: s.setValue("basecolors/"+name, self.baseColors[name].name()) # save default styles s.beginGroup("defaultstyles") for name in defaultStyles: s.beginGroup(name) self.saveTextFormat(self.defaultStyles[name], s) s.endGroup() s.endGroup() # save all specific styles s.beginGroup("allstyles") for group, styles in allStyles: s.beginGroup(group) for name in styles: s.beginGroup(name) self.saveTextFormat(self.allStyles[group][name], s) s.endGroup() s.endGroup() s.endGroup() def textFormat(self, group, name): """Return a QTextCharFormat() for the specified group and name.""" inherit = inherits[group].get(name) f = QTextCharFormat(self.defaultStyles[inherit]) if inherit else QTextCharFormat() f.merge(self.allStyles[group][name]) return f def palette(self): """Return a basic palette with text, background, selection and selection background filled in.""" p = QApplication.palette() p.setColor(QPalette.Text, self.baseColors['text']) p.setColor(QPalette.Base, self.baseColors['background']) p.setColor(QPalette.HighlightedText, self.baseColors['selectiontext']) p.setColor(QPalette.Highlight, self.baseColors['selectionbackground']) return p def saveTextFormat(self, fmt, settings): """(Internal) Store one QTextCharFormat in the QSettings instance.""" if fmt.hasProperty(QTextFormat.FontWeight): settings.setValue('bold', fmt.fontWeight() >= 70) else: settings.remove('bold') if fmt.hasProperty(QTextFormat.FontItalic): settings.setValue('italic', fmt.fontItalic()) else: settings.remove('italic') if fmt.hasProperty(QTextFormat.TextUnderlineStyle): settings.setValue('underline', fmt.fontUnderline()) else: settings.remove('underline') if fmt.hasProperty(QTextFormat.ForegroundBrush): settings.setValue('textColor', fmt.foreground().color().name()) else: settings.remove('textColor') if fmt.hasProperty(QTextFormat.BackgroundBrush): settings.setValue('backgroundColor', fmt.background().color().name()) else: settings.remove('backgroundColor') if fmt.hasProperty(QTextFormat.TextUnderlineColor): settings.setValue('underlineColor', fmt.underlineColor().name()) else: settings.remove('underlineColor') def loadTextFormat(self, fmt, settings): """(Internal) Merge values from the QSettings instance into the QTextCharFormat.""" if settings.contains('bold'): fmt.setFontWeight(QFont.Bold if settings.value('bold') in (True, 'true') else QFont.Normal) if settings.contains('italic'): fmt.setFontItalic(settings.value('italic') in (True, 'true')) if settings.contains('underline'): fmt.setFontUnderline(settings.value('underline') in (True, 'true')) if settings.contains('textColor'): fmt.setForeground(QColor(settings.value('textColor'))) if settings.contains('backgroundColor'): fmt.setBackground(QColor(settings.value('backgroundColor'))) if settings.contains('underlineColor'): fmt.setUnderlineColor(QColor(settings.value('underlineColor')))