class DetailView(QWidget): def __init__(self, context): super().__init__() self.context = context self.resize(300, 200) self.move(1000, 100) self.textbox = QPlainTextEdit(self) self.textbox.setPlainText(self.context) self.textbox.move(20, 20) self.textbox.resize(100, 100) self.search_button = QPushButton(self) self.search_button.setText("Search") self.search_button.clicked.connect(self._search) self.search_box = QLineEdit(self) self.search_box.returnPressed.connect(self.search_button.click) self.lay = QGridLayout(self) self.lay.addWidget(self.textbox, 0, 0) self.lay.addWidget(self.search_box, 1, 0) self.lay.addWidget(self.search_button, 2, 0) self.setLayout(self.lay) self.show() def _search(self): searchstring = self.search_box.text() #positions = [m.start() for m in re.finditer(searchstring, self.context)] self.textbox.find(searchstring) self.textbox.setBackgroundVisible(False)
class MainWindow(QWidget): def __init__(self, width, height): super().__init__() self.resize(width, height) # устанавливаем размер окна по размеру экрана self.setWindowTitle('FormulaVoiceInput') # заголовок self.tex = QPlainTextEdit() # редактируемое текстовое поле, в котором будут выводиться команды TEX self.set_tex() # настройка начального текста, необходимого для компиляции из tex в pdf self.image = QLabel() # здесь выводится картинка с формулой self.pixmap = QPixmap() # пиксельная карта для картинки self.scale = 1 # текущий к-т масштабирования картинки self.scroll = QScrollArea() # область с полосами прокрутки self.slider = QSlider(Qt.Horizontal) # ползунок для изменения масштаба self.set_slider() self.status_bar = QStatusBar() # нижняя строка состояния self.status_bar.addWidget(self.slider) # добавляем ползунок для изменения масштаба self.grid = QGridLayout() # макет сетки для виджетов self.set_layout() # установка макета self.words = SpeechProcessing(self.tex) # для обработки распознанной речи self.subprocess = QProcess(self) # подпроцесс для получения текста из pocketsphinx self.speech_to_text() # распознавание речи self.show() def set_tex(self): # начальная строка start_line = "\\documentclass{article}\n\ \\usepackage[left=0mm,right=0mm,top=0mm,bottom=0mm,bindingoffset=0mm]{geometry}\n\ \\usepackage[fleqn]{amsmath}\n\ \\begin{document}\n\ \\begin{gather*}\n\n\ \\end{gather*}\n\ \\end{document}" self.tex.insertPlainText(start_line) # вставка начальной строки self.tex.find('begin{gather*}', QTextDocument.FindBackward) # находим место фрагмента begin{gather*} self.tex.moveCursor(QTextCursor.Down) # устанавливаем курсор внутри def set_slider(self): self.slider.setMinimum(0) # минимальный масштаб self.slider.setMaximum(200) # максимальный масштаб self.slider.setValue(100) # устанавливаем ползунок посередине self.slider.valueChanged[int].\ connect(self.scale_img) # обработчик изменений положения ползунка def scale_img(self, scale): # масштабирование картинки с формулой self.scale = scale / 100 self.image = QLabel() # создаем новый виджет для картинки # устанавливаем масштабированную картинку self.image.setPixmap(self.pixmap.scaledToWidth(self.pixmap.size().width() * self.scale)) self.scroll.setWidget(self.image) # добавляем новый виджет в область прокрутки def set_layout(self): self.grid.addWidget(self.tex, 1, 0) self.grid.setSpacing(20) self.grid.addWidget(self.scroll, 1, 1) self.grid.addWidget(self.status_bar, 2, 1) self.setLayout(self.grid) # устанавливаем макет сетки для окна def speech_to_text(self): # распознавание речи с микрофона с помощью pocketsphinx logs = open('speech/pocketsphinx_logs', 'w') # создаем файл, в который будут записываться логи от pocketsphinx cmd = "pocketsphinx_continuous\ -hmm speech/map \ -dict speech/math.dict \ -jsgf speech/math.jsgf \ -logfn speech/pocketsphinx_logs \ -inmic yes" args = shlex.split(cmd) self.subprocess.\ readyReadStandardOutput.connect(self.stdout_ready) self.subprocess.start(args[0], args[1:]) def stdout_ready(self): # меняем кодировку текста, полученного в рез-те распознавания text = bytearray(self.subprocess.readAllStandardOutput()).decode("utf-8").rstrip() if text == 'новая формула': self.words.new_formula() elif text == 'обновить': self.create_img() self.update_img() else: self.words.queue.extend(text.split()) # добавляем новые слова self.words.parsing() # анализ распознанных слов self.create_img() self.update_img() def create_img(self): # получение pdf из файла tex tex_file = open('tex_file.tex', 'w') # создаем файл tex_file.tex tex_file.write(self.tex.toPlainText()) # запись текста из self.tex в tex_file tex_file.close() self.start_subprocess('pdflatex tex_file.tex', 1000) if path.isfile('tex_file.pdf'): self.start_subprocess('pdftoppm -png tex_file.pdf formula', 500) # запуск подпроцесса с параметрами, указанными в cmd и временем ожидания завершения delay(в миллисекундах) def start_subprocess(self, cmd, delay): args = shlex.split(cmd) p = QProcess(self) p.start(args[0], args[1:]) p.waitForFinished(delay) p.close() # загрузка новой картинки при добавлении новых символов def update_img(self): if path.isfile('formula-1.png'): self.pixmap.load('formula-1.png') self.image = QLabel() w = self.pixmap.size().width() self.image.setPixmap(self.pixmap.scaledToWidth(w * self.scale)) self.scroll.setWidget(self.image)
class myEditor(QMainWindow): def __init__(self, parent=None): super(myEditor, self).__init__(parent) self.MaxRecentFiles = 5 self.windowList = [] self.recentFileActs = [] self.setAttribute(Qt.WA_DeleteOnClose) # Editor Widget ... QIcon.setThemeName('Faenza-Dark') self.editor = QPlainTextEdit() self.editor.setStyleSheet(stylesheet2(self)) self.editor.setFrameStyle(QFrame.NoFrame) self.editor.setTabStopWidth(14) self.extra_selections = [] self.fname = "" self.filename = "" # Line Numbers ... self.numbers = NumberBar(self.editor) self.createActions() # Laying out... layoutH = QHBoxLayout() layoutH.setSpacing(1.5) layoutH.addWidget(self.numbers) layoutH.addWidget(self.editor) ### begin toolbar tb = QToolBar(self) tb.setWindowTitle("File Toolbar") self.newAct = QAction("&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.newAct.setIcon(QIcon.fromTheme("document-new")) self.openAct = QAction("&Open", self, shortcut=QKeySequence.Open, statusTip="open file", triggered=self.openFile) self.openAct.setIcon(QIcon.fromTheme("document-open")) self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save, statusTip="save file", triggered=self.fileSave) self.saveAct.setIcon(QIcon.fromTheme("document-save")) self.saveAsAct = QAction("&Save as ...", self, shortcut=QKeySequence.SaveAs, statusTip="save file as ...", triggered=self.fileSaveAs) self.saveAsAct.setIcon(QIcon.fromTheme("document-save-as")) self.exitAct = QAction("Exit", self, shortcut=QKeySequence.Quit, toolTip="Exit", triggered=self.handleQuit) self.exitAct.setIcon(QIcon.fromTheme("application-exit")) ### find / replace toolbar self.tbf = QToolBar(self) self.tbf.setWindowTitle("Find Toolbar") self.findfield = QLineEdit() self.findfield.addAction(QIcon.fromTheme("edit-find"), QLineEdit.LeadingPosition) self.findfield.setClearButtonEnabled(True) self.findfield.setFixedWidth(150) self.findfield.setPlaceholderText("find") self.findfield.setToolTip("press RETURN to find") self.findfield.setText("") ft = self.findfield.text() self.findfield.returnPressed.connect(self.findText) self.tbf.addWidget(self.findfield) self.replacefield = QLineEdit() self.replacefield.addAction(QIcon.fromTheme("edit-find-and-replace"), QLineEdit.LeadingPosition) self.replacefield.setClearButtonEnabled(True) self.replacefield.setFixedWidth(150) self.replacefield.setPlaceholderText("replace with") self.replacefield.setToolTip("press RETURN to replace the first") self.replacefield.returnPressed.connect(self.replaceOne) self.tbf.addSeparator() self.tbf.addWidget(self.replacefield) self.tbf.addSeparator() self.tbf.addAction("replace all", self.replaceAll) self.tbf.addSeparator() layoutV = QVBoxLayout() bar = self.menuBar() self.filemenu = bar.addMenu("File") self.separatorAct = self.filemenu.addSeparator() self.filemenu.addAction(self.newAct) self.filemenu.addAction(self.openAct) self.filemenu.addAction(self.saveAct) self.filemenu.addAction(self.saveAsAct) self.filemenu.addSeparator() for i in range(self.MaxRecentFiles): self.filemenu.addAction(self.recentFileActs[i]) self.updateRecentFileActions() self.filemenu.addSeparator() self.filemenu.addAction(self.exitAct) bar.setStyleSheet(stylesheet2(self)) editmenu = bar.addMenu("Edit") editmenu.addAction( QAction(QIcon.fromTheme('edit-copy'), "Copy", self, triggered=self.editor.copy, shortcut=QKeySequence.Copy)) editmenu.addAction( QAction(QIcon.fromTheme('edit-cut'), "Cut", self, triggered=self.editor.cut, shortcut=QKeySequence.Cut)) editmenu.addAction( QAction(QIcon.fromTheme('edit-paste'), "Paste", self, triggered=self.editor.paste, shortcut=QKeySequence.Paste)) editmenu.addAction( QAction(QIcon.fromTheme('edit-delete'), "Delete", self, triggered=self.editor.cut, shortcut=QKeySequence.Delete)) editmenu.addSeparator() editmenu.addAction( QAction(QIcon.fromTheme('edit-select-all'), "Select All", self, triggered=self.editor.selectAll, shortcut=QKeySequence.SelectAll)) layoutV.addWidget(bar) layoutV.addWidget(self.tbf) layoutV.addLayout(layoutH) ### main window mq = QWidget(self) mq.setLayout(layoutV) self.setCentralWidget(mq) # Event Filter ... self.installEventFilter(self) self.editor.setFocus() self.cursor = QTextCursor() self.editor.setPlainText("hello") self.editor.moveCursor(self.cursor.End) self.editor.document().modificationChanged.connect( self.setWindowModified) # Brackets ExtraSelection ... self.left_selected_bracket = QTextEdit.ExtraSelection() self.right_selected_bracket = QTextEdit.ExtraSelection() def createActions(self): for i in range(self.MaxRecentFiles): self.recentFileActs.append( QAction(self, visible=False, triggered=self.openRecentFile)) def openRecentFile(self): action = self.sender() if action: if (self.maybeSave()): self.openFileOnStart(action.data()) ### New File def newFile(self): if self.maybeSave(): self.editor.clear() self.editor.setPlainText("") self.filename = "" self.setModified(False) self.editor.moveCursor(self.cursor.End) ### open File def openFileOnStart(self, path=None): if path: inFile = QFile(path) if inFile.open(QFile.ReadWrite | QFile.Text): text = inFile.readAll() try: # Python v3. text = str(text, encoding='utf8') except TypeError: # Python v2. text = str(text) self.editor.setPlainText(text) self.filename = path self.setModified(False) self.fname = QFileInfo(path).fileName() self.setWindowTitle(self.fname + "[*]") self.document = self.editor.document() self.setCurrentFile(self.filename) ### open File def openFile(self, path=None): if self.maybeSave(): if not path: path, _ = QFileDialog.getOpenFileName( self, "Open File", QDir.homePath() + "/Documents/", "Text Files (*.txt *.csv *.py);;All Files (*.*)") if path: inFile = QFile(path) if inFile.open(QFile.ReadWrite | QFile.Text): text = inFile.readAll() try: # Python v3. text = str(text, encoding='utf8') except TypeError: # Python v2. text = str(text) self.editor.setPlainText(text) self.filename = path self.setModified(False) self.fname = QFileInfo(path).fileName() self.setWindowTitle(self.fname + "[*]") self.document = self.editor.document() self.setCurrentFile(self.filename) def fileSave(self): if (self.filename != ""): file = QFile(self.filename) print(self.filename) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning( self, "Error", "Cannot write file %s:\n%s." % (self.filename, file.errorString())) return outstr = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) outstr << self.editor.toPlainText() QApplication.restoreOverrideCursor() self.setModified(False) self.fname = QFileInfo(self.filename).fileName() self.setWindowTitle(self.fname + "[*]") self.setCurrentFile(self.filename) else: self.fileSaveAs() ### save File def fileSaveAs(self): fn, _ = QFileDialog.getSaveFileName(self, "Save as...", self.filename, "Python files (*.py)") if not fn: print("Error saving") return False lfn = fn.lower() if not lfn.endswith('.py'): fn += '.py' self.filename = fn self.fname = os.path.splitext(str(fn))[0].split("/")[-1] return self.fileSave() def closeEvent(self, e): if self.maybeSave(): e.accept() else: e.ignore() ### ask to save def maybeSave(self): if not self.isModified(): return True if self.filename.startswith(':/'): return True ret = QMessageBox.question(self, "Message", "<h4><p>The document was modified.</p>\n" \ "<p>Do you want to save changes?</p></h4>", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if ret == QMessageBox.Yes: if self.filename == "": self.fileSaveAs() return False else: self.fileSave() return True if ret == QMessageBox.Cancel: return False return True def findText(self): ft = self.findfield.text() if self.editor.find(ft): return else: self.editor.moveCursor(1) if self.editor.find(ft): self.editor.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor) def handleQuit(self): print("Goodbye ...") app.quit() def set_numbers_visible(self, value=True): self.numbers.setVisible(False) def match_left(self, block, character, start, found): map = {'{': '}', '(': ')', '[': ']'} while block.isValid(): data = block.userData() if data is not None: braces = data.braces N = len(braces) for k in range(start, N): if braces[k].character == character: found += 1 if braces[k].character == map[character]: if not found: return braces[k].position + block.position() else: found -= 1 block = block.next() start = 0 def match_right(self, block, character, start, found): map = {'}': '{', ')': '(', ']': '['} while block.isValid(): data = block.userData() if data is not None: braces = data.braces if start is None: start = len(braces) for k in range(start - 1, -1, -1): if braces[k].character == character: found += 1 if braces[k].character == map[character]: if found == 0: return braces[k].position + block.position() else: found -= 1 block = block.previous() start = None # ''' cursor = self.editor.textCursor() block = cursor.block() data = block.userData() previous, next = None, None if data is not None: position = cursor.position() block_position = cursor.block().position() braces = data.braces N = len(braces) for k in range(0, N): if braces[k].position == position - block_position or braces[ k].position == position - block_position - 1: previous = braces[k].position + block_position if braces[k].character in ['{', '(', '[']: next = self.match_left(block, braces[k].character, k + 1, 0) elif braces[k].character in ['}', ')', ']']: next = self.match_right(block, braces[k].character, k, 0) if next is None: next = -1 if next is not None and next > 0: if next == 0 and next >= 0: format = QTextCharFormat() cursor.setPosition(previous) cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) format.setBackground(QColor('white')) self.left_selected_bracket.format = format self.left_selected_bracket.cursor = cursor cursor.setPosition(next) cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) format.setBackground(QColor('white')) self.right_selected_bracket.format = format self.right_selected_bracket.cursor = cursor # ''' def paintEvent(self, event): highlighted_line = QTextEdit.ExtraSelection() highlighted_line.format.setBackground(lineHighlightColor) highlighted_line.format.setProperty(QTextFormat.FullWidthSelection, QVariant(True)) highlighted_line.cursor = self.editor.textCursor() highlighted_line.cursor.clearSelection() self.editor.setExtraSelections([ highlighted_line, self.left_selected_bracket, self.right_selected_bracket ]) def document(self): return self.editor.document def isModified(self): return self.editor.document().isModified() def setModified(self, modified): self.editor.document().setModified(modified) def setLineWrapMode(self, mode): self.editor.setLineWrapMode(mode) def clear(self): self.editor.clear() def setPlainText(self, *args, **kwargs): self.editor.setPlainText(*args, **kwargs) def setDocumentTitle(self, *args, **kwargs): self.editor.setDocumentTitle(*args, **kwargs) def set_number_bar_visible(self, value): self.numbers.setVisible(value) def replaceAll(self): print("replacing all") oldtext = self.editor.document().toPlainText() newtext = oldtext.replace(self.findfield.text(), self.replacefield.text()) self.editor.setPlainText(newtext) self.setModified(True) def replaceOne(self): print("replacing all") oldtext = self.editor.document().toPlainText() newtext = oldtext.replace(self.findfield.text(), self.replacefield.text(), 1) self.editor.setPlainText(newtext) self.setModified(True) def setCurrentFile(self, fileName): self.curFile = fileName if self.curFile: self.setWindowTitle("%s - Recent Files" % self.strippedName(self.curFile)) else: self.setWindowTitle("Recent Files") settings = QSettings('Axel Schneider', 'PTEdit') files = settings.value('recentFileList') try: files.remove(fileName) except ValueError: pass files.insert(0, fileName) del files[self.MaxRecentFiles:] settings.setValue('recentFileList', files) for widget in QApplication.topLevelWidgets(): if isinstance(widget, myEditor): widget.updateRecentFileActions() def updateRecentFileActions(self): mytext = "" settings = QSettings('Axel Schneider', 'PTEdit') files = settings.value('recentFileList') files = files if files else [] numRecentFiles = min(len(files), self.MaxRecentFiles) for i in range(numRecentFiles): text = "&%d %s" % (i + 1, self.strippedName(files[i])) self.recentFileActs[i].setText(text) self.recentFileActs[i].setData(files[i]) self.recentFileActs[i].setVisible(True) for j in range(numRecentFiles, self.MaxRecentFiles): self.recentFileActs[j].setVisible(False) self.separatorAct.setVisible((numRecentFiles > 0)) def clearRecentFileList(self, fileName): self.rmenu.clear() def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName()