def find(self, checked): print(checked) # Grab the parent's text text = self.parent.text_doc.toPlainText() # And the text to find query = self.findField.toPlainText() # By default regexes are case sensitive but usually a search isn't # case sensitive by default, so we need to switch this around here flags = 0 if self.caseSens.isChecked() else re.I # Compile the pattern pattern = re.compile(query, flags) # If the last match was successful, start at position after the last # match's start, else at 0 start = self.lastMatch.start() + 1 if self.lastMatch else 0 # The actual search self.lastMatch = pattern.search(text, start) if self.lastMatch: start = self.lastMatch.start() end = self.lastMatch.end() lineColor = QtGui.QColor(QtCore.Qt.green).lighter(160) Highlighter.highlightText(self.parent.text_doc, start, end, lineColor) else: # if the search was unsuccessful print("Nothing was found")
class Widget(QTextEdit): result = [] def __init__(self, left, top, width, height, parent=None): super().__init__(parent) self.left = left self.top = top self.width = width self.height = height self.setParent(parent) self.initWidget() self.highLighter = Highlighter(self.document()) # self.highLighter = Highlighter(self.document()) # self.setStyleSheet("""QTextEdit{ # font-family:'Consolas'; # color: #ccc; # background-color: #2b2b2b;}""") def returnResult(self, res): self.highLighter.getResult(res) def initWidget(self): self.setGeometry(self.left, self.top, self.width, self.height) self.show() def setText(self, text): super().setText(text) def getText(self): super().toPlainText()
def __init__(self, *args, **kwargs): super(EditorBase, self).__init__(*args, **kwargs) # Make sure editor always has a monospace font. self.__zoom = 0 self.setFont() self.__highlighter = Highlighter(self.document()) self.__highlighter.setRules(PythonHighlighter) # Set general document option option = self.document().defaultTextOption() option.setFlags(option.flags() | option.IncludeTrailingSpaces | option.AddSpaceForLineAndParagraphSeparators) if self.indentUsingSpaces(): option.setFlags(option.flags() | option.ShowTabsAndSpaces) self.document().setDefaultTextOption(option) # View Settings # self.setShowWhitespace() # self.setShowLineEndings() # self.setWrap() # self.setHighlightCurrentLine() # self.setLongLineIndicatorPosition() # So that graphical elements wont break. self.cursorPositionChanged.connect(self.viewport().update)
def __init__(self, player_key): self.level = None self.player = None self.player_key = player_key self.objects = [] self.ui = UI(self) self.turn_tracker = TurnTracker(self) self.turn = 'player' self.active_ability = None self.effect_tracker = EffectTracker(self) self.clock = pygame.time.Clock() self.tick = 0 self.pointer = Pointer(self) self.highlighter = Highlighter(self) self.sub_screen = pygame.Surface((720, 720)).convert() self.game_display = GameDisplay(self) self.screen_mode = 'zoomed' self.zoomed_sub_screen = pygame.Surface((19 * 16, 15 * 24)).convert() self.zoomed_sub_screen_scale = pygame.Surface( (19 * 16 * 2, 15 * 24 * 2)).convert() self.zoomed_sub_screen_scale_rect = self.zoomed_sub_screen_scale.get_rect( ) self.zoomed_sub_screen_scale_rect.topleft = (56, 0)
def __init__(self): super().__init__() self.title('Python Text Editor v3') self.geometry('800x600') self.foreground = 'black' self.background = 'lightgrey' self.text_foreground = 'black' self.text_background = 'white' self.load_scheme_file('schemes/default.yaml') self.configure_ttk_elements() self.font_size = 15 self.font_family = "Ubuntu Mono" self.load_font_file('schemes/font.yaml') self.text_area = TextArea(self, bg=self.text_background, fg=self.text_foreground, undo=True, font=(self.font_family, self.font_size)) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.scroll_text) self.text_area.configure(yscrollcommand=self.scrollbar.set) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1) self.highlighter = Highlighter(self.text_area, 'languages/python.yaml') self.menu = tk.Menu(self, bg=self.background, fg=self.foreground) self.all_menus = [self.menu] sub_menu_items = ["file", "edit", "tools", "help"] self.generate_sub_menus(sub_menu_items) self.configure(menu=self.menu) self.right_click_menu = tk.Menu(self, bg=self.background, fg=self.foreground, tearoff=0) self.right_click_menu.add_command(label='Cut', command=self.edit_cut) self.right_click_menu.add_command(label='Copy', command=self.edit_copy) self.right_click_menu.add_command(label='Paste', command=self.edit_paste) self.all_menus.append(self.right_click_menu) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events() self.open_file = ''
def load_syntax_highlighting_file(self): syntax_file = filedialog.askopenfilename(filetypes=[("YAML file", ("*.yaml", "*.yml"))]) if syntax_file: self.highlighter.clear_highlight() self.highlighter = Highlighter(self.text_area, syntax_file) self.highlighter.force_highlight()
def load_syntax_highlighting(self): syntax_file = filedialog.askopenfilename(filetypes=[('YAML file', ('*.yaml', '*.yml'))]) if syntax_file: self.highlighter.clear_highlight() self.highlighter = Highlighter(self.text_area, syntax_file) self.highlighter.force_highlight()
def terminal_mode(self, event=None): if self.thee_mode == 1: self.line_numbers.destroy() self.text_area.config(state=tk.NORMAL, tabs=4) self.text_area.delete('1.0', tk.END) self.text_area.insert(tk.END, self.terminal_mode_buffer) self.terminal.writeLoop() self.highlighter = Highlighter(self.text_area) self.thee_mode = 2
def __init__(self, left, top, width, height, parent=None): super().__init__(parent) self.left = left self.top = top self.width = width self.height = height self.setParent(parent) self.initWidget() self.highLighter = Highlighter(self.document())
def highlight(): data = request.get_json() highlight = Highlighter(data['filename'], data['code']) return_dict = { 'highlighted_code': highlight.get_highlighted_code(), 'css_class': highlight.get_stylesheet_content(), 'id_name': highlight.get_css_id_name(), 'success': True, } return jsonify(return_dict)
def __init__(self): super().__init__() self.text_area = TextArea(self, bg="white", fg="black", undo=True) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.scroll_text) self.text_area.configure(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1) self.highlighter = Highlighter(self.text_area, 'languages/python.yaml') # first_100_numbers = [str(n+1) for n in range(100)] # self.line_numbers.insert(1.0, "\n".join(first_100_numbers)) # self.line_numbers.configure(state="disabled", width=3) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events()
def __init__(self, parent=None): super(Editor, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.file_path = None self.proc = QProcess() Highlighter(self.ui.text_editor) self.ui.actionRun.triggered.connect(self.run_triggered) self.ui.actionOpen.triggered.connect(self.open_triggered) self.ui.actionSave.triggered.connect(self.save_triggered) self.ui.actionClose.triggered.connect(self.close_triggered) self.proc.started.connect(self.process_started) self.proc.errorOccurred.connect(self.process_error_occurred) self.proc.readyReadStandardOutput.connect( self.process_ready_read_standard_output) self.proc.finished.connect(self.process_finished) self.proc.readyReadStandardError.connect( self.process_ready_read_standard_error)
def __init__(self, settings, parent_textEdit): sys.excepthook = exception_handler QtWidgets.QDialog.__init__(self) self.settings = settings self.parent_textEdit = parent_textEdit self.queryTime = "" self.queryFilters = "" textEditSql = "" # Set up the user interface from Designer. self.ui = Ui_Dialog_sql() self.ui.setupUi(self) #self.setWindowTitle("Query: " + self.queryname) self.ui.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu) highlighter = Highlighter(self.ui.textEdit_sql) self.ui.textEdit_sql.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.textEdit_sql.customContextMenuRequested.connect(self.sql_menu) # fill textEdit_sql from queryname self.ui.textEdit_sql.setText(textEditSql) self.ui.tableWidget_results.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.tableWidget_results.customContextMenuRequested.connect( self.table_menu) # Add tables and fields to treeWidget self.get_schema_update_treeWidget() self.ui.treeWidget.itemClicked.connect(self.get_item) self.ui.pushButton_runSQL.clicked.connect(self.run_SQL) self.ui.pushButton_export.clicked.connect(self.export_file) self.ui.splitter.setSizes([20, 180]) self.ui.splitter_2.setSizes([10, 290])
def editor_mode(self, event=None): self.text_area.config(state=tk.NORMAL, tabs=4) self.text_area.delete('1.0', tk.END) self.text_area.insert(tk.END, self.editor_mode_buffer) self.highlighter = Highlighter(self.text_area) self.thee_mode = 1 self.line_numbers = LineNumbers(self.frame1, self.text_area) self.line_numbers.config(bg=self.text_background, width=len(self.line_numbers.line_number) * 10, highlightthickness=0) self.line_numbers.grid(row=0, column=0, sticky='ns') self.status_bar1.set("Line %d, Column %d" % (self.line, self.column)) self.status_bar3.set("%s" % self.file_name) self.status_bar4.set("Spaces: %d" % self.spaces) self.status_bar5.set("%s" % self.status)
def main(): highlighter = Highlighter() queries = make_queries_dict() if FLAGS.input_file is not None: scrape_document_file(FLAGS.input_file, queries, highlighter) else: scrape_query_file(queries[FLAGS.query], highlighter)
def test_highlight_doc(self): highlighted_result = Highlighter.highlight_doc(self.doc, self.query) msg = "\nhighlighted_result =\n" + highlighted_result expected_result = " [[HIGHLIGHT]]tacos[[HIGHLIGHT]] I've ever had were from Panchos. " expected_result = expected_result + "This sentence is about nothing. Great [[HIGHLIGHT]]" expected_result = expected_result + "tacos burritos[[HIGHLIGHT]], and [[HIGHLIGHT]]" expected_result = expected_result + "enchaladas[[HIGHLIGHT]]. Bean [[HIGHLIGHT]]burritos[[HIGHLIGHT]]" msg = msg + "\nexpected_result = \n" + expected_result self.assertEquals(expected_result, highlighted_result, msg)
def __init__(self, app, parent_textEdit): sys.excepthook = exception_handler QtWidgets.QDialog.__init__(self) self.app = app self.parent_textEdit = parent_textEdit self.queryTime = "" self.queryFilters = "" textEditSql = "" # Set up the user interface from Designer. self.ui = Ui_Dialog_sql() self.ui.setupUi(self) try: w = int(self.app.settings['dialogsql_w']) h = int(self.app.settings['dialogsql_h']) if h > 50 and w > 50: self.resize(w, h) except: pass self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) font = 'font: ' + str(self.app.settings['fontsize']) + 'pt ' font += '"' + self.app.settings['font'] + '";' self.setStyleSheet(font) #self.setWindowTitle("Query: " + self.queryname) self.ui.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu) highlighter = Highlighter(self.ui.textEdit_sql) self.ui.textEdit_sql.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.textEdit_sql.customContextMenuRequested.connect(self.sql_menu) # fill textEdit_sql from queryname self.ui.textEdit_sql.setText(textEditSql) self.ui.tableWidget_results.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.tableWidget_results.customContextMenuRequested.connect( self.table_menu) # Add tables and fields to treeWidget self.get_schema_update_treeWidget() self.ui.treeWidget.itemClicked.connect(self.get_item) self.ui.pushButton_runSQL.clicked.connect(self.run_SQL) self.ui.pushButton_export.clicked.connect(self.export_file) self.ui.splitter.setSizes([20, 180]) try: s0 = int(self.app.settings['dialogsql_splitter_h0']) s1 = int(self.app.settings['dialogsql_splitter_h1']) if s0 > 10 and s1 > 10: self.ui.splitter.setSizes([s0, s1]) except: pass self.ui.splitter_2.setSizes([10, 290]) try: s0 = int(self.app.settings['dialogsql_splitter_v0']) s1 = int(self.app.settings['dialogsql_splitter_v1']) if s0 > 10 and s1 > 10: self.ui.splitter_2.setSizes([s0, s1]) except: pass
def setUp(self): doc = "This is my review on Panchos Mexican Restaurant. This is only a " doc = doc + "test of the emergency broadcasting system. The best " doc = doc + "tacos I've ever had were from Panchos. This sentence is " doc = doc + "about nothing. Great tacos, burritos, and enchaladas. " doc = doc + "Bean burritos are also good. Lots more filler text here. " doc = doc + "The filler text in this sentence is muy bueno. Remember to " doc = doc + "eat lots of tacos." self.doc = doc self.query = "tacos burritos enchaladas" self.hi = Highlighter(self.doc, self.query)
def load_cursorPos(self, checked): print(checked) elements = self.data check = self.item_data[2] index = self.j_tree.currentIndex() item = self.model.itemFromIndex(index) item_data = QtGui.QStandardItem.data(item) self.item_text = QtGui.QStandardItem.text(item) for text in elements["Benchmarks"]: if item_data[0] == 2: for el in text["group_ids"]: if el["id_dupl"] == check: pos = el["position"] start = pos[0] end = pos[1] # length = end - start lineColor = QtGui.QColor(QtCore.Qt.red).lighter(160) Highlighter.highlightText(self.text_doc, start, end, lineColor)
def __init__(self, manager, white_player='human', black_player='human'): # game state members self.game_manager = manager self.game_running = False self.needs_redraw = True self.screen = None self.clock = pygame.time.Clock() # game components self.game_logic = GameLogic(self) self.game_grid = GameGrid(self) self.game_board = None self.white_player = None self.black_player = None self.initialize_players(white_player, black_player) self.turn_manager = TurnManager(self) self.highlighter = Highlighter(self) self.logger = Logger(self) self.buttons = {}
def __init__(self, console, simulation_keywords): self.simulation_keywords = simulation_keywords self.gui = Gtk.Table() self.window = Gtk.ScrolledWindow() self.console = console self.prevent_button_update = False self._textview = Gtk.TextView() self._textbuffer = GtkSource.Buffer() self._textview.set_buffer(self._textbuffer) self._textbuffer.set_max_undo_levels(-1) self._textbuffer.set_undo_manager(None) self._textbuffer.connect("changed", self.on_text_changed) self._textbuffer.connect("paste-done", self.on_text_changed) self.error_tag = self._textbuffer.create_tag("error", background="orange") font_description = Pango.FontDescription("monospace 9") self._textview.modify_font(font_description) misc.set_tabs(self._textview, font_description, 4) self.window.set_shadow_type(Gtk.ShadowType.IN) misc.gtk_set_margin(self.gui, 5, top=10) # Init highlight: self.highlight = Highlighter(self._textbuffer) self.load_highlight() self.error_bar = Gtk.InfoBar() self.error_bar_label = Gtk.Label() self.error_bar.get_content_area().add(self.error_bar_label) self.error_bar_label.show() self.error_bar.set_message_type(Gtk.MessageType.ERROR) self.window.add(self._textview) #self.error_bar.set_size_request(80, 100) self.gui.attach(self.window, 0, 8, 0, 1) self.gui.attach(self.error_bar, 8, 10, 0, 1) self.error_bar.set_no_show_all(True) # Init script self.load_simulation("./simulations/welcome.py")
def register_syntax(self, fileName): ext = manage_files.get_file_extension(fileName)[1:] if self.highlighter is not None and \ not self.path.endswith(ext): self.highlighter.deleteLater() if not self.path.endswith(ext): if ext in loader.extensions: self.highlighter = Highlighter( self.document(), loader.extensions.get(ext, 'py')) else: try: self.highlighter = HighlighterPygments( self.document(), fileName) except: print 'There is no lexer for this file' #for apply rehighlighting (rehighlighting form highlighter not responding) self.firstVisibleBlock().document().find('\n').insertText('')
def __init__(self): super().__init__() self.text_area = TextArea(self, bg="white", fg="black", undo=True) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.scroll_text) self.text_area.configure(yscrollcommand=self.scrollbar.set) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1) self.highlighter = Highlighter(self.text_area, 'languages/python.yaml') self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events()
def __init__(self, parent): super(NoteEditor, self).__init__(parent) self.highlighter = Highlighter( self.document() ) # the language dictionary to be used self.dictionary = None self.highlighter.setDictionary(self.dictionary) # create dictionary selector menu self.dictionarySelector = QMenu("&Dictionary") self.dictionaryActions = QActionGroup(self) self.noDicitionaryAction = self.dictionaryActions.addAction("None") self.noDicitionaryAction.setCheckable(True) self.dictionarySelector.addAction(self.noDicitionaryAction) self.defaultDicitionaryAction = self.dictionaryActions.addAction("Default") self.defaultDicitionaryAction.setCheckable(True) self.dictionarySelector.addAction(self.defaultDicitionaryAction) self.dictionarySelector.addSeparator() for lang in enchant.list_languages(): langAction = self.dictionaryActions.addAction(lang) langAction.setCheckable(True) self.dictionarySelector.addAction(langAction) # connect signal to change language dictionary and set the default language self.dictionaryActions.triggered.connect(self.changeDictionary) self.defaultDicitionaryAction.trigger() # configure the custom context menu for spelling suggestions self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showContextMenu) # settings self.noteFilePath = "" #stores path to the file being edited self.setFont(QFont("Monospace")) tabWidth = QFontMetrics( self.currentCharFormat().font() ).width(" ")#get the width of four spaces self.setTabStopWidth(tabWidth) #set the default tab width to be that of four spaces
def __init__(self, parent, type): super().__init__() self._parent = parent self._type = type fileLabel = QtWidgets.QLabel() fileLabel.setText("File:") self._fileComboBox = QtWidgets.QComboBox() self._fileComboBox.currentIndexChanged.connect(self.currentChanged) fileLayout = QtWidgets.QHBoxLayout() fileLayout.addWidget(fileLabel) fileLayout.addWidget(self._fileComboBox) self._editor = ShaderSource() self._highlighter = Highlighter(self._editor.document()) layout = QtWidgets.QVBoxLayout(self) layout.addLayout(fileLayout) layout.addWidget(self._editor)
def init_ui(self): self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit) font = QFont("Menlo", 12) # TODO: Create a layout and change the line spacing #spacing = QFontMetrics(font).lineSpacing() self.text_edit.setFont(font) self.highlighter = Highlighter(self.text_edit.document(), DEFAULT_RULES, DEFAULT_STYLE) #print("Highlighter doc: {}".format(self.text_edit.document())) menu_bar = self.menuBar() m_file = menu_bar.addMenu("File") i_open = QAction("Open", self) i_open.setShortcut('Ctrl+O') i_open.triggered.connect(self.on_open) m_file.addAction(i_open) self.setGeometry(300, 300, 350, 300) self.setWindowTitle("tea")
self.setExtraSelections(extraSelections) if __name__ == "__main__": try: import json except ImportError: import simplejson as json from highlighter import Highlighter app = QtGui.QApplication(sys.argv) js = CodeEditor() js.setWindowTitle('javascript') hl = Highlighter(js.document(), "javascript") js.show() def validateJSON(): style = str(js.toPlainText()) if not style.strip(): #no point in validating an empty string return pos = None try: json.loads(style) except ValueError: _, e, _ = sys.exc_info() s = str(e) print(s) if s == 'No JSON object could be decoded': pos = 0
def highlight(text, q, max_length=200, nchars_before=0): text = strip_tags(text) highlight = Highlighter(q, max_length=max_length) return highlight.highlight(text, nchars_before=nchars_before)
class EditorBase(QtGui.QPlainTextEdit): """ EditorBase Base editor class. """ # Todo fix up # _indentWidth = aforms.config['editor']['tab_size'] # _indentUsingSpaces = aforms.config['editor']['ident_using_spaces'] # _showSpaces = aforms.config['editor']['show_whitespace'] _indentWidth = 4 _indentUsingSpaces = True _showSpaces = True def __init__(self, *args, **kwargs): super(EditorBase, self).__init__(*args, **kwargs) # Make sure editor always has a monospace font. self.__zoom = 0 self.setFont() self.__highlighter = Highlighter(self.document()) self.__highlighter.setRules(PythonHighlighter) # Set general document option option = self.document().defaultTextOption() option.setFlags(option.flags() | option.IncludeTrailingSpaces | option.AddSpaceForLineAndParagraphSeparators) if self.indentUsingSpaces(): option.setFlags(option.flags() | option.ShowTabsAndSpaces) self.document().setDefaultTextOption(option) # View Settings # self.setShowWhitespace() # self.setShowLineEndings() # self.setWrap() # self.setHighlightCurrentLine() # self.setLongLineIndicatorPosition() # So that graphical elements wont break. self.cursorPositionChanged.connect(self.viewport().update) def setZoom(self, zoom): size = aforms.config['editor']['font_size'] self.__zoom = int(max(1-size, zoom)) self.setFont(self.fontInfo().family()) return self.__zoom def setFont(self, font=None): """ Set the font for the editor. """ defaultFont = aforms.font.defaultFont() if font is None: font = defaultFont try: font = QtGui.QFont(font) except ValueError: font = defaultFont print('setFont accepts None, QFont or a string') font.setStyleHint(font.TypeWriter, font.PreferDefault) fontInfo = QtGui.QFontInfo(font) family = fontInfo.family() if fontInfo.fixedPitch() else default.family() size = defaultFont.pointSize() + self.__zoom font = QtGui.QFont(family, size) super(EditorBase, self).setFont(font) return font def indentUsingSpaces(self): return self._indentUsingSpaces def indentWidth(self): return self._indentWidth def setIndentWidth(self, value): value = int(value) if value <= 0: raise ValueError('indentWidth must be greater than 0.') self._indentWidth = value self.setTabStopWidth(self.fontMetric().widtrh('i'*self._indentWidth)) def indentBlock(self, cursor, amount=1): pass
def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.init_UI() # 界面绘制交给InitUi方法 self.file_status = FileStatus() self.highlighter = Highlighter(self.ui)
class TextEditor(QMainWindow, Ui_MainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.init_UI() # 界面绘制交给InitUi方法 self.file_status = FileStatus() self.highlighter = Highlighter(self.ui) def init_UI(self): self.ui.setupUi(self) self.ui.textEdit.setFontPointSize(12) self.ui.textEdit.setStyleSheet("background-color:#F5F5F5") self.ui.textBrowser.setStyleSheet("background-color:#F5F5F5") self.setWindowTitle('未命名') self.show() @pyqtSlot() def on_textEdit_textChanged(self): if self.highlighter.flag_format_changed: self.highlighter.flag_format_changed = False return self.file_status.set_TextChanged() # set windows name if not (self.file_status.is_saved or self.file_status.is_saveFlag_setted): self.setWindowTitle(self.file_status.filename + '*') self.file_status.is_saveFlag_setted = True # set font if self.file_status.is_openFile: self.highlighter.mode_openfile() self.file_status.is_openFile = False else: self.highlighter.mode_normal() @pyqtSlot() # 打开文件 def on_act_open_triggered(self): filename = QFileDialog.getOpenFileName( self, caption='Open File', directory='./', filter="c files(*.c);; cpp files(*.cpp)") if filename[0] == '': return with open(filename[0], 'r', encoding='UTF-8') as f: content = '' for li in f.readlines(): content += li # file_status self.file_status.set_openStatus(filename[0]) # windows title self.setWindowTitle(self.file_status.filename) # set_text self.ui.textEdit.setText(content) @pyqtSlot() # 另存为 def on_act_saveAs_triggered(self): filename = QFileDialog.getSaveFileName( self, caption='Open File', directory='./', filter="c files(*.c);; cpp files(*.cpp)") if filename[0] == '': return with open(filename[0], 'w', encoding='UTF-8') as f: f.write(self.ui.textEdit.document().toPlainText()) self.setWindowTitle(self.file_status.filename) self.file_status.set_saveStatus() @pyqtSlot() # 保存文件 def on_act_save_triggered(self): if self.file_status.is_untitle: self.on_act_saveAs_triggered() else: with open(self.file_status.filedir, 'w', encoding='UTF-8') as f: f.write(self.ui.textEdit.document().toPlainText()) # windows title self.setWindowTitle(self.file_status.filename) self.file_status.set_saveStatus() @pyqtSlot() # 新建 def on_act_new_triggered(self): self.ui.textEdit.setText('') self.setWindowTitle('未命名') self.file_status.__init__() @pyqtSlot() # 剪切 def on_act_undo_triggered(self): self.ui.textEdit.undo() @pyqtSlot() # 剪切 def on_act_cut_triggered(self): self.ui.textEdit.cut() @pyqtSlot() # 复制 def on_act_copy_triggered(self): self.ui.textEdit.copy() @pyqtSlot() # 粘贴 def on_act_paste_triggered(self): self.ui.textEdit.paste()
class NoteEditor(QPlainTextEdit): """The note editing class used uses a plain text editing window to edit markdown files (notes).""" def __init__(self, parent): super(NoteEditor, self).__init__(parent) self.highlighter = Highlighter( self.document() ) # the language dictionary to be used self.dictionary = None self.highlighter.setDictionary(self.dictionary) # create dictionary selector menu self.dictionarySelector = QMenu("&Dictionary") self.dictionaryActions = QActionGroup(self) self.noDicitionaryAction = self.dictionaryActions.addAction("None") self.noDicitionaryAction.setCheckable(True) self.dictionarySelector.addAction(self.noDicitionaryAction) self.defaultDicitionaryAction = self.dictionaryActions.addAction("Default") self.defaultDicitionaryAction.setCheckable(True) self.dictionarySelector.addAction(self.defaultDicitionaryAction) self.dictionarySelector.addSeparator() for lang in enchant.list_languages(): langAction = self.dictionaryActions.addAction(lang) langAction.setCheckable(True) self.dictionarySelector.addAction(langAction) # connect signal to change language dictionary and set the default language self.dictionaryActions.triggered.connect(self.changeDictionary) self.defaultDicitionaryAction.trigger() # configure the custom context menu for spelling suggestions self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showContextMenu) # settings self.noteFilePath = "" #stores path to the file being edited self.setFont(QFont("Monospace")) tabWidth = QFontMetrics( self.currentCharFormat().font() ).width(" ")#get the width of four spaces self.setTabStopWidth(tabWidth) #set the default tab width to be that of four spaces #%%% To do: use 'keyPressEvent' to implement auto-indent %%% def setWrapMode(self, wrapMode): self.setWordWrapMode(wrapMode) def getDictionarySelector(self): return self.dictionarySelector def changeDictionary(self, action): """Change the spell check dictionary (language) based on the languaged selected from the 'Dictionary' menu.""" # change the language dictionary if action is self.noDicitionaryAction: self.dictionary = None elif action is self.defaultDicitionaryAction: self.dictionary = enchant.Dict(locale.getdefaultlocale()[0]) else: self.dictionary = enchant.Dict(action.text()) # rehighlight the text to find spelling errors self.highlighter.setDictionary(self.dictionary) self.highlighter.rehighlight() def showContextMenu(self, position): """Shows an appropriate context menu for the area of the editor that was right-clicked.""" # create the context menu that will be displayed contextMenu = QMenu() # select the word that was right-clicked textCursor = self.cursorForPosition(position) textCursor.select(QTextCursor.WordUnderCursor) word = textCursor.selectedText() # if a word was selected and it's misspelled, show a suggestion menu if word and not len(word) is 0: if not self.dictionary.check(word): # create the suggestion menu for suggestion in self.dictionary.suggest(word): a = contextMenu.addAction(suggestion) a.setData("custom") contextMenu.addSeparator() a = contextMenu.addAction("Add to dictionary") a.setData("custom") else: a = contextMenu.addAction("Remove from dictionary") a.setData("custom") contextMenu.addSeparator() # add standard context menu actions standardMenu = self.createStandardContextMenu(position); isFirst = True for a in standardMenu.children(): if isFirst: isFirst = False else: contextMenu.addAction(a) # show context menu action = contextMenu.exec(self.mapToGlobal(position)) # if a suggestion was selected, replace the current word with it if action and action.data() == "custom": if action.text() == "Add to dictionary": self.dictionary.add(word) elif action.text() == "Remove from dictionary": self.dictionary.remove(word) else: textCursor.removeSelectedText() textCursor.insertText(action.text()) def openFileRequest(self, filePath): """Open a file using its path (string) in the editor. Return 'True' if successful, 'False' otherwise.""" noErrors = True #set return state noteFile = open(filePath, "r") if noteFile: #if the file was opened successfully self.setPlainText( noteFile.read() ) #set its contents in the editor self.noteFilePath = filePath #save the file path to the internal variable self.noteFileChanged.emit( os.path.abspath(self.noteFilePath) ) #emit signal to notify other objects of file change else: #else, return an error noErrors = False noteFile.close() return noErrors def writeToFile(self, filePath): """Writes text in editor to file 'filePath'. Return 'True' if successful, 'False' otherwise.""" noErrors = True if os.path.exists(filePath): #check if file exists filePath = os.path.abspath(filePath) #make path absolute just in case... noteFile = open(filePath, "w") if noteFile: #if file was opened successfully noteFile.write( self.toPlainText() ) #write text to file else: noErrors = False else: noErrors = False return noErrors def saveFileRequest(self): """Saves text in editor to note file. Return 'True' if successful, 'False' otherwise.""" noErrors = True if self.noteFilePath != "": #if note path is set (the editor knows which file it should save to) noErrors = self.writeToFile( self.noteFilePath )#write text to note file else: noErrors = False return noErrors def saveAsRequested(self, filePath): """Save text in editor to a new file ('filePath') and set it as the new note file. Return 'True' if successful, 'False' otherwise.""" noErrors = True newFile = open(filePath, "w+") #create the new file newFile.close() noErrors = self.writeToFile( filePath ) #write text to the new file if noErrors: #if no errors occured self.noteFilePath = filePath #set the new file as the internal note file self.noteFileChanged.emit( os.path.abspath(self.noteFilePath) ) #emit signal to notify other objects of file change return noErrors def saveCopyAsRequested(self, filePath): """Save note to a new file without loading it. Return 'True' if successful, 'False' otherwise.""" noErrors = True newFile = open(filePath, "w+") #create the new file newFile.close() noErrors = self.writeToFile( filePath ) #wrtie text to the new file ######################################################################################## ### Note that 'self.noteFilePath' (path to the note file being edited) is not changed ## ######################################################################################## return noErrors def getNoteFileName(self): """Returns the file name of the note being edited.""" return os.path.basename(self.noteFilePath) def getNotePath(self): """Returns the path to the note being edited.""" return self.noteFilePath #~signals~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ noteFileChanged = pyqtSignal([str]) #a signal to be emitted when 'self.noteFilePath' (path to the note file being edited) is changed
class Main(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) self.doctree=None self.lineMarks={} # We put things we want rendered here self.render_queue = Queue() # We get things rendered back self.pdf_queue = Queue() # We get doctrees for the outline viewer self.doctree_queue = Queue() print('Starting background renderer...', end=' ') self.renderProcess=Process(target = renderQueue, args=(self.render_queue, self.pdf_queue, self.doctree_queue)) self.renderProcess.daemon=True self.renderProcess.start() print('DONE') # This is always the same self.ui=Ui_MainWindow() self.ui.setupUi(self) # Adjust column widths in the structure tree self.ui.tree.header().setStretchLastSection(False) self.ui.tree.header().setResizeMode(0, QtGui.QHeaderView.Stretch) self.ui.tree.header().setResizeMode(1, QtGui.QHeaderView.ResizeToContents) self.pdf=PDFWidget() self.ui.pageNum = QtGui.QSpinBox() self.ui.pageNum.setMinimum(1) self.ui.pageNum.setValue(1) self.connect(self.pdf,QtCore.SIGNAL('pageCount'), self.ui.pageNum.setMaximum) self.connect(self.pdf,QtCore.SIGNAL('pageChanged'), self.ui.pageNum.setValue) self.connect(self.ui.pageNum,QtCore.SIGNAL('valueChanged(int)'), self.pdf.gotoPage) self.ui.actionShow_ToolBar=self.ui.toolBar.toggleViewAction() self.ui.actionShow_ToolBar.setText("Show Main Toolbar") self.ui.menuView.addAction(self.ui.actionShow_ToolBar) self.ui.pdfbar.addAction(self.pdf.ui.previous) self.ui.pdfbar.addWidget(self.ui.pageNum) self.ui.pdfbar.addAction(self.pdf.ui.__next__) self.ui.pdfbar.addSeparator() self.ui.pdfbar.addAction(self.pdf.ui.zoomin) self.ui.pdfbar.addAction(self.pdf.ui.zoomout) self.ui.actionShow_PDFBar=self.ui.pdfbar.toggleViewAction() self.ui.actionShow_PDFBar.setText("Show PDF Toolbar") self.ui.menuView.addAction(self.ui.actionShow_PDFBar) self.ui.dockLayout.addWidget(self.ui.pdfbar) self.ui.dockLayout.addWidget(self.pdf) self.ui.dock.hide() self.ui.actionShow_PDF=self.ui.dock.toggleViewAction() self.ui.actionShow_PDF.setText('Show Preview') self.ui.menuView.addAction(self.ui.actionShow_PDF) self.ui.actionShow_Structure=self.ui.structure.toggleViewAction() self.ui.actionShow_Structure.setText('Show Document Outline') self.ui.menuView.addAction(self.ui.actionShow_Structure) self.text_md5='' self.style_md5='' self.hl1 = Highlighter(self.ui.text.document(),'rest') self.hl2 = Highlighter(self.ui.style.document(),'javascript') self.editorPos=QtGui.QLabel() self.ui.statusBar.addWidget(self.editorPos) self.editorPos.show() self.statusMessage=QtGui.QLabel() self.ui.statusBar.addWidget(self.statusMessage) self.statusMessage.show() self.on_text_cursorPositionChanged() self.on_actionRender_triggered() # Connect editing actions to the editors self.ui.text.undoAvailable.connect(self.ui.actionUndo1.setEnabled) self.ui.actionUndo1.triggered.connect(self.ui.text.undo) self.ui.text.redoAvailable.connect(self.ui.actionRedo1.setEnabled) self.ui.actionRedo1.triggered.connect(self.ui.text.redo) self.ui.text.copyAvailable.connect(self.ui.actionCopy1.setEnabled) self.ui.actionCopy1.triggered.connect(self.ui.text.copy) self.ui.text.copyAvailable.connect(self.ui.actionCut1.setEnabled) self.ui.actionCut1.triggered.connect(self.ui.text.cut) self.ui.actionPaste1.triggered.connect(self.ui.text.paste) self.ui.style.undoAvailable.connect(self.ui.actionUndo2.setEnabled) self.ui.actionUndo2.triggered.connect(self.ui.style.undo) self.ui.style.redoAvailable.connect(self.ui.actionRedo2.setEnabled) self.ui.actionRedo2.triggered.connect(self.ui.style.redo) self.ui.style.copyAvailable.connect(self.ui.actionCopy2.setEnabled) self.ui.actionCopy2.triggered.connect(self.ui.style.copy) self.ui.style.copyAvailable.connect(self.ui.actionCut2.setEnabled) self.ui.actionCut2.triggered.connect(self.ui.style.cut) self.ui.actionPaste2.triggered.connect(self.ui.style.paste) self.clipBoard=QtGui.QApplication.clipboard() self.clipBoard.changed.connect(self.clipChanged) self.hookEditToolbar(self.ui.text) self.clipChanged(QtGui.QClipboard.Clipboard) self.text_fname=None self.style_fname=None self.pdf_fname=None self.ui.searchbar.setVisible(False) self.ui.searchWidget=SearchWidget() self.ui.searchbar.addWidget(self.ui.searchWidget) self.ui.actionFind.triggered.connect(self.ui.searchbar.show) self.ui.actionFind.triggered.connect(self.ui.searchWidget.ui.text.setFocus) self.ui.searchWidget.ui.close.clicked.connect(self.ui.searchbar.hide) self.ui.searchWidget.ui.close.clicked.connect(self.returnFocus) self.ui.searchWidget.ui.next.clicked.connect(self.doFind) self.ui.searchWidget.ui.previous.clicked.connect(self.doFindBackwards) self.updatePdf() self.renderTimer=QtCore.QTimer() self.renderTimer.timeout.connect(self.on_actionRender_triggered) self.renderTimer.start(5000) def returnFocus(self): """after the search bar closes, focus on the editing widget""" print('RF:', self.ui.tabs.currentIndex()) if self.ui.tabs.currentIndex()==0: self.ui.text.setFocus() else: self.ui.style.setFocus() def doFindBackwards (self): return self.doFind(backwards=True) def doFind(self, backwards=False): flags=QtGui.QTextDocument.FindFlags() print(flags) if backwards: flags=QtGui.QTextDocument.FindBackward if self.ui.searchWidget.ui.matchCase.isChecked(): flags=flags|QtGui.QTextDocument.FindCaseSensitively text=str(self.ui.searchWidget.ui.text.text()) print('Serching for:',text) if self.ui.tabs.currentIndex()==0: r=self.ui.text.find(text,flags) else: r=self.ui.style.find(text,flags) if r: self.statusMessage.setText('') else: self.statusMessage.setText('%s not found'%text) def clipChanged(self, mode=None): if mode is None: return if mode == QtGui.QClipboard.Clipboard: if str(self.clipBoard.text()): self.ui.actionPaste1.setEnabled(True) self.ui.actionPaste2.setEnabled(True) else: self.ui.actionPaste1.setEnabled(False) self.ui.actionPaste2.setEnabled(False) def hookEditToolbar(self, editor): if editor == self.ui.text: self.ui.actionUndo2.setVisible(False) self.ui.actionRedo2.setVisible(False) self.ui.actionCut2.setVisible(False) self.ui.actionPaste2.setVisible(False) self.ui.actionCopy2.setVisible(False) self.ui.actionUndo1.setVisible(True) self.ui.actionRedo1.setVisible(True) self.ui.actionCut1.setVisible(True) self.ui.actionPaste1.setVisible(True) self.ui.actionCopy1.setVisible(True) else: self.ui.actionUndo1.setVisible(False) self.ui.actionRedo1.setVisible(False) self.ui.actionCut1.setVisible(False) self.ui.actionPaste1.setVisible(False) self.ui.actionCopy1.setVisible(False) self.ui.actionUndo2.setVisible(True) self.ui.actionRedo2.setVisible(True) self.ui.actionCut2.setVisible(True) self.ui.actionPaste2.setVisible(True) self.ui.actionCopy2.setVisible(True) def createPopupMenu(self): self.popup=QtGui.QMenu() self.popup.addAction(self.ui.actionShow_ToolBar) self.popup.addAction(self.ui.actionShow_PDFBar) self.popup.addAction(self.ui.actionShow_PDF) return self.popup def enableHL(self): self.hl1.enabled=True self.hl2.enabled=True self.hl1.rehighlight() self.hl2.rehighlight() def disableHL(self): self.hl1.enabled=False self.hl2.enabled=False def on_actionSettings_triggered(self, b=None): if b is not None: return # I need to create a stylesheet object so I can parse and merge # the current stylesheet try: data=json.loads(str(self.ui.style.toPlainText())) except: # TODO: fail if sheet doesn't validate data={} config=ConfigDialog(data=copy(data)) config.exec_() # merge the edited stylesheet with current one because the editor # is not complete yet. When it is, just replace it. data.update(config.data) self.ui.style.setPlainText(json.dumps(data, indent=2)) def on_actionTest_Action_triggered(self, b=None): if b is not None: return self.testwidget=PageTemplates(self.styles) self.testwidget.show() def on_tree_itemClicked(self, item=None, column=None): if item is None: return destline=int(item.text(1))-1 destblock=self.ui.text.document().findBlockByLineNumber(destline) cursor=self.ui.text.textCursor() cursor.setPosition(destblock.position()) self.ui.text.setTextCursor(cursor) self.ui.text.ensureCursorVisible() def on_actionAbout_Bookrest_triggered(self, b=None): if b is None: return dlg=AboutDialog() dlg.exec_() def on_actionSave_Text_triggered(self, b=None): if b is not None: return if self.text_fname is not None: f=codecs.open(self.text_fname,'w+','utf-8') f.seek(0) f.write(str(self.ui.text.toPlainText())) f.close() else: self.on_actionSaveAs_Text_triggered() def on_actionSaveAs_Text_triggered(self, b=None): if b is not None: return fname=str(QtGui.QFileDialog.getSaveFileName(self, 'Save As', os.getcwd(), 'reSt files (*.txt *.rst)' )) if fname: self.text_fname=fname self.on_actionSave_Text_triggered() def on_actionLoad_Text_triggered(self, b=None): if b is None: return fname=QtGui.QFileDialog.getOpenFileName(self, 'Open File', os.getcwd(), 'reSt files (*.txt *.rst)' ) self.text_fname=fname self.disableHL() self.ui.text.setPlainText(codecs.open(self.text_fname,'r','utf-8').read()) self.enableHL() def on_actionSave_Style_triggered(self, b=None): if b is not None: return if self.style_fname is not None: f=codecs.open(self.style_fname,'w+','utf-8') f.seek(0) f.write(str(self.ui.style.toPlainText())) f.close() else: self.on_actionSaveAs_Style_triggered() def on_actionSaveAs_Style_triggered(self, b=None): if b is not None: return fname=str(QtGui.QFileDialog.getSaveFileName(self, 'Save As', os.getcwd(), 'style files (*.json *.style)' )) if fname: self.style_fname=fname self.on_actionSave_Style_triggered() def on_actionLoad_Style_triggered(self, b=None): if b is None: return fname=QtGui.QFileDialog.getOpenFileName(self, 'Open File', os.getcwd(), 'style files (*.json *.style)' ) self.style_fname=fname self.disableHL() self.ui.style.setPlainText(codecs.open(self.style_fname,'rb', 'utf-8').read()) self.enableHL() def on_actionSave_PDF_triggered(self, b=None): if b is not None: return # render it without line numbers in the toc self.on_actionRender_triggered(preview=False) if self.pdf_fname is not None: f=open(self.pdf_fname,'wb+') f.seek(0) f.write(self.goodPDF) f.close() else: self.on_actionSaveAs_PDF_triggered() def on_actionSaveAs_PDF_triggered(self, b=None): if b is not None: return fname=str(QtGui.QFileDialog.getSaveFileName(self, 'Save As', os.getcwd(), 'PDF files (*.pdf)' )) if fname: self.pdf_fname=fname self.on_actionSave_PDF_triggered() def on_tabs_currentChanged(self, i=None): print('IDX:',self.ui.tabs.currentIndex()) if self.ui.tabs.currentIndex() == 0: self.on_text_cursorPositionChanged() print('hooking text editor') self.hookEditToolbar(self.ui.text) else: self.on_style_cursorPositionChanged() print('hooking style editor') self.hookEditToolbar(self.ui.style) def on_style_cursorPositionChanged(self): cursor=self.ui.style.textCursor() self.editorPos.setText('Line: %d Col: %d'%(cursor.blockNumber(),cursor.columnNumber())) def on_text_cursorPositionChanged(self): cursor=self.ui.text.textCursor() row=cursor.blockNumber() column=cursor.columnNumber() self.editorPos.setText('Line: %d Col: %d'%(row,column)) l='line-%s'%(row+1) m=self.lineMarks.get(l,None) if m: self.pdf.gotoPosition(*m) def validateStyle(self): style=str(self.ui.style.toPlainText()) if not style.strip(): #no point in validating an empty string self.statusMessage.setText('') return pos=None try: json.loads(style) self.statusMessage.setText('') except ValueError as e: s=str(e) if s == 'No JSON object could be decoded': pos=0 elif s.startswith('Expecting '): pos=int(s.split(' ')[-1][:-1]) elif s.startswith('Extra data'): pos=int(s.split(' ')[-3]) else: pass self.statusMessage.setText('Stylesheet error: %s'%s) # This makes a red bar appear in the line # containing position pos self.ui.style.highlightError(pos) on_style_textChanged = validateStyle def on_actionRender_triggered(self, b=None, preview=True): if b is not None: return text=str(self.ui.text.toPlainText()) style=str(self.ui.style.toPlainText()) self.hl1.rehighlight() m1=md5() m1.update(text.encode('utf-8')) m1=m1.digest() m2=md5() m2.update(style.encode('utf-8')) m2=m2.digest() flag = m1 != self.text_md5 style_file=None if m2 != self.style_md5 and style: fd, style_file=tempfile.mkstemp() os.write(fd,style) os.close(fd) print('Loading styles from style_file') flag = True if flag: if not preview: pass # Send text to the renderer in foreground # FIXME: render is no longer accessible from the parent # process #doctree = docutils.core.publish_doctree(text) #self.goodPDF=render(doctree, preview=False) else: # Que to render in background self.render_queue.put([style_file, text, preview]) self.text_md5=m1 self.style_md5=m2 def updatePdf(self): # See if there is something in the doctree Queue try: self.doctree, self.warnings = self.doctree_queue.get(False) self.doctree.reporter=log class Visitor(docutils.nodes.SparseNodeVisitor): def __init__(self, document, treeWidget): self.treeWidget=treeWidget self.treeWidget.clear() self.doctree=document self.nodeDict={} docutils.nodes.SparseNodeVisitor.__init__(self, document) def visit_section(self, node): print('SECTION:',node.line, end=' ') item=QtGui.QTreeWidgetItem(["",str(node.line)]) if node.parent==self.doctree: # Top level section self.treeWidget.addTopLevelItem(item) self.nodeDict[id(node)]=item else: self.nodeDict[id(node.parent)].addChild(item) self.nodeDict[id(node)]=item def visit_title(self, node): if id(node.parent) in self.nodeDict: self.nodeDict[id(node.parent)].setText(0,node.astext()) def visit_document(self,node): print('DOC:',node.line) print(self.doctree.__class__) self.visitor=Visitor(self.doctree, self.ui.tree) self.doctree.walkabout(self.visitor) print(self.visitor.nodeDict) except Empty: pass # See if there is something in the PDF Queue try: self.lastPDF=self.pdf_queue.get(False) self.pdf.loadDocument(self.lastPDF) toc=self.pdf.document.toc() if toc: tempMarks=[] def traverse(node): children=node.childNodes() for i in range(children.length()): n=children.item(i) e=n.toElement() if e: tag=str(e.tagName()) if tag.startswith('LINE'): dest=str(e.attribute('Destination')) dest=QtPoppler.Poppler.LinkDestination(dest) tempMarks.append([int(tag.split('-')[1]), [dest.pageNumber(), dest.top(), dest.left(),1.]]) traverse(n) traverse(toc) tempMarks.sort() self.lineMarks={} lastMark=None lastKey=0 for key,dest in tempMarks: # Fix height of the previous mark, unless we changed pages if lastMark and self.lineMarks[lastMark][0]==dest[0]: self.lineMarks[lastMark][3]=dest[1] # Fill missing lines if lastMark: ldest=self.lineMarks[lastMark] else: ldest=[1,0,0,0] for n in range(lastKey,key): self.lineMarks['line-%s'%n]=ldest k='line-%s'%key self.lineMarks[k]=dest lastMark = k lastKey = key self.on_text_cursorPositionChanged() except Empty: #Nothing there pass # Schedule to run again QtCore.QTimer.singleShot(500,self.updatePdf)
def __init__(self): super().__init__() self.title('Python Text Editor v3') self.geometry('800x600') self.foreground = 'black' self.background = 'lightgrey' self.text_foreground = '#313131' self.text_background = '#f0f0f0' self.terminal_background = 'darkcyan' self.terminal_foreground = 'white' self.config_dir = os.path.join(str(Path.home()), '.tkedit') self.default_scheme_path = os.path.join(self.config_dir, 'schemes/default.yaml') self.python_language_path = os.path.join(self.config_dir, 'languages/python.yaml') self.font_scheme_path = os.path.join(self.config_dir, 'schemes/font.yaml') self.create_config_directory_if_needed() self.load_scheme_file(self.default_scheme_path) self.configure_ttk_elements() # Change self.font_size = 14 self.font_family = "Droid Sana Mono" self.terminal_font_size = 12 self.terminal_font_family = "Consolas" # Change self.load_font_file(self.font_scheme_path) self.text_area = TextArea(self, bg=self.text_background, fg=self.text_foreground, undo=True, font=(self.font_family, self.font_size)) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.scroll_text) self.text_area.configure(yscrollcommand=self.scrollbar.set) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1, font=(self.font_family, self.font_size)) self.highlighter = Highlighter(self.text_area, self.python_language_path) self.terminal = Terminal(self, height=8, bg=self.terminal_background, fg=self.terminal_foreground, undo=True, font=(self.terminal_font_family, self.terminal_font_size)) self.menu = tk.Menu(self, bg=self.background, fg=self.foreground) self.all_menus = [self.menu] self.text_area.__setattr__("update_line", self.line_numbers.force_update) self.text_area.__setattr__("update_highlight", self.highlighter.force_highlight) sub_menu_items = ["file", "edit", "tools", "help"] self.generate_sub_menus(sub_menu_items) self.configure(menu=self.menu) self.right_click_menu = tk.Menu(self, bg=self.background, fg=self.foreground, tearoff=0) self.right_click_menu.add_command(label='Cut', command=self.edit_cut) self.right_click_menu.add_command(label='Copy', command=self.edit_copy) self.right_click_menu.add_command(label='Paste', command=self.edit_paste) # Change self.right_click_menu.add_command(label='Undo', command=self.edit_undo) self.right_click_menu.add_command(label='Redo', command=self.edit_redo) # Change self.all_menus.append(self.right_click_menu) # change self.count_area = tk.Text(self, height=1) self.count_area.pack(side=tk.BOTTOM, fill=tk.X) # change # Change self.terminal.pack(side=tk.BOTTOM, fill=tk.X) # Change self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events() self.open_file = ''
class MainWindow(tk.Tk): def __init__(self): super().__init__() self.title('Python Text Editor v3') self.geometry('800x600') self.foreground = 'black' self.background = 'lightgrey' self.text_foreground = '#313131' self.text_background = '#f0f0f0' self.terminal_background = 'darkcyan' self.terminal_foreground = 'white' self.config_dir = os.path.join(str(Path.home()), '.tkedit') self.default_scheme_path = os.path.join(self.config_dir, 'schemes/default.yaml') self.python_language_path = os.path.join(self.config_dir, 'languages/python.yaml') self.font_scheme_path = os.path.join(self.config_dir, 'schemes/font.yaml') self.create_config_directory_if_needed() self.load_scheme_file(self.default_scheme_path) self.configure_ttk_elements() # Change self.font_size = 14 self.font_family = "Droid Sana Mono" self.terminal_font_size = 12 self.terminal_font_family = "Consolas" # Change self.load_font_file(self.font_scheme_path) self.text_area = TextArea(self, bg=self.text_background, fg=self.text_foreground, undo=True, font=(self.font_family, self.font_size)) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.scroll_text) self.text_area.configure(yscrollcommand=self.scrollbar.set) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1, font=(self.font_family, self.font_size)) self.highlighter = Highlighter(self.text_area, self.python_language_path) self.terminal = Terminal(self, height=8, bg=self.terminal_background, fg=self.terminal_foreground, undo=True, font=(self.terminal_font_family, self.terminal_font_size)) self.menu = tk.Menu(self, bg=self.background, fg=self.foreground) self.all_menus = [self.menu] self.text_area.__setattr__("update_line", self.line_numbers.force_update) self.text_area.__setattr__("update_highlight", self.highlighter.force_highlight) sub_menu_items = ["file", "edit", "tools", "help"] self.generate_sub_menus(sub_menu_items) self.configure(menu=self.menu) self.right_click_menu = tk.Menu(self, bg=self.background, fg=self.foreground, tearoff=0) self.right_click_menu.add_command(label='Cut', command=self.edit_cut) self.right_click_menu.add_command(label='Copy', command=self.edit_copy) self.right_click_menu.add_command(label='Paste', command=self.edit_paste) # Change self.right_click_menu.add_command(label='Undo', command=self.edit_undo) self.right_click_menu.add_command(label='Redo', command=self.edit_redo) # Change self.all_menus.append(self.right_click_menu) # change self.count_area = tk.Text(self, height=1) self.count_area.pack(side=tk.BOTTOM, fill=tk.X) # change # Change self.terminal.pack(side=tk.BOTTOM, fill=tk.X) # Change self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events() self.open_file = '' def bind_events(self): self.text_area.bind("<MouseWheel>", self.scroll_text) self.text_area.bind("<Button-4>", self.scroll_text) self.text_area.bind("<Button-5>", self.scroll_text) self.text_area.bind("<Button-3>", self.show_right_click_menu) # Change self.count_area.bind(self.count_text()) # Change self.bind('<Control-f>', self.show_find_window) self.bind('<Control-n>', self.file_new) self.bind('<Control-o>', self.file_open) self.bind('<Control-s>', self.file_save) self.bind('<Control-h>', self.help_about) self.bind('<Control-m>', self.tools_change_syntax_highlighting) self.bind('<Control-g>', self.tools_change_colour_scheme) self.bind('<Control-l>', self.tools_change_font) self.line_numbers.bind("<MouseWheel>", lambda e: "break") self.line_numbers.bind("<Button-4>", lambda e: "break") self.line_numbers.bind("<Button-5>", lambda e: "break") # change def count_text(self): contents = self.text_area.get(1.0, tk.END).replace(" ", "").split("\n") self.count_area.delete(1.0, tk.END) self.count_area.insert( tk.END, "当前字数为:" + str(CountWord(contents).bind_event(contents)) + " 字") return self.after(50, self.count_text) # change def scroll_text(self, *args): if len(args) > 1: self.text_area.yview_moveto(args[1]) self.line_numbers.yview_moveto(args[1]) else: event = args[0] if event.delta: move = -1 * (event.delta / 120) else: if event.num == 5: move = 1 else: move = -1 self.text_area.yview_scroll(int(move), "units") self.line_numbers.yview_scroll(int(move) * 3, "units") def show_find_window(self, event=None): FindWindow(self.text_area) def show_right_click_menu(self, event): x = self.winfo_x() + self.text_area.winfo_x() + event.x y = self.winfo_y() + self.text_area.winfo_y() + event.y self.right_click_menu.post(x, y) def generate_sub_menus(self, sub_menu_items): window_methods = [ method_name for method_name in dir(self) if callable(getattr(self, method_name)) ] tkinter_methods = [ method_name for method_name in dir(tk.Tk) if callable(getattr(tk.Tk, method_name)) ] my_methods = [ method for method in set(window_methods) - set(tkinter_methods) ] my_methods = sorted(my_methods) for item in sub_menu_items: sub_menu = tk.Menu(self.menu, tearoff=0, bg=self.background, fg=self.foreground) matching_methods = [] for method in my_methods: if method.startswith(item): matching_methods.append(method) for match in matching_methods: actual_method = getattr(self, match) method_shortcut = actual_method.__doc__.strip() friendly_name = ' '.join(match.split('_')[1:]) sub_menu.add_command(label=friendly_name.title(), command=actual_method, accelerator=method_shortcut) self.menu.add_cascade(label=item.title(), menu=sub_menu) self.all_menus.append(sub_menu) def show_about_page(self): msg.showinfo( "About", "My text editor, version 2, written in Python3.6 using tkinter!") def load_syntax_highlighting_file(self): syntax_file = filedialog.askopenfilename(filetypes=[("YAML file", ("*.yaml", "*.yml"))]) if syntax_file: self.highlighter.clear_highlight() self.highlighter = Highlighter(self.text_area, syntax_file) self.highlighter.force_highlight() def load_scheme_file(self, scheme): with open(scheme, 'r') as stream: try: config = yaml.load(stream) except yaml.YAMLError as error: print(error) return self.foreground = config['foreground'] self.background = config['background'] self.text_foreground = config['text_foreground'] self.text_background = config['text_background'] def load_font_file(self, file_path): with open(file_path, 'r') as stream: try: config = yaml.load(stream) except yaml.YAMLError as error: print(error) return self.font_family = config['family'] self.font_size = config['size'] def change_colour_scheme(self): ColourChooser(self) def apply_colour_scheme(self, foreground, background, text_foreground, text_background): self.text_area.configure(fg=text_foreground, bg=text_background) self.background = background self.foreground = foreground for menu in self.all_menus: menu.configure(bg=self.background, fg=self.foreground) self.configure_ttk_elements() def configure_ttk_elements(self): style = ttk.Style() style.configure('editor.TLabel', foreground=self.foreground, background=self.background) style.configure('editor.TButton', foreground=self.foreground, background=self.background) def change_font(self): FontChooser(self) def update_font(self): self.load_font_file(self.font_scheme_path) self.text_area.configure(font=(self.font_family, self.font_size)) def create_config_directory_if_needed(self): if not os.path.exists(self.config_dir): os.mkdir(self.config_dir) os.mkdir(os.path.join(self.config_dir, 'schemes')) os.mkdir(os.path.join(self.config_dir, 'languages')) self.create_default_scheme_if_needed() self.create_font_scheme_if_needed() self.create_python_language_if_needed() def create_default_scheme_if_needed(self): if not os.path.exists(self.default_scheme_path): yaml_file_contents = f"background: 'lightgrey'\n" \ + f"foreground: 'black'\n" \ + f"text_background: 'white'\n" \ + f"text_foreground: 'black'\n" with open(self.default_scheme_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) def create_font_scheme_if_needed(self): if not os.path.exists(self.font_scheme_path): yaml_file_contents = f"family: Ubuntu Mono\n" \ + f"size: 14" with open(self.font_scheme_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) def create_python_language_if_needed(self): if not os.path.exists(self.python_language_path): yaml_file_contents = """ categories: keywords: colour: orange matches: [for, def, while, from, import, as, with, self] variables: colour: red4 matches: ['True', 'False', None] conditionals: colour: green matches: [try, except, if, else, elif] functions: colour: blue4 matches: [int, str, dict, list, set, float] numbers: colour: purple strings: colour: '#e1218b' """ with open(self.python_language_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) # =========== Menu Functions ============== def file_new(self, event=None): """ Ctrl+N """ self.text_area.delete(1.0, tk.END) self.open_file = None self.line_numbers.force_update() def file_open(self, event=None): """ Ctrl+O """ file_to_open = filedialog.askopenfilename() if file_to_open: self.open_file = file_to_open self.text_area.display_file_contents(file_to_open) self.highlighter.force_highlight() self.line_numbers.force_update() def file_save(self, event=None): """ Ctrl+S """ current_file = self.open_file if self.open_file else None if not current_file: current_file = filedialog.asksaveasfilename() if current_file: contents = self.text_area.get(1.0, tk.END) with open(current_file, 'w') as file: file.write(contents) def edit_cut(self, event=None): """ Ctrl+X """ self.text_area.event_generate("<Control-x>") self.line_numbers.force_update() self.highlighter.force_highlight() def edit_paste(self, event=None): """ Ctrl+V """ self.text_area.event_generate("<Control-v>") self.line_numbers.force_update() self.highlighter.force_highlight() def edit_copy(self, event=None): """ Ctrl+C """ self.text_area.event_generate("<Control-c>") def edit_select_all(self, event=None): """ Ctrl+A """ self.text_area.event_generate("<Control-a>") def edit_find_and_replace(self, event=None): """ Ctrl+F """ self.show_find_window() def edit_undo(self, event=None): """ Ctrl+Z """ self.text_area.event_generate("<Control-z>") self.line_numbers.force_update() self.highlighter.force_highlight() def edit_redo(self, event=None): """ Ctrl+Y """ self.text_area.event_generate("<Control-y>") self.line_numbers.force_update() self.highlighter.force_highlight() def help_about(self, event=None): """ Ctrl+H """ self.show_about_page() def tools_change_syntax_highlighting(self, event=None): """ Ctrl+M """ self.load_syntax_highlighting_file() def tools_change_colour_scheme(self, event=None): """ Ctrl+G """ self.change_colour_scheme() def tools_change_font(self, event=None): """ Ctrl+L """ self.change_font()
def __init__(self): QtGui.QMainWindow.__init__(self) self.doctree=None self.lineMarks={} # We put things we want rendered here self.render_queue = Queue() # We get things rendered back self.pdf_queue = Queue() # We get doctrees for the outline viewer self.doctree_queue = Queue() print('Starting background renderer...', end=' ') self.renderProcess=Process(target = renderQueue, args=(self.render_queue, self.pdf_queue, self.doctree_queue)) self.renderProcess.daemon=True self.renderProcess.start() print('DONE') # This is always the same self.ui=Ui_MainWindow() self.ui.setupUi(self) # Adjust column widths in the structure tree self.ui.tree.header().setStretchLastSection(False) self.ui.tree.header().setResizeMode(0, QtGui.QHeaderView.Stretch) self.ui.tree.header().setResizeMode(1, QtGui.QHeaderView.ResizeToContents) self.pdf=PDFWidget() self.ui.pageNum = QtGui.QSpinBox() self.ui.pageNum.setMinimum(1) self.ui.pageNum.setValue(1) self.connect(self.pdf,QtCore.SIGNAL('pageCount'), self.ui.pageNum.setMaximum) self.connect(self.pdf,QtCore.SIGNAL('pageChanged'), self.ui.pageNum.setValue) self.connect(self.ui.pageNum,QtCore.SIGNAL('valueChanged(int)'), self.pdf.gotoPage) self.ui.actionShow_ToolBar=self.ui.toolBar.toggleViewAction() self.ui.actionShow_ToolBar.setText("Show Main Toolbar") self.ui.menuView.addAction(self.ui.actionShow_ToolBar) self.ui.pdfbar.addAction(self.pdf.ui.previous) self.ui.pdfbar.addWidget(self.ui.pageNum) self.ui.pdfbar.addAction(self.pdf.ui.__next__) self.ui.pdfbar.addSeparator() self.ui.pdfbar.addAction(self.pdf.ui.zoomin) self.ui.pdfbar.addAction(self.pdf.ui.zoomout) self.ui.actionShow_PDFBar=self.ui.pdfbar.toggleViewAction() self.ui.actionShow_PDFBar.setText("Show PDF Toolbar") self.ui.menuView.addAction(self.ui.actionShow_PDFBar) self.ui.dockLayout.addWidget(self.ui.pdfbar) self.ui.dockLayout.addWidget(self.pdf) self.ui.dock.hide() self.ui.actionShow_PDF=self.ui.dock.toggleViewAction() self.ui.actionShow_PDF.setText('Show Preview') self.ui.menuView.addAction(self.ui.actionShow_PDF) self.ui.actionShow_Structure=self.ui.structure.toggleViewAction() self.ui.actionShow_Structure.setText('Show Document Outline') self.ui.menuView.addAction(self.ui.actionShow_Structure) self.text_md5='' self.style_md5='' self.hl1 = Highlighter(self.ui.text.document(),'rest') self.hl2 = Highlighter(self.ui.style.document(),'javascript') self.editorPos=QtGui.QLabel() self.ui.statusBar.addWidget(self.editorPos) self.editorPos.show() self.statusMessage=QtGui.QLabel() self.ui.statusBar.addWidget(self.statusMessage) self.statusMessage.show() self.on_text_cursorPositionChanged() self.on_actionRender_triggered() # Connect editing actions to the editors self.ui.text.undoAvailable.connect(self.ui.actionUndo1.setEnabled) self.ui.actionUndo1.triggered.connect(self.ui.text.undo) self.ui.text.redoAvailable.connect(self.ui.actionRedo1.setEnabled) self.ui.actionRedo1.triggered.connect(self.ui.text.redo) self.ui.text.copyAvailable.connect(self.ui.actionCopy1.setEnabled) self.ui.actionCopy1.triggered.connect(self.ui.text.copy) self.ui.text.copyAvailable.connect(self.ui.actionCut1.setEnabled) self.ui.actionCut1.triggered.connect(self.ui.text.cut) self.ui.actionPaste1.triggered.connect(self.ui.text.paste) self.ui.style.undoAvailable.connect(self.ui.actionUndo2.setEnabled) self.ui.actionUndo2.triggered.connect(self.ui.style.undo) self.ui.style.redoAvailable.connect(self.ui.actionRedo2.setEnabled) self.ui.actionRedo2.triggered.connect(self.ui.style.redo) self.ui.style.copyAvailable.connect(self.ui.actionCopy2.setEnabled) self.ui.actionCopy2.triggered.connect(self.ui.style.copy) self.ui.style.copyAvailable.connect(self.ui.actionCut2.setEnabled) self.ui.actionCut2.triggered.connect(self.ui.style.cut) self.ui.actionPaste2.triggered.connect(self.ui.style.paste) self.clipBoard=QtGui.QApplication.clipboard() self.clipBoard.changed.connect(self.clipChanged) self.hookEditToolbar(self.ui.text) self.clipChanged(QtGui.QClipboard.Clipboard) self.text_fname=None self.style_fname=None self.pdf_fname=None self.ui.searchbar.setVisible(False) self.ui.searchWidget=SearchWidget() self.ui.searchbar.addWidget(self.ui.searchWidget) self.ui.actionFind.triggered.connect(self.ui.searchbar.show) self.ui.actionFind.triggered.connect(self.ui.searchWidget.ui.text.setFocus) self.ui.searchWidget.ui.close.clicked.connect(self.ui.searchbar.hide) self.ui.searchWidget.ui.close.clicked.connect(self.returnFocus) self.ui.searchWidget.ui.next.clicked.connect(self.doFind) self.ui.searchWidget.ui.previous.clicked.connect(self.doFindBackwards) self.updatePdf() self.renderTimer=QtCore.QTimer() self.renderTimer.timeout.connect(self.on_actionRender_triggered) self.renderTimer.start(5000)
class HighlighterTest(unittest.TestCase): def setUp(self): doc = "This is my review on Panchos Mexican Restaurant. This is only a " doc = doc + "test of the emergency broadcasting system. The best " doc = doc + "tacos I've ever had were from Panchos. This sentence is " doc = doc + "about nothing. Great tacos, burritos, and enchaladas. " doc = doc + "Bean burritos are also good. Lots more filler text here. " doc = doc + "The filler text in this sentence is muy bueno. Remember to " doc = doc + "eat lots of tacos." self.doc = doc self.query = "tacos burritos enchaladas" self.hi = Highlighter(self.doc, self.query) def test_tokenize_query_terms(self): tokens_result = self.hi.tokenize_query_terms() expected_result = ["tacos", "burritos", "enchaladas"] #print(tokens_result) self.assertEqual(expected_result, tokens_result, tokens_result) def test_query_term_density(self): query_terms = self.hi.tokenize_query_terms() query_term_partial_density = self.hi.query_term_partial_density() total_query_term_density = self.hi.query_term_density(query_term_partial_density) expected_result = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0, 0, 0, 0, 0, 0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0, 0, 0, 0, 0, 0, 0, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.2, 0.2, 0.2, 0.2, 0 ] #print(total_query_term_density) self.assertEqual(total_query_term_density, expected_result, total_query_term_density) def test_max_term_density_window(self): query_terms = self.hi.tokenize_query_terms() query_term_partial_density = self.hi.query_term_partial_density() total_query_term_density = self.hi.query_term_density(query_term_partial_density) result_window = self.hi.max_term_density_window(total_query_term_density) self.assertTrue(0 <= result_window[0] < result_window[1], "Failed basic windowing.") self.assertTrue(self.hi.maximum_snippit_length >= result_window[1] - result_window[0], "Window width too large.") def test_highlight_document(self): highlighted_result = self.hi.highlight_document() #print(highlighted_result) self.assertTrue(highlighted_result, highlighted_result) def test_highlight_doc(self): highlighted_result = Highlighter.highlight_doc(self.doc, self.query) msg = "\nhighlighted_result =\n" + highlighted_result expected_result = " [[HIGHLIGHT]]tacos[[HIGHLIGHT]] I've ever had were from Panchos. " expected_result = expected_result + "This sentence is about nothing. Great [[HIGHLIGHT]]" expected_result = expected_result + "tacos burritos[[HIGHLIGHT]], and [[HIGHLIGHT]]" expected_result = expected_result + "enchaladas[[HIGHLIGHT]]. Bean [[HIGHLIGHT]]burritos[[HIGHLIGHT]]" msg = msg + "\nexpected_result = \n" + expected_result self.assertEquals(expected_result, highlighted_result, msg)
class MainWindow(tk.Tk): def __init__(self): super().__init__() self.title('THEE') self.minsize(550, 40) # (width, height) self.winfo_screenwidth = self.winfo_screenwidth() self.winfo_screenheight = self.winfo_screenheight() self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.width = int(self.winfo_screenwidth / 3) self.height = int(self.winfo_screenheight / 3) self.geometry(f'{self.width}x{self.height}') self.thee_mode = 0 # 0: welcome, 1: editor, 2: terminal, 3: help self.count_text_changed = 0 self.editor_mode_buffer = "" self.terminal_mode_buffer = "" self.key_buffer = [] self.file_name = "untitled" self.status = "unsaved" self.spaces = 4 self.line = 1 self.column = 1 self.foreground = config.color['foreground'] self.background = config.color['background'] self.text_foreground = config.color['text_foreground'] self.text_background = config.color['text_background'] self.insertbackground = config.color['insertbackground'] self.statusbar_background = config.color['statusbarbg'] self.frame1 = tk.Frame(self, bg=self.background, width=self.width, height=self.height - 15) self.frame2 = tk.Frame(self, bg=self.statusbar_background, width=self.width, height=10) self.frame1.grid(row=0, column=0, sticky='wens') self.frame2.grid(row=1, column=0, sticky='wens') self.frame1.grid_columnconfigure(1, weight=1) self.frame1.grid_rowconfigure(0, weight=1) self.config_dir = os.path.join(str(Path.home()), '.thee') self.text_font_size = config.font['text']['size'] self.text_font_family = config.font['text']['family'] self.statusbar_font_size = config.font['statusbar']['size'] self.statusbar_font_family = config.font['statusbar']['family'] self.create_widget() # Entry point ==========# self.terminal = Terminal(self, self.text_area) # run terminal self.bind_events() self.open_file = '' self.protocol("WM_DELETE_WINDOW", self.close_window) def create_widget(self): self.text_area = TextArea(self.frame1, bg=self.text_background, fg=self.text_foreground, undo=True, relief=tk.FLAT, font=(self.text_font_family, self.text_font_size), insertbackground=self.insertbackground) self.text_area.config(highlightthickness=0) self.text_area.grid(row=0, column=1, sticky='wens') self.text_area.focus_set() self.welcome(event=None) self.status_bar1 = StatusBar(self.frame2, bg="pink", width=30, height=10) self.status_bar2 = StatusBar(self.frame2, bg="orange", width=30, height=10) self.status_bar3 = StatusBar(self.frame2, bg="blue", width=30, height=10) self.status_bar4 = StatusBar(self.frame2, bg="green", width=30, height=10) self.status_bar5 = StatusBar(self.frame2, bg="purple", width=30, height=10) self.status_bar1.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.status_bar2.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.status_bar3.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.status_bar4.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.status_bar5.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) def editor_mode(self, event=None): self.text_area.config(state=tk.NORMAL, tabs=4) self.text_area.delete('1.0', tk.END) self.text_area.insert(tk.END, self.editor_mode_buffer) self.highlighter = Highlighter(self.text_area) self.thee_mode = 1 self.line_numbers = LineNumbers(self.frame1, self.text_area) self.line_numbers.config(bg=self.text_background, width=len(self.line_numbers.line_number) * 10, highlightthickness=0) self.line_numbers.grid(row=0, column=0, sticky='ns') self.status_bar1.set("Line %d, Column %d" % (self.line, self.column)) self.status_bar3.set("%s" % self.file_name) self.status_bar4.set("Spaces: %d" % self.spaces) self.status_bar5.set("%s" % self.status) def terminal_mode(self, event=None): if self.thee_mode == 1: self.line_numbers.destroy() self.text_area.config(state=tk.NORMAL, tabs=4) self.text_area.delete('1.0', tk.END) self.text_area.insert(tk.END, self.terminal_mode_buffer) self.terminal.writeLoop() self.highlighter = Highlighter(self.text_area) self.thee_mode = 2 def text_been_modified(self, event=None): flag = self.text_area.edit_modified() if flag: # prevent from getting called twice if self.thee_mode == 1 and self.count_text_changed > 1: # editor mode self.editor_mode_buffer = self.text_area.get( 1.0, tk.END + "-1c") self.status = "unsaved" self.status_bar5.set("%s" % self.status) self.update_line_column() self.line_numbers.config( width=len(self.line_numbers.line_number) * 10) elif self.thee_mode == 2 and self.count_text_changed > 1: # terminal mode self.terminal_mode_buffer = self.text_area.get( 1.0, tk.END + "-1c") self.count_text_changed += 1 #reset so this will be called on the next change self.text_area.edit_modified(False) def retrieve_selected_line(self, event=None): if self.thee_mode == 1: self.current_line = self.text_area.get("1.0", 'end').rstrip() if event.keysym.isnumeric(): self.key_buffer.append(event.keysym) # check buffer after 500ms (0.5s) self.after(500, self.selected_line_action) def selected_line_action(self): if self.key_buffer: index = int(''.join(self.key_buffer)) - 1 self.key_buffer.clear() self.selected_line = self.current_line.split('\n')[index] selected_str = self.selected_line + "\n" # write selected code line(s) to the console in order to it running self.terminal.proc.stdin.write(selected_str.encode()) self.terminal.proc.stdin.flush() self.terminal_mode() def update_line_column(self, event=None): if self.thee_mode == 1: line, column = self.text_area.index(tk.INSERT).split('.') self.line = int(line) self.column = int(column) + 1 self.status_bar1.set("Line %d, Column %d" % (self.line, self.column)) def close_window(self): if self.editor_mode_buffer and self.status == "unsaved": #and self.status.get() == "unsaved": #SATUSBAR if msg.askokcancel("Quit", "Would you like to save the data?"): self.file_save() self.terminal.alive = False self.terminal.destroy() else: self.terminal.alive = False self.terminal.destroy() else: self.terminal.alive = False self.terminal.destroy() def bind_events(self): self.focus_set() self.text_area.bind_all('<<Modified>>', self.text_been_modified) self.text_area.bind('<Return>', self.enter) self.bind_all('<Button-1>', self.update_line_column) self.bind_all('<Control-e>', self.editor_mode) self.bind_all('<Control-t>', self.terminal_mode) self.bind_all('<Control-Key>', self.retrieve_selected_line) self.bind('<Control-f>', self.show_find_window) self.bind('<Control-n>', self.file_new) self.bind('<Control-o>', self.file_open) self.bind('<Control-s>', self.file_save) self.bind('<Control-S>', self.file_save_as) self.bind('<Control-w>', self.welcome) self.bind('<Control-h>', self.help_about) def enter(self, event=None): if self.thee_mode == 2: self.terminal.enter() def show_find_window(self, event=None): FindWindow(self.frame1, self.text_area) def show_welcome_page(self): if self.thee_mode == 1: self.line_numbers.destroy() self.text_area.config(state=tk.NORMAL) self.text_area.delete('1.0', tk.END) message = ''' \n\n THEE version 1.0 THE simple python key bindings Editor type Ctrl-h for help information by Fidel R. Monteiro <*****@*****.**> \n The Pynosso Project | Sat, Jun 26 2020 ''' self.text_area.insert(tk.END, message) self.text_area.config(state=tk.DISABLED) self.thee_mode = 0 def show_about_page(self): if self.thee_mode == 1: self.line_numbers.destroy() self.text_area.config(state=tk.NORMAL) self.text_area.delete('1.0', tk.END) message = ''' HELP Mode Commands Ctrl+e : Text mode Ctrl+t : Terminal mode Ctrl+<number> : Run selected line in python console Editing Commands Ctrl+a : Select all text Ctrl+x : Cut selected text Ctrl+c : Copy selected text Ctrl+v : Paste cut/copied text Ctrl+z : Undo Ctrl+y : Redo File Commands Ctrl+o : Open file Ctrl+s : Save current content Ctrl+S : Save current content as <filename> Ctrl+p : Print current content Ctrl+n : Open new file General Ctrl+m : Change syntax highlighting Ctrl+g : Change colour scheme Ctrl+l : Change font Ctrl+h : Display this help window AUTHOR Written by Fidel R. Monteiro (fm65) Sat, Jun 26 2020 thee version 1.0 "simple is better than complex" ''' self.text_area.insert(tk.END, message) self.text_area.config(state=tk.DISABLED) self.thee_mode = 3 def apply_colour_scheme(self, foreground, background, text_foreground, text_background): self.text_area.configure(fg=text_foreground, bg=text_background) self.background = background self.foreground = foreground for menu in self.all_menus: menu.configure(bg=self.background, fg=self.foreground) def update_font(self): #self.load_font_file(self.font_scheme_path) self.text_area.configure(font=(self.text_font_family, self.text_font_size)) def create_config_directory_if_needed(self): if not os.path.exists(self.config_dir): os.mkdir(self.config_dir) # =========== Menu Functions ============== def file_new(self, event=None): """ Ctrl+N """ self.text_area.delete(1.0, tk.END) self.open_file = None self.editor_mode() self.line_numbers.force_update() def file_open(self, event=None): """ Ctrl+O """ self.editor_mode() file_to_open = filedialog.askopenfilename(filetypes=[('Python files', '*.py')], defaultextension='.py') if file_to_open: self.open_file = file_to_open self.file_name = self.open_file.split('/')[-1] self.status_bar3.set("%s" % self.file_name) self.text_area.display_file_contents(file_to_open) self.highlighter.force_highlight() self.line_numbers.force_update() def file_save(self, event=None): """ Ctrl+s """ current_file = self.open_file if self.open_file else None if not current_file: current_file = filedialog.asksaveasfilename(filetypes=[ ('Python files', '*.py') ], defaultextension='.py') self.open_file = current_file self.file_name = current_file.split('/')[-1] self.status_bar3.set("%s" % self.file_name) if current_file: contents = self.text_area.get(1.0, tk.END) with open(current_file, 'w') as file: file.write(contents) self.status = "saved" self.status_bar5.set("%s" % self.status) def file_save_as(self, event=None): """ Ctrl+S """ new_file_name = filedialog.asksaveasfilename(filetypes=[ ('Python files', '*.py') ], defaultextension='.py', confirmoverwrite=False) f = open(self.new_file_name, 'w') f.write(self.get('1.0', 'end')) f.close() self.status = "saved" self.status_bar5.set("%s" % self.status) def edit_cut(self, event=None): """ Ctrl+X """ self.text_area.event_generate("<Control-x>") self.line_numbers.force_update() def edit_paste(self, event=None): """ Ctrl+V """ self.text_area.event_generate("<Control-v>") self.line_numbers.force_update() self.highlighter.force_highlight() def edit_copy(self, event=None): """ Ctrl+C """ self.text_area.event_generate("<Control-c>") def edit_select_all(self, event=None): """ Ctrl+A """ self.text_area.event_generate("<Control-a>") def edit_find_and_replace(self, event=None): """ Ctrl+F """ self.show_find_window() def welcome(self, event=None): """ Ctrl+W """ self.show_welcome_page() def help_about(self, event=None): """ Ctrl+H """ self.show_about_page()
class Script: def __init__(self, console, simulation_keywords): self.simulation_keywords = simulation_keywords self.gui = Gtk.Table() self.window = Gtk.ScrolledWindow() self.console = console self.prevent_button_update = False self._textview = Gtk.TextView() self._textbuffer = GtkSource.Buffer() self._textview.set_buffer(self._textbuffer) self._textbuffer.set_max_undo_levels(-1) self._textbuffer.set_undo_manager(None) self._textbuffer.connect("changed", self.on_text_changed) self._textbuffer.connect("paste-done", self.on_text_changed) self.error_tag = self._textbuffer.create_tag("error", background="orange") font_description = Pango.FontDescription("monospace 9") self._textview.modify_font(font_description) misc.set_tabs(self._textview, font_description, 4) self.window.set_shadow_type(Gtk.ShadowType.IN) misc.gtk_set_margin(self.gui, 5, top=10) # Init highlight: self.highlight = Highlighter(self._textbuffer) self.load_highlight() self.error_bar = Gtk.InfoBar() self.error_bar_label = Gtk.Label() self.error_bar.get_content_area().add(self.error_bar_label) self.error_bar_label.show() self.error_bar.set_message_type(Gtk.MessageType.ERROR) self.window.add(self._textview) #self.error_bar.set_size_request(80, 100) self.gui.attach(self.window, 0, 8, 0, 1) self.gui.attach(self.error_bar, 8, 10, 0, 1) self.error_bar.set_no_show_all(True) # Init script self.load_simulation("./simulations/welcome.py") def on_text_changed(self, x='', y=''): self.highlight.perform() error = None if hasattr(self.console, "controls"): error = self.console.controls.refresh() if not error: if self.prevent_button_update: self.prevent_button_update = False else: self.try_update_button_states() if hasattr(self.console, "controls"): self.error_bar.hide() else: error_name = error[0] exception = error[1] line = error[2] self.highlight_error_line(line) if hasattr(self.console, "controls"): messsage = _(error_name) messsage = '<span weight="bold">' + _(error_name) + ':</span>\n' if error_name in ["IndentationError", "SyntaxError"]: #pos = exception[1][2] #wrong_line = exception[1][3] messsage += "\n" + _(exception[0]) #messsage += "\n" + '<span style="italic">' + wrong_line + '</span>' elif error_name in ["NameError"]: name = exception[0][6:][:-16] messsage += "\n" + _("name '%s' is not defined") % name elif error_name in ["AttributeError"]: match = re.search("no attribute '(.*?)'", exception[0]) name = match.group(1) match = re.search("'(.*?)' object", exception[0]) obj = match.group(1) messsage += "\n" + _("'%s' object has no attribute '%s'") % (obj, name) else: messsage += "\n" + str(exception) print exception.args self.error_bar_label.set_markup('<span foreground="black">' + messsage + '</span>') self.error_bar.show() def get_functions_data(self, name, max_args=0): script = self.get_script() functions = [] reg_str = name + "\(" for i in xrange(max_args): reg_str += "(" if i > 0: reg_str += ",\s*" reg_str += "(.*?)" reg_str += ")?" reg_str += "\)" regexp_object = re.compile(reg_str) for match in regexp_object.finditer(script): entry = [] d = { 'element': match.group(0), 'start': match.start(0), 'end': match.end(0) } entry.append( d ) for i in xrange(max_args): try: arg = eval(match.group(2 + 2*i)) except: arg = False entry.append(arg) functions.append(entry) return functions def try_update_button_states(self): # 1. Let # regexp wyszukujacy let(option_name, value_from_script), # Z zapisem do listy register w postaci rekordow: # register[option_name] = value_from_script register_change = {} lets_data = self.get_functions_data('let', 2) for data in lets_data: if data[1] and data[2]: value = False try: option_name = data[1] value = data[2] register_change[option_name] = value except: pass # pobieram z option_browsera informacje o dostepnych let-ach # i ustawiam wszystkie na False. Potem aktualizuje liste dostepnych # let-ow lista register (aby wyeliminowac usuniete w skrypcie let-y) available = self.console.option_browser.get_let_actions_list() register = {} for let in available: register[let] = False register.update(register_change) register = register.items() self.console.option_browser.update_button_states_from_register(register) # 2. ... def undo(self): if self._textbuffer.can_undo(): self._textbuffer.undo() def redo(self): if self._textbuffer.can_redo(): self._textbuffer.redo() def insert(self, *args, **kwargs): self.insert_or_modify(True, *args, **kwargs) def modify(self, *args, **kwargs): """ try to find object specified by identity of x first arguments and modify them (where x is given by 'identities' keyword argument) otherwise add new object with specified variables """ self.insert_or_modify(False, *args, **kwargs) def insert_or_modify(self, force_new_instance, *args, **kwargs): """ where - on_top, on_bottom, after_comments into - script_body, <function_name> """ # whether to insert new instances on top or bottom of script where = kwargs.pop('where', "on_bottom") into = kwargs.pop('into', "script_body") # if the force_new_instance is true, # we will not modify exiting objects, but always add new # otherwise it will modify all existing object of specified # (by first 'non-self' positional argument) name. # It's possible to specify number of arguments that have to be identical, # to modify existing a function identities = kwargs.pop('identities', 1) args = list(args) object_type = args.pop(0) name = args.pop(0) if object_type in ["function", "object"]: new_func = misc.create_function_str(name, *args) if force_new_instance: return self.insert_into_buffer(new_func, where, into) replaced = False if identities > len(args): raise Exception("Wrong arguments: identities higher than number of non-special arguments (all arguments excep self, object_type and name)") data_list = self.get_functions_data(name, len(args)) for data in data_list: good_match = True for i in xrange(identities): if args[i] != data[1 + i]: good_match = False if good_match: self.replace_on_pos(data[0]["start"], data[0]["end"], new_func) replaced = True if not replaced: return self.insert_into_buffer(new_func, where, into) #if obj_type == class_function: (ball.push(...)) #if obj_type == class_property: (ball.velocity = ...) #if obj_type == comment: (# name = Nazwa symulacji) else: raise ValueError("object_type: " + str(object_type) + " is not defined") def replace_on_pos(self, start, end, new_text): start = self._textbuffer.get_iter_at_offset(start) end = self._textbuffer.get_iter_at_offset(end) self.prevent_button_update = True self._textbuffer.delete(start, end) self.prevent_button_update = True self._textbuffer.insert(start, new_text) def insert_into_buffer(self, new_text, where, into): if where in ["on_top", "after_comments"]: offset = 0 if where == "after_comments": script = self.get_script() inside_doc_string = False for line in script.split("\n"): raw_line = line.strip() if not raw_line: offset += len(line) + 1 break if inside_doc_string: if raw_line[-3:] == '"""': inside_doc_string = False offset += len(line) + 1 continue else: # TODO: could docstring by with '''? if raw_line[:3] == '"""': if raw_line[-3:] != '"""': inside_doc_string = True offset += len(line) + 1 continue # if line is comment if raw_line[0] == "#": offset += len(line) + 1 else: if not inside_doc_string: break new_text += "\n" iterator = self._textbuffer.get_iter_at_offset(offset) else: iterator = self._textbuffer.get_end_iter() new_text = "\n" + new_text self._textbuffer.insert(iterator, new_text) def highlight_error_line(self, line_number): start = self._textbuffer.get_iter_at_line(line_number - 1) end = self._textbuffer.get_iter_at_line(line_number) self._textbuffer.apply_tag(self.error_tag, start, end) def load_simulation(self, path): script = misc.file_get_contents(path) self.set_script(script) self.path = path def save_simulation(self, path): self.path = path script = self.get_script() misc.file_put_contents(path, script) def load_highlight(self): python_tfn = ["None", "True", "False", "-?\d+\.\d+", "-?\d+"] python_statements = [ "nonlocal", "and", "as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "raise", "return", "not", "or", "pass", "print", "try", "while", "with", "yield" ] self.highlight.add_words(python_statements, "statements", foreground="brown", weight=Pango.Weight.BOLD) self.highlight.add_words(python_tfn, "tfn", foreground="purple") self.highlight.add_words(self.simulation_keywords, "sim_func", foreground="green") self.highlight.add([re.compile("(?P<highlight>const)([.]+)")], "const", foreground="green", weight=Pango.Weight.NORMAL) self.highlight.add([re.compile("[\"](.*?)[\"]|['](.*?)[']")], "string", foreground="purple", weight=Pango.Weight.NORMAL) self.highlight.add([re.compile("def([\s])(?P<highlight>(.*?))\((.*?)\):")], "after_def", foreground="purple", weight=Pango.Weight.NORMAL) # comment must be last (color overriding) self.highlight.add([re.compile("#(.)*")], "comment", foreground="blue", weight=Pango.Weight.NORMAL) def set_script(self, text): self._textbuffer.begin_not_undoable_action() self._textbuffer.set_text(text) self._textbuffer.end_not_undoable_action() def get_script(self): start_iter = self._textbuffer.get_start_iter() end_iter = self._textbuffer.get_end_iter() return self._textbuffer.get_text(start_iter, end_iter, True)
class MainWindow(tk.Tk): ''' @param tk.TK: Windows、Mac、Unix下TK GUI套件的标准Python接口,可实现GUI界面 ''' #窗口设置 def __init__(self): ''' @update 1.添加横向滚动条 2.修正行号与滚动条和窗口的关联 3.修正行号显示 ''' super().__init__() self.title('Python Text Editor v3') self.geometry('800x600') self.foreground = 'black' self.background = 'lightgrey' self.text_foreground = 'black' self.text_background = 'white' self.config_dir = os.path.join(str(Path.home()), '.tkedit') self.default_scheme_path = os.path.join(self.config_dir, 'schemes/default.yaml') self.python_language_path = os.path.join(self.config_dir, 'languages/python.yaml') self.none_language_path = os.path.join(self.config_dir, 'languages/None.yaml') self.font_scheme_path = os.path.join(self.config_dir, 'fonts/font.yaml') self.create_config_directory_if_needed() self.load_scheme_file(self.default_scheme_path) self.configure_ttk_elements() self.font_size = 15 self.font_family = "Ubuntu Mono" self.load_font_file(self.font_scheme_path) self.text_area = TextArea(self, bg=self.text_background, fg=self.text_foreground, undo=True, font=(self.font_family, self.font_size)) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.text_area.yview) self.scrollbar2 = ttk.Scrollbar(orient="horizontal", command=self.text_area.xview) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1, font=(self.font_family, self.font_size)) self.scrollbar.config(command=self.text_area.yview) self.text_area.config(yscrollcommand=self.scrollbar.set, xscrollcommand=self.scrollbar2.set) self.highlighter = Highlighter(self.text_area, self.none_language_path) self.menu = tk.Menu(self, bg=self.background, fg=self.foreground) self.all_menus = [self.menu] sub_menu_items = ["file", "edit", "tools", "help"] self.generate_sub_menus(sub_menu_items) self.configure(menu=self.menu) self.right_click_menu = tk.Menu(self, bg=self.background, fg=self.foreground, tearoff=0) self.right_click_menu.add_command(label='Cut', command=self.edit_cut) self.right_click_menu.add_command(label='Copy', command=self.edit_copy) self.right_click_menu.add_command(label='Paste', command=self.edit_paste) self.all_menus.append(self.right_click_menu) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.scrollbar2.pack(side=tk.BOTTOM, fill=tk.X) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events() self.open_file = '' #绑定事件 def bind_events(self): self.text_area.bind("<MouseWheel>", self.scroll_text) self.text_area.bind("<Button-4>", self.scroll_text) self.text_area.bind("<Button-5>", self.scroll_text) self.text_area.bind("<Button-3>", self.show_right_click_menu) self.bind('<Control-f>', self.show_find_window) self.bind('<Control-n>', self.file_new) self.bind('<Control-o>', self.file_open) self.bind('<Control-s>', self.file_save) self.bind('<Control-h>', self.help_about) self.bind('<Control-m>', self.tools_change_syntax_highlighting) self.bind('<Control-g>', self.tools_change_colour_scheme) self.bind('<Control-l>', self.tools_change_font) self.line_numbers.bind("<MouseWheel>", lambda e: "break") self.line_numbers.bind("<Button-4>", lambda e: "break") self.line_numbers.bind("<Button-5>", lambda e: "break") #滚动事件 def scroll_text(self, *args): if len(args) > 1: self.text_area.yview_moveto(args[1]) self.line_numbers.yview_moveto(args[1]) else: event = args[0] if event.delta: move = -1 * (event.delta / 120) else: if event.num == 5: move = 1 else: move = -1 self.text_area.yview_scroll(int(move), "units") self.line_numbers.yview_scroll(int(move) * 3, "units") #内容查找、替换窗口 def show_find_window(self, event=None): FindWindow(self.text_area) #显示右键菜单 def show_right_click_menu(self, event): x = self.winfo_x() + self.text_area.winfo_x() + event.x y = self.winfo_y() + self.text_area.winfo_y() + event.y self.right_click_menu.post(x, y) #设置子菜单 def generate_sub_menus(self, sub_menu_items): ''' @param sub_menu_items: 子菜单项目 ''' window_methods = [ method_name for method_name in dir(self) if callable(getattr(self, method_name)) ] tkinter_methods = [ method_name for method_name in dir(tk.Tk) if callable(getattr(tk.Tk, method_name)) ] my_methods = [ method for method in set(window_methods) - set(tkinter_methods) ] my_methods = sorted(my_methods) for item in sub_menu_items: sub_menu = tk.Menu(self.menu, tearoff=0, bg=self.background, fg=self.foreground) matching_methods = [] for method in my_methods: if method.startswith(item): matching_methods.append(method) for match in matching_methods: actual_method = getattr(self, match) method_shortcut = actual_method.__doc__.strip() friendly_name = ' '.join(match.split('_')[1:]) sub_menu.add_command(label=friendly_name.title(), command=actual_method, accelerator=method_shortcut) self.menu.add_cascade(label=item.title(), menu=sub_menu) self.all_menus.append(sub_menu) #显示关于 def show_about_page(self): msg.showinfo( "About", "My text editor, version 2, written in Python3.6 using tkinter!") #加载文字高亮配色方案 def load_syntax_highlighting_file(self): syntax_file = filedialog.askopenfilename(filetypes=[("YAML file", ("*.yaml", "*.yml"))]) if syntax_file: self.highlighter.clear_highlight() self.highlighter = Highlighter(self.text_area, syntax_file) self.highlighter.force_highlight() #加载窗体配色方案 def load_scheme_file(self, scheme): ''' @param scheme: 目标配置文件 @update GSC 预检测 ''' with open(scheme, 'r') as stream: try: config = yaml.load(stream) except yaml.YAMLError as error: print(error) return if 'foreground' in config: self.foreground = config['foreground'] if 'background' in config: self.background = config['background'] if 'text_foreground' in config: self.text_foreground = config['text_foreground'] if 'text_background' in config: self.text_background = config['text_background'] #加载字体及字号方案 def load_font_file(self, file_path): ''' @param file_path: 方案存放路径及文件名 ''' with open(file_path, 'r') as stream: try: config = yaml.load(stream) except yaml.YAMLError as error: print(error) return if 'family' in config: self.font_family = config['family'] if 'size' in config: self.font_size = config['size'] #修改窗体配色方案 def change_colour_scheme(self): ColourChooser(self) #应用窗体配色方案 def apply_colour_scheme(self, foreground, background, text_foreground, text_background): ''' @param foreground: 新窗体前景色 @param background: 新窗体背景色 @param text_foreground: 新字体前景色 @param text_background: 新字体背景色 ''' self.text_area.configure(fg=text_foreground, bg=text_background) self.background = background self.foreground = foreground for menu in self.all_menus: menu.configure(bg=self.background, fg=self.foreground) self.configure_ttk_elements() #应用按钮、标签配色方案 def configure_ttk_elements(self): style = ttk.Style() style.configure('editor.TLabel', foreground=self.foreground, background=self.background) style.configure('editor.TButton', foreground=self.foreground, background=self.background) #字体选择窗口 def change_font(self): FontChooser(self) #更新字体 def update_font(self): ''' @update 1.添加行号字体更新 ''' self.load_font_file(self.font_scheme_path) self.text_area.configure(font=(self.font_family, self.font_size)) self.line_numbers.configure(font=(self.font_family, self.font_size)) #运行环境检测 def create_config_directory_if_needed(self): if not os.path.exists(self.config_dir): os.mkdir(self.config_dir) if not os.path.exists(os.path.join(self.config_dir, 'schemes')): os.mkdir(os.path.join(self.config_dir, 'schemes')) if not os.path.exists(os.path.join(self.config_dir, 'languages')): os.mkdir(os.path.join(self.config_dir, 'languages')) if not os.path.exists(os.path.join(self.config_dir, 'fonts')): os.mkdir(os.path.join(self.config_dir, 'fonts')) self.create_default_scheme_if_needed() self.create_font_scheme_if_needed() self.create_python_language_if_needed() #运行环境创建 -- 窗体默认配置方案 def create_default_scheme_if_needed(self): if not os.path.exists(self.default_scheme_path): yaml_file_contents = f"background: 'lightgrey'\n" \ + f"foreground: 'black'\n" \ + f"text_background: 'white'\n" \ + f"text_foreground: 'black'\n" with open(self.default_scheme_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) #运行环境创建 -- 字体默认配置方案 def create_font_scheme_if_needed(self): if not os.path.exists(self.font_scheme_path): yaml_file_contents = f"family: Ubuntu Mono\n" \ + f"size: 15" with open(self.font_scheme_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) #运行环境创建 -- 语言文字高亮默认配置方案 def create_python_language_if_needed(self): if not os.path.exists(self.python_language_path): yaml_file_contents = """ categories: keywords: colour: orange matches: [for, def, while, from, import, as, with, self] variables: colour: red4 matches: ['True', 'False', None] conditionals: colour: green matches: [try, except, if, else, elif] functions: colour: blue4 matches: [int, str, dict, list, set, float] numbers: colour: purple strings: colour: '#e1218b' """ with open(self.python_language_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) if not os.path.exists(self.none_language_path): yaml_file_contents = """ categories: keywords: colour: black matches: [for, def, while, from, import, as, with, self] variables: colour: black matches: ['True', 'False', None] conditionals: colour: black matches: [try, except, if, else, elif] functions: colour: black matches: [int, str, dict, list, set, float] numbers: colour: black strings: colour: black """ with open(self.none_language_path, 'w') as yaml_file: yaml_file.write(yaml_file_contents) # =========== Menu Functions ============== #新建文件 def file_new(self, event=None): """ Ctrl+N """ self.text_area.delete(1.0, tk.END) self.open_file = "" self.line_numbers.force_update() #打开文件 def file_open(self, event=None): """ Ctrl+O """ file_to_open = filedialog.askopenfilename() if file_to_open: self.open_file = file_to_open self.text_area.display_file_contents(file_to_open) self.highlighter.force_highlight() self.line_numbers.force_update() #保存文件 def file_save(self, event=None): """ Ctrl+S """ current_file = self.open_file if self.open_file else None if not current_file: current_file = filedialog.asksaveasfilename() if current_file: contents = self.text_area.get(1.0, tk.END) with open(current_file, 'w') as file: file.write(contents) #剪切 def edit_cut(self, event=None): """ Ctrl+X """ self.text_area.event_generate("<Control-x>") self.line_numbers.force_update() #粘贴 def edit_paste(self, event=None): """ Ctrl+V """ self.text_area.event_generate("<Control-v>") self.line_numbers.force_update() self.highlighter.force_highlight() #复制 def edit_copy(self, event=None): """ Ctrl+C """ self.text_area.event_generate("<Control-c>") #全选 def edit_select_all(self, event=None): """ Ctrl+A """ self.text_area.event_generate("<Control-a>") #查找和替换 def edit_find_and_replace(self, event=None): """ Ctrl+F """ self.show_find_window() #帮助与关于 def help_about(self, event=None): """ Ctrl+H """ self.show_about_page() #选择语言(文字高亮) def tools_change_syntax_highlighting(self, event=None): """ Ctrl+M """ self.load_syntax_highlighting_file() #选择窗体配色 def tools_change_colour_scheme(self, event=None): """ Ctrl+G """ self.change_colour_scheme() #选择字体字号 def tools_change_font(self, event=None): """ Ctrl+L """ self.change_font()
def __init__(self): ''' @update 1.添加横向滚动条 2.修正行号与滚动条和窗口的关联 3.修正行号显示 ''' super().__init__() self.title('Python Text Editor v3') self.geometry('800x600') self.foreground = 'black' self.background = 'lightgrey' self.text_foreground = 'black' self.text_background = 'white' self.config_dir = os.path.join(str(Path.home()), '.tkedit') self.default_scheme_path = os.path.join(self.config_dir, 'schemes/default.yaml') self.python_language_path = os.path.join(self.config_dir, 'languages/python.yaml') self.none_language_path = os.path.join(self.config_dir, 'languages/None.yaml') self.font_scheme_path = os.path.join(self.config_dir, 'fonts/font.yaml') self.create_config_directory_if_needed() self.load_scheme_file(self.default_scheme_path) self.configure_ttk_elements() self.font_size = 15 self.font_family = "Ubuntu Mono" self.load_font_file(self.font_scheme_path) self.text_area = TextArea(self, bg=self.text_background, fg=self.text_foreground, undo=True, font=(self.font_family, self.font_size)) self.scrollbar = ttk.Scrollbar(orient="vertical", command=self.text_area.yview) self.scrollbar2 = ttk.Scrollbar(orient="horizontal", command=self.text_area.xview) self.line_numbers = LineNumbers(self, self.text_area, bg="grey", fg="white", width=1, font=(self.font_family, self.font_size)) self.scrollbar.config(command=self.text_area.yview) self.text_area.config(yscrollcommand=self.scrollbar.set, xscrollcommand=self.scrollbar2.set) self.highlighter = Highlighter(self.text_area, self.none_language_path) self.menu = tk.Menu(self, bg=self.background, fg=self.foreground) self.all_menus = [self.menu] sub_menu_items = ["file", "edit", "tools", "help"] self.generate_sub_menus(sub_menu_items) self.configure(menu=self.menu) self.right_click_menu = tk.Menu(self, bg=self.background, fg=self.foreground, tearoff=0) self.right_click_menu.add_command(label='Cut', command=self.edit_cut) self.right_click_menu.add_command(label='Copy', command=self.edit_copy) self.right_click_menu.add_command(label='Paste', command=self.edit_paste) self.all_menus.append(self.right_click_menu) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.scrollbar2.pack(side=tk.BOTTOM, fill=tk.X) self.line_numbers.pack(side=tk.LEFT, fill=tk.Y) self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.bind_events() self.open_file = ''
def setupEditor(self): self.highlighter = Highlighter(self.ui.mainTextEdit.document())
from main import app from highlighter import Highlighter if __name__ == "__main__": h = Highlighter('hello.py', "print('hello world')") h.get_highlighted_code() h.get_stylesheet_content() h.get_css_id_name() print('successfully finished all tests')