class Example(QMainWindow): def __init__(self): super().__init__() self.text_editor = QPlainTextEdit() self.syntax = PythonHighlighter(self.text_editor.document()) self.init_ui() def init_ui(self): background_color = QColor() background_color.setNamedColor('#282821') color_palette = self.text_editor.palette() color_palette.setColor(QPalette.Text, Qt.white) color_palette.setColor(QPalette.Base, background_color) self.text_editor.setPalette(color_palette) default_font = self.text_editor.font() default_font.setPointSize(9) self.text_editor.setFont(default_font) self.setWindowTitle('Example') self.setCentralWidget(self.text_editor) self.setGeometry(500, 500, 500, 500) self.show()
class TextLogElement(object): def __init__(self, maximum_block_count: int = 1000, font_size_pt: int = 10, font_family: str = "Courier", title: str = "Log") -> None: # For nested layouts: (1) create everything, (2) lay out self.log_group = StyledQGroupBox(title) log_layout_1 = QVBoxLayout() log_layout_2 = QHBoxLayout() self.log = QPlainTextEdit() self.log.setReadOnly(True) self.log.setLineWrapMode(QPlainTextEdit.NoWrap) self.log.setMaximumBlockCount(maximum_block_count) font = self.log.font() font.setFamily(font_family) font.setPointSize(font_size_pt) log_clear_button = QPushButton('Clear log') log_clear_button.clicked.connect(self.log.clear) log_copy_button = QPushButton('Copy to clipboard') log_copy_button.clicked.connect(self.copy_whole_log) log_layout_2.addWidget(log_clear_button) log_layout_2.addWidget(log_copy_button) log_layout_2.addStretch(1) log_layout_1.addWidget(self.log) log_layout_1.addLayout(log_layout_2) self.log_group.setLayout(log_layout_1) def get_widget(self) -> QWidget: return self.log_group def add(self, msg: str) -> None: # http://stackoverflow.com/questions/16568451 # self.log.moveCursor(QTextCursor.End) self.log.appendPlainText(msg) # ... will append it as a *paragraph*, i.e. no need to add a newline # self.scroll_to_end_of_log() def copy_whole_log(self) -> None: # Ctrl-C will copy the selected parts. # log.copy() will copy the selected parts. self.log.selectAll() self.log.copy() self.log.moveCursor(QTextCursor.End) self.scroll_to_end_of_log() def scroll_to_end_of_log(self) -> None: vsb = self.log.verticalScrollBar() vsb.setValue(vsb.maximum()) hsb = self.log.horizontalScrollBar() hsb.setValue(0)
class SystemLog: def __init__(self, display_name, source_name): self.display_name = display_name self.source_name = source_name # FIXME: need to handle rotated logs self.last_filename = os.path.join(get_home_path(), display_name) self.content = '' self.edit = QPlainTextEdit() self.normal_font = self.edit.font() self.monospace_font = QFont('monospace') self.edit.setUndoRedoEnabled(False) self.edit.setReadOnly(True) self.edit.setWordWrapMode(QTextOption.NoWrap) self.edit.setPlainText( 'Click "Refresh" to download {0}.'.format(display_name)) self.monospace_font.setStyleHint(QFont.TypeWriter) def log(self, message, bold=False, pre=False): if bold: self.edit.appendHtml('<b>{0}</b>'.format(html.escape(message))) elif pre: self.edit.appendHtml('<pre>{0}</pre>'.format(message)) else: self.edit.appendPlainText(message) def reset(self): self.content = None self.edit.setPlainText('') self.edit.setFont(self.normal_font) def set_content(self, content): self.content = content self.edit.setPlainText('') self.edit.setFont(self.monospace_font) self.edit.setPlainText(content)
class Notepad(QMainWindow): def __init__(self): self.judgeConfigFile() self.clipboard = QApplication.clipboard() self.lastSearchText = "" self.lastReplaceSearchText = "" self.reset = False self.config = parser.ConfigParser() self.config.read(CONFIG_FILE_PATH) QMainWindow.__init__(self) self.initUI() def initUI(self): self.setWindowTitle("无标题 - 记事本") self.initEditText() self.createActions() self.createStatusBar() self.createMenubars() self.readSettings() self.text.document().contentsChanged.connect(self.documentWasModified) self.setCurrentFile('') def initEditText(self): self.text = QPlainTextEdit() self.text.setContextMenuPolicy(Qt.CustomContextMenu) self.text.customContextMenuRequested.connect(self.customContextMenu) self.setCentralWidget(self.text) def customContextMenu(self): menu = QMenu(self) menu.addAction(self.undoAction) menu.addSeparator() menu.addAction(self.cutAction) menu.addAction(self.copyAction) menu.addAction(self.pasteAction) menu.addAction(self.deleteAction) menu.addSeparator() menu.addAction(self.selectAllAction) menu.exec_(QCursor.pos()) return menu def documentWasModified(self): self.setWindowModified(self.text.document().isModified()) if "" != self.text.toPlainText(): self.findAction.setEnabled(True) self.findNextAction.setEnabled(True) else: self.findAction.setEnabled(False) self.findNextAction.setEnabled(False) def readSettings(self): width = getConfig(self.config, "Display", "width", "1000") height = getConfig(self.config, "Display", "height", "600") size = QSize(int(width), int(height)) screen = QDesktopWidget().screenGeometry() pos_x = getConfig(self.config, "Display", "x", (screen.width() - 1000) // 2) pos_y = getConfig(self.config, "Display", "y", (screen.height() - 600) // 2) pos = QPoint(int(pos_x), int(pos_y)) toolbar = getConfig(self.config, "Display", "toolbar", "True") wrapMode = getConfig(self.config, "TextEdit", "wrapmode", "True") fontFamile = getConfig(self.config, "TextEdit", "font", "Consolas") fontSize = getConfig(self.config, "TextEdit", "size", 14) fonts = QFont(fontFamile, int(fontSize)) if "True" == wrapMode: wrapMode = QPlainTextEdit.WidgetWidth else: wrapMode = QPlainTextEdit.NoWrap self.resize(size) self.move(pos) self.text.setLineWrapMode(wrapMode) self.text.setFont(fonts) def resetSettings(self): writeConfig(self.config, "Display", "width", "1000") writeConfig(self.config, "Display", "height", "600") screen = QDesktopWidget().screenGeometry() writeConfig(self.config, "Display", "x", str((screen.width() - 1000) // 2)) writeConfig(self.config, "Display", "y", str((screen.height() - 600) // 2)) writeConfig(self.config, "Display", "toolbar", "True") writeConfig(self.config, "TextEdit", "wrapmode", "True") writeConfig(self.config, "TextEdit", "font", "Consolas") writeConfig(self.config, "TextEdit", "size", "14") self.config.write(open(CONFIG_FILE_PATH, "w")) QMessageBox.information(self, "记事本", "重置成功,请重启记事本!") self.reset = True self.close() def writeSettings(self): writeConfig(self.config, "Display", "height", str(self.size().height())) writeConfig(self.config, "Display", "width", str(self.size().width())) writeConfig(self.config, "Display", "x", str(self.pos().x())) writeConfig(self.config, "Display", "y", str(self.pos().y())) writeConfig( self.config, "TextEdit", "wrapmode", str(self.text.lineWrapMode() == QPlainTextEdit.WidgetWidth)) writeConfig(self.config, "TextEdit", "font", self.text.font().family()) writeConfig(self.config, "TextEdit", "size", str(self.text.font().pointSize())) self.config.write(open(CONFIG_FILE_PATH, "w")) def judgeConfigFile(self): if not os.path.exists(CONFIG_FILE_PATH): f = open(CONFIG_FILE_PATH, mode="w", encoding="UTF-8") f.close() def createActions(self): self.saveAction = QAction("&保存", self, shortcut=QKeySequence.Save, statusTip="保存文件", triggered=self.save) self.exitAction = QAction("退出", self, shortcut="Ctrl+Q", statusTip="退出程序", triggered=self.close) self.undoAction = QAction("撤销", self, shortcut=QKeySequence.Undo, statusTip="撤销编辑", triggered=self.text.undo) self.cutAction = QAction("剪切", self, shortcut=QKeySequence.Cut, statusTip="剪切选中的文本", triggered=self.text.cut) self.copyAction = QAction("复制", self, shortcut=QKeySequence.Copy, statusTip="复制选中的文本", triggered=self.text.copy) self.pasteAction = QAction("粘贴", self, shortcut=QKeySequence.Paste, statusTip="粘贴剪切板的文本", triggered=self.text.paste) self.clearAction = QAction("清空剪切板", self, statusTip="清空剪切板", triggered=self.clearClipboard) self.deleteAction = QAction("删除", self, statusTip="删除选中的文本", triggered=self.delete) self.findAction = QAction("查找", self, statusTip="查找文本", triggered=self.findText, shortcut=QKeySequence.Find) self.findNextAction = QAction("查找下一个", self, statusTip="查找文本", triggered=self.findNextText, shortcut=QKeySequence.FindNext) self.replaceAction = QAction("替换", self, statusTip="替换文本", triggered=self.replaceText, shortcut=QKeySequence.Replace) self.selectAllAction = QAction("全选", self, shortcut=QKeySequence.SelectAll, statusTip="全选", triggered=self.text.selectAll) self.autoWrapAction = QAction("自动换行", self, statusTip="设置自动换行", triggered=self.setWrap) self.fontAction = QAction("字体", self, statusTip="设置字体", triggered=self.setFont_) self.aboutQtAction = QAction("关于Qt", self, triggered=QApplication.instance().aboutQt) self.undoAction.setEnabled(False) self.cutAction.setEnabled(False) self.copyAction.setEnabled(False) self.deleteAction.setEnabled(False) if "" == self.clipboard.text(): self.pasteAction.setEnabled(False) self.clearAction.setEnabled(False) if "" == self.text.toPlainText(): self.findAction.setEnabled(False) self.findNextAction.setEnabled(False) self.text.undoAvailable.connect(self.undoAction.setEnabled) self.text.copyAvailable.connect(self.cutAction.setEnabled) self.text.copyAvailable.connect(self.copyAction.setEnabled) self.text.copyAvailable.connect(self.deleteAction.setEnabled) self.clipboard.dataChanged.connect(self.enabledSomeActionByClipboard) def enabledSomeActionByClipboard(self): if ("" != self.clipboard.text()): self.pasteAction.setEnabled(True) self.clearAction.setEnabled(True) def clearClipboard(self): self.clipboard.clear() self.pasteAction.setEnabled(False) self.clearAction.setEnabled(False) def createStatusBar(self): self.statusBar().showMessage("准备就绪") def createMenubars(self): file = self.menuBar().addMenu("文件") file.addAction(self.saveAction) file.addSeparator() file.addSeparator() file.addAction(self.exitAction) edit = self.menuBar().addMenu("编辑") edit.addAction(self.undoAction) edit.addSeparator() edit.addAction(self.cutAction) edit.addAction(self.copyAction) edit.addAction(self.pasteAction) edit.addAction(self.clearAction) edit.addAction(self.deleteAction) edit.addSeparator() edit.addAction(self.findAction) edit.addAction(self.findNextAction) edit.addAction(self.replaceAction) edit.addSeparator() edit.addAction(self.selectAllAction) style = self.menuBar().addMenu("格式") style.addAction(self.autoWrapAction) style.addAction(self.fontAction) help = self.menuBar().addMenu("帮助") help.addAction(self.aboutQtAction) def maybeSave(self): if self.text.document().isModified(): ret = self.tip() if 0 == ret: return self.save() if 2 == ret: return False return True def openFile(self, fileName): file = QFile(fileName) if not file.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning( self, "记事本", "文件%s不能被读取:\n%s." % (fileName, file.errorString())) return inf = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) self.text.setPlainText(inf.readAll()) QApplication.restoreOverrideCursor() self.setCurrentFile(fileName) self.show() self.statusBar().showMessage("文件读取成功", 2000) def setCurrentFile(self, fileName): self.curFile = fileName self.text.document().setModified(False) self.setWindowModified(False) if self.curFile: shownName = self.strippedName(self.curFile) else: shownName = '未命名.txt' self.setWindowTitle("%s[*] - 记事本" % shownName) def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName() def save(self): if self.curFile: return self.saveFile(self.curFile) else: return self.saveAs() def saveFile(self, fileName): file = QFile(fileName) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning( self, "记事本", "文件%s不能被写入:\n%s." % (fileName, file.errorString())) return False outf = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) outf << self.text.toPlainText() QApplication.restoreOverrideCursor() self.setCurrentFile(fileName) self.statusBar().showMessage("写入文件成功", 2000) return True def closeEvent(self, event): if not self.maybeSave(): event.ignore() else: if not self.reset: self.writeSettings() event.accept() def tip(self, title="记事本", content="文件已被修改,是否保存?"): alertBox = QMessageBox(self) saveButton = alertBox.addButton("保存", QMessageBox.ActionRole) unSaveButton = alertBox.addButton("不保存", QMessageBox.ActionRole) cancelButton = alertBox.addButton("取消", QMessageBox.ActionRole) alertBox.setWindowTitle(title) alertBox.setText(content) alertBox.exec_() button = alertBox.clickedButton() if saveButton == button: return 0 elif unSaveButton == button: return 1 elif cancelButton == button: return 2 else: return -1 def delete(self): cursor = self.text.textCursor() if not cursor.isNull(): cursor.removeSelectedText() self.statusBar().showMessage("删除成功", 2000) def findText(self): self.displayFindDialog() def findNextText(self): if "" == self.lastSearchText: self.displayFindDialog() else: self.searchText() def displayFindDialog(self): self.findDialog = QDialog(self) label = QLabel("查找内容:") self.lineEdit = QLineEdit() self.lineEdit.setText(self.lastSearchText) label.setBuddy(self.lineEdit) self.findButton = QPushButton("查找下一个") self.findButton.setDefault(True) self.findButton.clicked.connect(self.searchText) buttonBox = QDialogButtonBox(Qt.Vertical) buttonBox.addButton(self.findButton, QDialogButtonBox.ActionRole) topLeftLayout = QHBoxLayout() topLeftLayout.addWidget(label) topLeftLayout.addWidget(self.lineEdit) leftLayout = QVBoxLayout() leftLayout.addLayout(topLeftLayout) mainLayout = QGridLayout() mainLayout.setSizeConstraint(QLayout.SetFixedSize) mainLayout.addLayout(leftLayout, 0, 0) mainLayout.addWidget(buttonBox, 0, 1) mainLayout.setRowStretch(2, 1) self.findDialog.setLayout(mainLayout) self.findDialog.setWindowTitle("查找") self.findDialog.show() def searchText(self): cursor = self.text.textCursor() findIndex = cursor.anchor() text = self.lineEdit.text() content = self.text.toPlainText() length = len(text) self.lastSearchText = text index = content.find(text, findIndex) if -1 == index: errorDialog = QMessageBox(self) errorDialog.addButton("取消", QMessageBox.ActionRole) errorDialog.setWindowTitle("记事本") errorDialog.setText("找不到\"%s\"." % text) errorDialog.setIcon(QMessageBox.Critical) errorDialog.exec_() else: start = index cursor = self.text.textCursor() cursor.clearSelection() cursor.movePosition(QTextCursor.Start, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.Right, QTextCursor.MoveAnchor, start + length) cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, length) cursor.selectedText() self.text.setTextCursor(cursor) def replaceText(self): replaceDialog = QDialog(self) replaceLabel = QLabel("替换内容:") self.replaceText = QLineEdit() self.replaceText.setText(self.lastReplaceSearchText) replaceLabel.setBuddy(self.replaceText) replaceToLabel = QLabel("替换为 :") self.replaceToText = QLineEdit() replaceToLabel.setBuddy(self.replaceToText) findNextButton = QPushButton("查找下一个") findNextButton.setDefault(True) replaceButton = QPushButton("替换") replaceAllButton = QPushButton("全部替换") cancelAllButton = QPushButton("取消") findNextButton.clicked.connect(lambda: self.replaceOrSearch(False)) cancelAllButton.clicked.connect(replaceDialog.close) replaceButton.clicked.connect(lambda: self.replaceOrSearch(True)) replaceAllButton.clicked.connect(self.replaceAllText) buttonBox = QDialogButtonBox(Qt.Vertical) buttonBox.addButton(findNextButton, QDialogButtonBox.ActionRole) buttonBox.addButton(replaceButton, QDialogButtonBox.ActionRole) buttonBox.addButton(replaceAllButton, QDialogButtonBox.ActionRole) buttonBox.addButton(cancelAllButton, QDialogButtonBox.ActionRole) topLeftLayout = QHBoxLayout() topLeftLayout.addWidget(replaceLabel) topLeftLayout.addWidget(self.replaceText) topLeftLayout2 = QHBoxLayout() topLeftLayout2.addWidget(replaceToLabel) topLeftLayout2.addWidget(self.replaceToText) leftLayout = QVBoxLayout() leftLayout.addLayout(topLeftLayout) leftLayout.addLayout(topLeftLayout2) mainLayout = QGridLayout() mainLayout.setSizeConstraint(QLayout.SetFixedSize) mainLayout.addLayout(leftLayout, 0, 0) mainLayout.addWidget(buttonBox, 0, 1) mainLayout.setRowStretch(2, 1) replaceDialog.setLayout(mainLayout) replaceDialog.setWindowTitle("替换") replaceDialog.show() def replaceOrSearch(self, isReplace): cursor = self.text.textCursor() findIndex = cursor.anchor() text = self.replaceText.text() content = self.text.toPlainText() length = len(text) index = content.find(text, findIndex) self.lastReplaceSearchText = text if -1 == index: errorDialog = QMessageBox(self) errorDialog.addButton("取消", QMessageBox.ActionRole) errorDialog.setWindowTitle("记事本") errorDialog.setText("找不到\"%s\"." % text) errorDialog.setIcon(QMessageBox.Critical) errorDialog.exec_() else: start = index if isReplace: toReplaceText = self.replaceToText.text() prefix = content[0:start] postfix = content[start + length:] newText = prefix + toReplaceText + postfix self.text.setPlainText(newText) length = len(toReplaceText) self.text.document().setModified(True) cursor = self.text.textCursor() cursor.clearSelection() cursor.movePosition(QTextCursor.Start, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.Right, QTextCursor.MoveAnchor, start + length) cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, length) cursor.selectedText() self.text.setTextCursor(cursor) def replaceAllText(self): text = self.replaceText.text() content = self.text.toPlainText() toReplaceText = self.replaceToText.text() content = content.replace(text, toReplaceText) self.text.setPlainText(content) self.text.document().setModified(True) def setWrap(self): mode = self.text.lineWrapMode() if 1 == mode: self.text.setLineWrapMode(QPlainTextEdit.NoWrap) else: self.text.setLineWrapMode(QPlainTextEdit.WidgetWidth) def toggleToolBar(self): if self.toolBar.isHidden(): self.toolBar.show() else: self.toolBar.hide() def preferences(self): print("") def setFont_(self): font, ok = QFontDialog.getFont(QFont(self.text.toPlainText()), self) if ok: self.text.setFont(font)
class ParameterDialog(QDialog): def __init__(self, file_name='', sep=',', decimal='.', header=True, index=False): super().__init__() self.setMinimumSize(520, 200) self.separator = sep self.decimal = decimal self.header = header self.index = index QBtn = QDialogButtonBox.Ok | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(QBtn) self.buttonBox.accepted.connect(self.validate) self.buttonBox.rejected.connect(self.reject) self.preview = QPlainTextEdit() # set font to monospaced font = self.preview.font() font.setFamily("Courier New") self.preview.setFont(font) self.layout = QVBoxLayout() groupbox_file = QGroupBox("Path to CSV file:") self.layout.addWidget(groupbox_file) self.layout_file = QHBoxLayout() groupbox_file.setLayout(self.layout_file) self.layout_file.addWidget(QLabel("File:")) self.filename = QLineEdit() self.filename.textChanged.connect(self.onFileNameTextChange) self.filename.setText(file_name) self.layout_file.addWidget(self.filename) self.btn_file = QPushButton("...") self.btn_file.setMaximumWidth(25) self.btn_file.clicked.connect(self.onBtnFileClicked) self.layout_file.addWidget(self.btn_file) # separator and decimal group self.layout_sepdec = QHBoxLayout() # separator (delimiter) groupbox_sep = QGroupBox("Separator:") self.layout_sepdec.addWidget(groupbox_sep) self.layout_sep = QVBoxLayout() self.layout_sep.setAlignment(QtCore.Qt.AlignCenter) groupbox_sep.setLayout(self.layout_sep) self.radio_comma = QRadioButton("Comma") self.radio_comma.setChecked(sep == ',') self.radio_comma.separator = "," self.radio_comma.toggled.connect(self.onClicked) self.radio_semicol = QRadioButton("Semicolon") self.radio_semicol.setChecked(sep == ';') self.radio_semicol.separator = ";" self.radio_semicol.toggled.connect(self.onClicked) self.radio_tab = QRadioButton("Tab") self.radio_tab.setChecked(sep == '\t') self.radio_tab.separator = "\t" self.radio_tab.toggled.connect(self.onClicked) self.layout_sep.addWidget(self.radio_comma) self.layout_sep.addWidget(self.radio_semicol) self.layout_sep.addWidget(self.radio_tab) # decimal point groupbox_dp = QGroupBox("Decimal point:") self.layout_sepdec.addWidget(groupbox_dp) self.layout_dp = QVBoxLayout() self.layout_dp.setAlignment(QtCore.Qt.AlignCenter) groupbox_dp.setLayout(self.layout_dp) self.radio_dp_dot = QRadioButton("Dot") self.radio_dp_dot.setChecked(decimal == '.') self.radio_dp_dot.decimal = "." self.radio_dp_dot.toggled.connect(self.onClickedDecimal) self.radio_dp_comma = QRadioButton("Comma") self.radio_dp_comma.setChecked(decimal == ',') self.radio_dp_comma.decimal = "," self.radio_dp_comma.toggled.connect(self.onClickedDecimal) self.layout_dp.addWidget(self.radio_dp_dot) self.layout_dp.addWidget(self.radio_dp_comma) # self.layout.addLayout(self.layout_sepdec) # header and index column groupbox_header = QGroupBox("Header and row labels:") self.layout.addWidget(groupbox_header) self.layout_header = QVBoxLayout() groupbox_header.setLayout(self.layout_header) self.chk_header = QCheckBox("Header (column name) in first row") self.chk_header.setChecked(self.header) self.layout_header.addWidget(self.chk_header) self.chk_header.stateChanged.connect(self.onClickedHeader) self.chk_index = QCheckBox("Row label in first column") self.chk_index.setChecked(self.index) self.layout_header.addWidget(self.chk_index) self.chk_index.stateChanged.connect(self.onClickedIndex) # preview csv file groupbox_pre = QGroupBox("Preview:") self.layout.addWidget(groupbox_pre) self.layout_pre = QHBoxLayout() groupbox_pre.setLayout(self.layout_pre) file_head = self.file_preview(self.filename.text()) self.preview.document().setPlainText(file_head) self.preview.setReadOnly(True) self.layout_pre.addWidget(self.preview) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def file_preview(self, path: str, n=8) -> str: """ return first n lines from csv file, if file exists """ file_head = '' if path != '' and os.path.isfile(path): with open(path, 'r') as f: for i in range(1, n): file_head += f.readline() return file_head def validate(self): if self.filename.text() == '': QMessageBox.about(self, 'Error', 'Path to CSV file is mandatory.') self.filename.setFocus() else: self.accept() def onClicked(self): radio = self.sender() if radio.isChecked(): self.separator = radio.separator def onClickedDecimal(self): radio = self.sender() if radio.isChecked(): self.decimal = radio.decimal def onClickedHeader(self, state): if state == QtCore.Qt.Checked: self.header = True else: self.header = False def onClickedIndex(self, state): if state == QtCore.Qt.Checked: self.index = True else: self.index = False def onBtnFileClicked(self): file_name, _ = QFileDialog.getOpenFileName( self, "Open CSV file...", "", "CSV (*.csv);;All Files (*)") if file_name: self.filename.setText(file_name) file_head = self.file_preview(self.filename.text()) self.preview.document().setPlainText(file_head) def onFileNameTextChange(self): """ on text change in field filename (QLineEdit) - clear and set new preview""" file_head = self.file_preview(self.filename.text()) self.preview.document().setPlainText(file_head)
class Editor(QMainWindow): def __init__(self): super().__init__() self.currentpath = '' self.font = QFont("Monaco", 14) self.font.setFixedPitch(True) self.title = 'simple text editor' self.textEditor = QPlainTextEdit() self.textEditor.setFont(self.font) # set tab length 4 spaces self.textEditor.setTabStopDistance( QFontMetricsF(self.textEditor.font()).width(' ') * 4) self.menubar = self.menuBar() self.menubar.setFont(self.font) self.init_ui() def setFileMenu(self): # fileMenu setting fileMenu = self.menubar.addMenu('File') fileMenu.setFont(self.font) # action newAct = QAction('New', self) openAct = QAction('Open', self) autoSave = QAction('AutoSavle', self, checkable=True) exitAct = QAction('Exit', self) saveAct = QAction('Save', self) exitAct.triggered.connect(qApp.quit) newAct.setShortcut('Ctrl+N') openAct.setShortcut('Ctrl+O') exitAct.setShortcut('Ctrl+Q') saveAct.setShortcut('Ctrl+S') # action detail openAct.triggered.connect(self.openFile) newAct.triggered.connect(self.newFile) saveAct.triggered.connect(self.saveFile) # autoSave set default False autoSave.setChecked(False) # add action on fileMenu fileMenu.addAction(newAct) fileMenu.addAction(openAct) fileMenu.addAction(saveAct) fileMenu.addAction(autoSave) fileMenu.addAction(exitAct) def newFile(self): fname = QFileDialog.getSaveFileName(self, 'Save file') context = self.textEditor.toPlainText() if fname[0]: with open(fname[0], 'w') as f: f.write(context) self.currentpath = fname[0] def saveFile(self): path = self.currentpath context = self.textEditor.toPlainText() if path: with open(path, 'w') as f: f.write(context) def setSettingMenu(self): # settingMenu setting settingMenu = self.menubar.addMenu('Setting') settingMenu.setFont(self.font) # action fontAct = QAction('Font', self) # action detail settingMenu.addAction(fontAct) # add action on settingMenu fontAct.triggered.connect(self.selectFont) def selectFont(self): font, ok = QFontDialog.getFont() if ok: self.textEditor.setFont(font) def init_ui(self): self.setFileMenu() self.setSettingMenu() self.setCentralWidget(self.textEditor) # setting window self.setGeometry(500, 300, 800, 600) self.setWindowTitle(self.title) self.show() def contextMenuEvent(self, event): cmenu = QMenu(self) newAct = cmenu.addAction("New") openAct = cmenu.addAction("Open") quitAct = cmenu.addAction("Quit") action = cmenu.exec_(self.mapToGlobal(event.pos())) if action == newAct: pass if action == quitAct: qApp.quit() if action == openAct: pass def openFile(self): # get current use main home full path as string main_home = Path.home().absolute().as_posix() fname = QFileDialog.getOpenFileName(self, 'Open File', main_home) if fname[0]: with open(fname[0], 'r') as f: content = f.read() self.textEditor.setPlainText(content) self.currentpath = fname[0]
class App(QMainWindow): def __init__(self): super().__init__() self.title = 'Resolution theorem prover' self.left = 300 self.top = 300 self.width = 600 self.height = 600 self.init_ui() def init_ui(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # Create textboxGivens self.textboxExpr = QLineEdit(self) self.textboxExpr.move(20, 20) self.textboxExpr.resize(300, 30) font = self.textboxExpr.font() font.setPointSize(10) self.textboxExpr.setFont(font) # Create textboxConclusion self.textboxConc = QLineEdit(self) self.textboxConc.move(330, 20) self.textboxConc.resize(50, 30) font = self.textboxConc.font() font.setPointSize(10) self.textboxConc.setFont(font) self.verboseText = QPlainTextEdit(self) self.verboseText.setReadOnly(True) self.verboseText.move(20, 60) self.verboseText.resize(550, 500) font = self.verboseText.font() font.setPointSize(12) self.verboseText.setFont(font) # Create a button in the window self.button = QPushButton('Resolute', self) self.button.move(400, 20) # connect button to function on_click self.button.clicked.connect(self.on_click) self.show() def print_expr(self, idx : int, expr, val_to_names : dict, parents = ()): self.verboseText.appendPlainText(str(idx) + ') ' + ' ∨ '.join( ('¬' if x < 0 else '') + str(val_to_names[abs(x)]) for x in expr) + ('\t resolvent of ' + str(parents) if parents else '') + '\n') def print_expr_str(self, idx : int, expr_str : str, parents = ()): self.verboseText.appendPlainText(str(idx) + ') ' + expr_str + ('\t resolvent of ' + str(parents) if parents else '') + '\n') @pyqtSlot() def on_click(self): if self.textboxConc.text() is '' or self.textboxExpr.text() is '': return try: text = '(' + (self.textboxExpr.text() + ', ~' + self.textboxConc.text())\ .replace(' ', '').replace(',', ')/\\(') + ')' givens = expr( text ) concl = expr( self.textboxConc.text() ) simplified_str = str( simplify(givens)) self.verboseText.setPlainText('Expression: ' + simplified_str + '\n') simplified_str = simplified_str.split('∧') expressions = [] simplified_str = [simplified_str[-1]] + simplified_str[:-1] liters = [] expr_parts = [] b = '() ' for let in simplified_str: for s in b: let = let.replace(s, '') if len(let) == 1 or len(let) == 2 and let[0] == '¬': liters.append(let) else: expr_parts.append(let) simplified_str = liters + expr_parts dict_letters = dict() dict_values = dict() for ex in simplified_str: cur = ex.split('∨') cur_exp = set() for symb in cur: letter = symb.replace('¬', '') if letter not in dict_letters: dict_letters[letter] = len(dict_letters) + 1 dict_values[dict_letters[letter]] = letter cur_exp.add(dict_letters[letter] * (-1 if '¬' in symb else 1)) expressions.append(cur_exp) self.print_expr(len(expressions), cur_exp, dict_values) i = 0 result = False while (not result) and (i < len(expressions) - 1): for j in range(i + 1, len(expressions)): if not (expressions[j] <= expressions[i]): tmp = expressions[i].union(expressions[j]) new_exp = set() has_conc_pair = False for l in tmp: if -l in tmp: has_conc_pair = True else: new_exp.add(l) if has_conc_pair and new_exp not in expressions: if len(new_exp) == 0: result = True self.print_expr_str(len(expressions) + 1, '▯', (i + 1, j + 1)) break else: expressions.append(new_exp) self.print_expr(len(expressions), new_exp, dict_values, (i + 1, j + 1)) i += 1 if result: self.verboseText.appendPlainText('Theorem proved.') else: self.verboseText.appendPlainText('Theorem was not proved.') except: self.verboseText.appendPlainText('Incorrect expression.')
class InputDock(QDockWidget): def __init__(self): super().__init__() self._methods = {} self.setWindowFlags(Qt.WindowTitleHint) central_widget = QWidget() self._layout = QVBoxLayout() central_widget.setLayout(self._layout) self.setWidget(central_widget) self._general_box = QGroupBox("General") size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding) self._general_layout = QVBoxLayout() self._general_box.setLayout(self._general_layout) self._layout.addWidget(self._general_box) self._measurement_box = QGroupBox("Measurement") size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._measurement_box.setSizePolicy(size_policy) self._measurement_layout = QVBoxLayout() self._measurement_box.setLayout(self._measurement_layout) self._layout.addWidget(self._measurement_box) self._init_dir_picker() self._init_comment() self._init_contacts_picker() self._init_sample_config() self._init_measurement_box() self._init_input_area() self._init_controls() def _init_comment(self): self._comment_box = QPlainTextEdit() metrics = QFontMetrics(self._comment_box.font()) row_height = metrics.lineSpacing() # four rows self._comment_box.setFixedHeight(4 * row_height) self._general_layout.addWidget(QLabel("Comment:")) self._general_layout.addWidget(self._comment_box) def _init_controls(self): self._play_string = "\u25b6" self._pause_string = "\u23F8" button_layout = QHBoxLayout() self._measure_button = QPushButton(self._play_string) self._measure_button.setDisabled(True) font = self._measure_button.font() font.setPointSize(20) font.setWeight(100) self._measure_button.setFont(font) self._measure_button.setToolTip("start/pause a measurement") self._abort_button = QPushButton("\u2716") self._abort_button.setDisabled(True) font = self._abort_button.font() font.setPointSize(20) font.setWeight(100) self._abort_button.setFont(font) self._abort_button.setToolTip("abort the current measurement") self._next_button = QPushButton("\u2192") self._next_button.setDisabled(True) font = self._next_button.font() font.setPointSize(20) font.setWeight(100) self._next_button.setFont(font) self._next_button.setToolTip("next contacts") button_layout.addStretch() button_layout.addWidget(self._measure_button) button_layout.addWidget(self._abort_button) button_layout.addWidget(self._next_button) self._layout.addLayout(button_layout) def _init_dir_picker(self): self._dir_picker = DirectoryPicker() self._general_layout.addWidget(self._dir_picker) def _init_contacts_picker(self): self._contacts_picker = ContactsSelector() self._general_layout.addWidget(self._contacts_picker) self._contacts_picker.save_triggered.connect(self._on_new_group) def _on_new_group(self): contacts = self._contacts_picker.contacts name, okay = QInputDialog.getText(self, "New Group", "Enter Group Name") if name != "" and okay: result = self._sample_config.add_config(name, contacts) if result == False: error = QErrorMessage() error.showMessage("Name is already in use") def _init_sample_config(self): self._sample_config = SampleConfig() self._general_layout.addWidget(self._sample_config) def _init_measurement_box(self): self._method_selection_box = QComboBox() self._method_selection_box.currentTextChanged.connect( self._method_changed) self._method_model = self._method_selection_box.model() default_item = QStandardItem("-- Select a method --") default_item.setEnabled(False) self._method_model.appendRow(default_item) self._measurement_layout.addWidget(self._method_selection_box) def _init_input_area(self): self._scroll_area = QScrollArea() self._scroll_area.setWidgetResizable(True) self._scroll_area.setFrameShape(QFrame.NoFrame) self._scroll_layout = QVBoxLayout() scroll_widget = QWidget() self._scroll_area.setWidget(scroll_widget) scroll_widget.setLayout(self._scroll_layout) self._layout.addWidget(self._scroll_area) self._measurement_layout.addWidget(self._scroll_area) def add_method(self, method_name: str, method): if not method_name in self._methods: dynamic_layout = DynamicInputLayout(method.inputs()) dynamic_layout.setContentsMargins(0, 0, 0, 0) container = QWidget() container.hide() container.setLayout(dynamic_layout) self._scroll_layout.addWidget(container) self._methods[method_name] = (container, method) self._method_selection_box.addItem(method_name) def load_sample_config(self, file_name): self._sample_config.load_from_file(file_name) def save_sample_config(self, file_name): self._sample_config.save_to_file(file_name) @property def comment(self): text = self._comment_box.toPlainText() text = text.replace("\n", "\n# ") return "# " + text @property def directory_path(self): self._dir_picker.directory @property def contacts(self): self._contacts_picker.contacts @property def method(self): self._method_selection_box.currentText() @property def inputs(self): widget, _ = self._methods[self.method] method_layout = widget.layout() return method_layout.get_inputs() def _method_changed(self): selected_method = self._method_selection_box.currentText() self._hide_all_methods() self._show_method(selected_method) def _hide_all_methods(self): [widget.hide() for _, (widget, _) in self._methods.items()] def _show_method(self, name): if name in self._methods: widget, method = self._methods[name] widget.show() self._contacts_picker.set_number_of_contacts( method.number_of_contacts())
class DebugWindow(QMainWindow): """A main window to edit text and examine the generated token structure. Example:: from PyQt5.Qt import * a=QApplication([]) from parceqt.debug import DebugWindow w = DebugWindow() w.resize(1200,900) w.show() w.set_theme("default", True) from parce.lang.css import * w.set_root_lexicon(Css.root) w.set_text(open("path/to/parce/themes/default.css").read()) In the debug window you can edit the text at the left and directly at the right examine the tree structure. Along the top of the window the path to the token at the current cursor position is displayed, from the root lexicon upto the token, from which the action is displayed. Clicking a button selects the associated range of the context or token in the text view. Clicking an item in the tree also selects that range in the text. Moving the cursor in the text updates the current item in the tree, and the displayed ancestor path. """ show_updated_region_enabled = False def __init__(self, parent=None): super().__init__(parent, windowTitle="parceqt debugger") f = self._updated_format = QTextCharFormat() c = QColor("palegreen") c.setAlpha(64) f.setBackground(c) f = self._currentline_format = QTextCharFormat() f.setProperty(QTextCharFormat.FullWidthSelection, True) self._actions = Actions(self) self._actions.add_menus(self.menuBar()) widget = QWidget(self) self.setCentralWidget(widget) layout = QVBoxLayout(margin=4, spacing=2) widget.setLayout(layout) top_layout = QHBoxLayout(margin=0, spacing=0) self.guessButton = QToolButton(self, clicked=self.guess_root_lexicon, toolTip="Guess Language", icon=self.style().standardIcon( QStyle.SP_BrowserReload)) self.lexiconChooser = LexiconChooser(self) self.ancestorView = AncestorView(self) top_layout.addWidget(self.guessButton) top_layout.addWidget(self.lexiconChooser) top_layout.addWidget(self.ancestorView) top_layout.addStretch(10) layout.addLayout(top_layout) self.guessButton.setFixedHeight( self.lexiconChooser.sizeHint().height()) splitter = QSplitter(self, orientation=Qt.Horizontal) layout.addWidget(splitter, 100) self.textEdit = QPlainTextEdit(lineWrapMode=QPlainTextEdit.NoWrap, cursorWidth=2) self.treeView = QTreeView() splitter.addWidget(self.textEdit) splitter.addWidget(self.treeView) splitter.setStretchFactor(0, 3) splitter.setStretchFactor(1, 2) self.extraSelectionManager = ExtraSelectionManager(self.textEdit) self.document = d = self.textEdit.document() self.textEdit.setDocument(self.document) self.worker = w = parceqt.worker(d) self.builder = b = w.builder() w.debugging = True self.setStatusBar(QStatusBar()) self.create_model() # signal connections self.textEdit.viewport().installEventFilter(self) self.textEdit.installEventFilter(self) self.lexiconChooser.lexicon_changed.connect( self.slot_root_lexicon_changed) self.ancestorView.node_clicked.connect(self.slot_node_clicked) w.started.connect(self.slot_build_started) w.tree_updated.connect(self.slot_build_updated) self.textEdit.cursorPositionChanged.connect( self.slot_cursor_position_changed) self.treeView.clicked.connect(self.slot_item_clicked) self.textEdit.setFocus() self.set_theme() # somewhat larger font by default font = self.textEdit.font() font.setPointSizeF(11) self.textEdit.setFont(font) def create_model(self): """Instantiate a tree model for the tree view.""" m = self.treeView.model() if not m: m = parceqt.treemodel.TreeModel(self.builder.root) m.connect_debugging_builder(self.builder) self.treeView.setModel(m) def delete_model(self): """Delete the model and remove it from the tree.""" m = self.treeView.model() if m: m.disconnect_debugging_builder(self.builder) self.treeView.setModel(None) m.deleteLater() def set_text(self, text): """Set the text in the text edit.""" self.document.setPlainText(text) def set_root_lexicon(self, lexicon): """Set the root lexicon to use.""" self.lexiconChooser.set_root_lexicon(lexicon) def guess_root_lexicon(self): """Again choose the root lexicon based on the text.""" text = self.document.toPlainText() if text: self.set_root_lexicon(parce.find(contents=text)) def open_file(self, filename): """Read a file from disk and guess the language.""" text = read_file(filename) root_lexicon = parce.find(filename=filename, contents=text) self.set_text(text) self.set_root_lexicon(root_lexicon) c = self.textEdit.textCursor() c.setPosition(0) self.textEdit.setTextCursor(c) def set_theme(self, theme="default", adjust_widget=True): """Set the theme to use for the text edit.""" if isinstance(theme, str): theme = parce.theme_by_name(theme) formatter = parceqt.formatter.Formatter(theme) if theme else None if adjust_widget: if formatter: font = formatter.font(self) self.textEdit.setPalette(formatter.palette(self)) else: font = QApplication.font(self) self.textEdit.setPalette(QApplication.palette(self)) font.setPointSizeF(self.textEdit.font().pointSizeF()) # keep size self.textEdit.setFont(font) self.highlight_current_line() h = parceqt.highlighter.SyntaxHighlighter.instance(self.worker) h.set_formatter(formatter) def slot_build_started(self): """Called when the tree builder has started a build.""" self.treeView.setCursor(Qt.BusyCursor) def slot_build_updated(self): """Called when the tree builder has finished a build.""" self.treeView.unsetCursor() self.slot_cursor_position_changed() self.statusBar().showMessage(", ".join( lexicon_names(self.builder.lexicons))) tree = self.worker.get_root() self.lexiconChooser.setToolTip( parceqt.treemodel.TreeModel.node_tooltip(tree)) if self.show_updated_region_enabled: self.show_updated_region() def slot_cursor_position_changed(self): """Called when the text cursor moved.""" tree = self.worker.get_root() if tree: pos = self.textEdit.textCursor().position() doc = parceqt.document.Document(self.document) token = doc.token(pos) if token: self.ancestorView.set_token_path(token) model = self.treeView.model() if model: index = model.get_model_index(token) self.treeView.setCurrentIndex(index) elif tree is not None: self.ancestorView.clear() self.highlight_current_line() def slot_item_clicked(self, index): """Called when a node in the tree view is clicked.""" tree = self.worker.get_root() if tree: model = self.treeView.model() if model: node = self.treeView.model().get_node(index) cursor = self.textEdit.textCursor() cursor.setPosition(node.end) cursor.setPosition(node.pos, QTextCursor.KeepAnchor) self.textEdit.setTextCursor(cursor) self.textEdit.setFocus() def slot_node_clicked(self, node): """Called when a button in the ancestor view is clicked.""" tree = self.worker.get_root() if tree and node.root() is tree: cursor = self.textEdit.textCursor() cursor.setPosition(node.end) cursor.setPosition(node.pos, QTextCursor.KeepAnchor) self.textEdit.setTextCursor(cursor) self.textEdit.setFocus() model = self.treeView.model() if model: index = model.get_model_index(node) self.treeView.expand(index) self.treeView.setCurrentIndex(index) def slot_root_lexicon_changed(self, lexicon): """Called when the root lexicon is changed.""" parceqt.set_root_lexicon(self.document, lexicon) def highlight_current_line(self): """Highlight the current line.""" group = QPalette.Active if self.textEdit.hasFocus( ) else QPalette.Inactive p = self.textEdit.palette() color = p.color(group, QPalette.AlternateBase) self._currentline_format.setBackground(color) if color != p.color(group, QPalette.Base): c = self.textEdit.textCursor() c.clearSelection() self.extraSelectionManager.highlight(self._currentline_format, [c]) else: self.extraSelectionManager.clear(self._currentline_format) def show_updated_region(self): """Highlight the updated region for 2 seconds.""" end = self.builder.end if end >= self.document.characterCount() - 1: end = self.document.characterCount() - 1 if self.builder.start == 0: return c = QTextCursor(self.document) c.setPosition(end) c.setPosition(self.builder.start, QTextCursor.KeepAnchor) self.extraSelectionManager.highlight(self._updated_format, [c], msec=2000) def clear_updated_region(self): self.extraSelectionManager.clear(self._updated_format) def eventFilter(self, obj, ev): """Implemented to support Ctrl+wheel zooming and keybfocus handling.""" if obj == self.textEdit: if ev.type() in (QEvent.FocusIn, QEvent.FocusOut): self.highlight_current_line() else: # viewport if ev.type() == QEvent.Wheel and ev.modifiers( ) == Qt.ControlModifier: if ev.angleDelta().y() > 0: self.textEdit.zoomIn() elif ev.angleDelta().y() < 0: self.textEdit.zoomOut() return True return False