class GestorPortapapelesVentana(QMainWindow): def __init__(self): super().__init__() self.inicializarGui() def inicializarGui(self): self.setWindowTitle('Gestor Portapapeles') self.setFixedSize(400, 300) self.txt_texto_fuente = QPlainTextEdit(self) self.txt_texto_fuente.move(30, 10) self.txt_texto_fuente.setFixedWidth(340) self.txt_texto_fuente.setFixedHeight(100) self.btn_copiar = QPushButton('Copiar', self) self.btn_copiar.move(30, 110) self.btn_copiar.setFixedWidth(340) self.btn_copiar.clicked.connect(self.copiar) self.txt_texto_destino = QPlainTextEdit(self) self.txt_texto_destino.move(30, 150) self.txt_texto_destino.setFixedWidth(340) self.txt_texto_destino.setFixedHeight(100) self.btn_pegar = QPushButton('Pegar', self) self.btn_pegar.move(30, 250) self.btn_pegar.setFixedWidth(340) self.btn_pegar.clicked.connect(self.pegar) def copiar(self): self.txt_texto_fuente.copy() def pegar(self): self.txt_texto_destino.paste()
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 App(QMainWindow): ''' The main Qt application for the writer. ''' def __init__(self): super().__init__() self.working_file_name = '' self.InitUI() def InitUI(self): # Set up window & settings self.setMinimumSize(300, 200) self.resize(800, 600) self.move(400, 100) self.setWindowTitle(WINDOW_TITLE) # == Widgets and Layout == # Create the central widget self.widget = QWidget(self) self.widget.setStyleSheet('background-color:' + BACKGROUND_COLOR + ';') # Create default text font self.input_font = get_monospaced_font(FONT_SIZE) # Create info bar font self.info_font = get_monospaced_font(INFO_FONT_SIZE) # Create LineEdit for the top info bar # This bar is for displaying info about # the current file self.top_info_bar = QLineEdit(self.widget) self.top_info_bar.setReadOnly(True) self.top_info_bar.setMaximumWidth(MAXIMUM_WIDTH) # Set font and style self.top_info_bar.setStyleSheet('qproperty-alignment: AlignCenter;') self.top_info_bar.setFont(self.info_font) self.top_info_bar.setFrame(QFrame.NoFrame) self.update_top_info_bar() # Create text feild self.center_text = QPlainTextEdit(self.widget) self.center_text.setMaximumWidth(MAXIMUM_WIDTH) self.center_text.setFont(self.input_font) # Disable frame style self.center_text.setFrameStyle(QFrame.NoFrame) # Text feild custom context menu self.center_text.setContextMenuPolicy(Qt.CustomContextMenu) self.center_text.customContextMenuRequested.connect( self.context_menu_event) # Create VBox and set it as layout self.widget.setLayout(QVBoxLayout()) # Layout settings self.widget.layout().addWidget(self.top_info_bar) self.widget.layout().addWidget(self.center_text) self.widget.layout().setContentsMargins(0, 0, 0, 0) self.widget.layout().setAlignment(Qt.AlignCenter) self.setCentralWidget(self.widget) # Init keyboard shortcuts self.InitKeyboard() def InitKeyboard(self): ''' Init keyboard shortcuts ''' # == KEYBOARD SHORTCUTS == # Save, (Ctrl+S) self.save_keyboard_shortcut = QShortcut(QKeySequence('Ctrl+S'), self) self.save_keyboard_shortcut.activated.connect(self.save_text_file) self.saveas_keyboard_shortcut = QShortcut( QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_S), self) self.saveas_keyboard_shortcut.activated.connect(self.saveas_text_file) # Open file (Ctrl+O) self.open_keyboard_shortcut = QShortcut(QKeySequence('Ctrl+O'), self) self.open_keyboard_shortcut.activated.connect(self.open_text_file) # Export to browser self.export_keyboard_shortcut = QShortcut( QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_E), self) self.export_keyboard_shortcut.activated.connect(self.export_to_browser) # Fullscreen toggle (F11) self.fullscreen_keyboard_shortcut = QShortcut(QKeySequence(Qt.Key_F11), self) self.fullscreen_keyboard_shortcut.activated.connect( self.toggle_fullscreen) def save_text_file(self): ''' Saves the written text to a file ''' text_to_save = self.center_text.toPlainText() if self.working_file_name: with open(self.working_file_name, 'w', encoding='utf-8') as save_file: save_file.write(text_to_save) else: name = QFileDialog.getSaveFileName( self, 'Save as...', self.working_file_name, 'All Files (*);;Text Files (*.txt)')[0] if name: with open(name, 'w', encoding='utf-8') as save_file: save_file.write(text_to_save) # Show the new file path self.working_file_name = name # Update info bar self.update_top_info_bar() def saveas_text_file(self): # Set file name to '' to save as new file self.working_file_name = '' self.save_text_file() def open_text_file(self): ''' Opens a file and ''' name = QFileDialog.getOpenFileName( self, 'Open a File', self.working_file_name, 'All Files (*);;Text Files (*.txt);;HTML Files (*.html)')[0] if name: with open(name, 'r', encoding='utf-8') as new_file: new_text = new_file.read() self.center_text.setPlainText(new_text) # Set current file name to the opened one. self.working_file_name = name # Update info bar self.update_top_info_bar() def export_to_browser(self): # Grab the raw markdown text markdown_text = self.center_text.toPlainText() # Convert it to html using the markdown module markdown_html = markdown.markdown(markdown_text) # Create tempfile for the html # The tuple consists of file descriptor and path temp_file_descriptor, temp_file_path = tempfile.mkstemp(suffix='.html', text=True) # Write html to temp file os.write(temp_file_descriptor, markdown_html.encode(encoding='utf-8')) # Open the temp file in the browser webbrowser.open('file://' + os.path.realpath(temp_file_path)) # Close the temp file os.close(temp_file_descriptor) def context_menu_event(self, event): ''' This function defines the context menu that appears on right-click. ''' context_menu = QMenu(self) copy_action = context_menu.addAction( 'Copy\t(Ctrl+C)') # Copy to clipboard paste_action = context_menu.addAction( 'Paste\t(Ctrl+V)') # Paste clipboard # Save file save_action = context_menu.addAction('Save\t(Ctrl+S)') # Save file as... save_as_action = context_menu.addAction('Save as...\t(Ctrl+Shift+S)') # Open file open_action = context_menu.addAction('Open file\t(Ctrl+O)') # Export and open in browser export_action = context_menu.addAction( 'Export to browser\t(Ctrl+Shift+E)') action = context_menu.exec_(self.center_text.mapToGlobal(event)) # Copy and paste actions if action == copy_action: self.center_text.copy() elif action == paste_action: self.center_text.paste() # File managment actions elif action == save_as_action: self.saveas_text_file() elif action == save_action: self.save_text_file() elif action == open_action: self.open_text_file() # Markdown actions elif action == export_action: self.export_to_browser() def update_top_info_bar(self): if self.working_file_name != '': self.top_info_bar.setText(self.working_file_name) else: self.top_info_bar.setText('New File') def toggle_fullscreen(self): ''' Toggles fullscreen on and off ''' if self.isFullScreen(): self.showNormal() else: self.showFullScreen()
class MainWindow12(QMainWindow): def __init__(self): super(MainWindow12, self).__init__() self.commandslist = [] self.tracker = 0 os.chdir(os.path.expanduser(path12)) # print(os.getcwd()) self.name = (str(getpass.getuser()) + "@" + str(socket.gethostname()) + ":" + str(os.getcwd()) + "$ ") self.setWindowTitle('PyQt5Terminal') self.setWindowIcon(QIcon.fromTheme("terminal-emulator")) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) self.process.readyRead.connect(self.dataReady) self.process.readyReadStandardError.connect( self.onReadyReadStandardError) self.process.readyReadStandardOutput.connect( self.onReadyReadStandardOutput) self.process.finished.connect(self.isFinished) self.process.setWorkingDirectory(os.getcwd()) self.createStatusBar() self.commandfield = QPlainTextEdit() self.commandfield.setLineWrapMode(QPlainTextEdit.NoWrap) self.commandfield.setFixedHeight(44) self.commandfield.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.commandfield.setAcceptDrops(True) self.cursor = self.commandfield.textCursor() self.textWindow = QPlainTextEdit(self) #self.setStyleSheet(mystylesheet(self)) self.textWindow.setReadOnly(True) layout = QVBoxLayout() layout.addWidget(self.textWindow) layout.addWidget(self.commandfield) self.wid = QWidget() self.wid.setLayout(layout) self.setCentralWidget(self.wid) self.setGeometry(0, 0, 600, 500) self.commandfield.setPlainText(self.name) self.cursorEnd() self.commandfield.setFocus() self.copySelectedTextAction = QAction(QIcon.fromTheme("edit-copy"), "Copy", shortcut="Shift+Ctrl+c", triggered=self.copyText) self.textWindow.addAction(self.copySelectedTextAction) self.pasteTextAction = QAction(QIcon.fromTheme("edit-paste"), "Copy", shortcut="Shift+Ctrl+v", triggered=self.pasteText) self.commandfield.addAction(self.pasteTextAction) self.cancelAction = QAction("Cancel", shortcut="Ctrl+c", triggered=self.killProcess) self.textWindow.addAction(self.cancelAction) self.commandfield.installEventFilter(self) # self.textWindow.installEventFilter(self) QApplication.setCursorFlashTime(1000) self.cursorEnd() print(self.process.workingDirectory()) self.settings = QSettings("QTerminal", "QTerminal") self.readSettings() def closeEvent(self, e): self.writeSettings() def cursorEnd(self): self.name = (str(getpass.getuser()) + "@" + str(socket.gethostname()) + ":" + str(os.getcwd()) + "$ ") self.commandfield.setPlainText(self.name) cursor = self.commandfield.textCursor() cursor.movePosition(11, 0) self.commandfield.setTextCursor(cursor) self.commandfield.setFocus() def eventFilter(self, source, event): if source == self.commandfield: if (event.type() == QEvent.DragEnter): event.accept() return True elif (event.type() == QEvent.Drop): print('Drop') self.setDropEvent(event) return True elif (event.type() == QEvent.KeyPress): cursor = self.commandfield.textCursor() # print('key press:', (event.key(), event.text())) if event.key() == Qt.Key_Backspace: if cursor.positionInBlock() <= len(self.name): return True else: return False elif event.key() == Qt.Key_Return: self.run() return True elif event.key() == Qt.Key_Left: if cursor.positionInBlock() <= len(self.name): return True else: return False elif event.key() == Qt.Key_Delete: if cursor.positionInBlock() <= len(self.name) - 1: return True else: return False elif event.modifiers() == Qt.ControlModifier and event.key( ) == Qt.Key_C: self.killProcess() return True elif event.key() == Qt.Key_Up: try: if self.tracker != 0: cursor.select(QTextCursor.BlockUnderCursor) cursor.removeSelectedText() self.commandfield.appendPlainText(self.name) self.commandfield.insertPlainText( self.commandslist[self.tracker]) self.tracker -= 1 except IndexError: self.tracker = 0 return True elif event.key() == Qt.Key_Down: try: if self.tracker != 0: cursor.select(QTextCursor.BlockUnderCursor) cursor.removeSelectedText() self.commandfield.appendPlainText(self.name) self.commandfield.insertPlainText( self.commandslist[self.tracker]) self.tracker += 1 except IndexError: self.tracker = 0 return True else: return False else: return False else: return False def copyText(self): self.textWindow.copy() def pasteText(self): self.commandfield.paste() def killProcess(self): print("cancelled") self.process.kill() self.textWindow.appendPlainText("cancelled") self.cursorEnd() def createStatusBar(self): sysinfo = QSysInfo() myMachine = "current CPU Architecture: " + sysinfo.currentCpuArchitecture( ) + " *** " + sysinfo.prettyProductName( ) + " *** " + sysinfo.kernelType() + " " + sysinfo.kernelVersion() self.statusBar().showMessage(myMachine, 0) def setDropEvent(self, event): self.commandfield.setFocus() if event.mimeData().hasUrls(): f = str(event.mimeData().urls()[0].toLocalFile()) print("is file:", f) if " " in f: self.commandfield.insertPlainText("'{}'".format(f)) else: self.commandfield.insertPlainText(f) event.accept() elif event.mimeData().hasText(): ft = event.mimeData().text() print("is text:", ft) if " " in ft: self.commandfield.insertPlainText("'{}'".format(ft)) else: self.commandfield.insertPlainText(ft) else: event.ignore() def run(self): print("started") cli = [] cmd = "" t = "" self.textWindow.setFocus() self.textWindow.appendPlainText(self.commandfield.toPlainText()) cli = shlex.split(self.commandfield.toPlainText().replace( self.name, '').replace("'", '"'), posix=False) cmd = str(cli[0]) ### is the executable if cmd == "exit": quit() elif cmd == "cd": del cli[0] path = " ".join(cli) os.chdir(os.path.abspath(path)) self.process.setWorkingDirectory(os.getcwd()) print("workingDirectory:", self.process.workingDirectory()) self.cursorEnd() else: self.process.setWorkingDirectory(os.getcwd()) print("workingDirectory", self.process.workingDirectory()) del cli[0] if (QStandardPaths.findExecutable(cmd)): self.commandslist.append( self.commandfield.toPlainText().replace(self.name, "")) print("command", cmd, "found") t = " ".join(cli) if self.process.state() != 2: #self.process.waitForStarted() self.process.waitForReadyRead() self.process.waitForFinished() if "|" in t or ">" in t or "<" in t: print("special characters") self.process.start('sh -c "' + cmd + ' ' + t + '"') print("running", ('sh -c "' + cmd + ' ' + t + '"')) else: self.process.start(cmd + " " + t) print("running", (cmd + " " + t)) else: print("command not found ...") self.textWindow.appendPlainText("command not found ...") self.cursorEnd() def dataReady(self): out = "" try: out = str(self.process.readAll(), encoding='utf8').rstrip() except TypeError: out = str(self.process.readAll()).rstrip() self.textWindow.moveCursor(self.cursor.Start) ### changed self.textWindow.appendPlainText(out) def onReadyReadStandardError(self): self.error = self.process.readAllStandardError().data().decode() self.textWindow.appendPlainText(self.error.strip('\n')) self.cursorEnd() def onReadyReadStandardOutput(self): self.result = self.process.readAllStandardOutput().data().decode() self.textWindow.appendPlainText(self.result.strip('\n')) self.cursorEnd() self.state = self.process.state() def isFinished(self): print("finished") self.name = (str(getpass.getuser()) + "@" + str(socket.gethostname()) + ":" + str(os.getcwd()) + "$ ") self.commandfield.setPlainText(self.name) self.cursorEnd() def readSettings(self): if self.settings.contains("commands"): self.commandslist = self.settings.value("commands") if self.settings.contains("pos"): pos = self.settings.value("pos", QPoint(200, 200)) self.move(pos) if self.settings.contains("size"): size = self.settings.value("size", QSize(400, 400)) self.resize(size) def writeSettings(self): self.settings.setValue("commands", self.commandslist) self.settings.setValue("pos", self.pos()) self.settings.setValue("size", self.size())
class LogWindow(QMainWindow): emit_msg = pyqtSignal(str) def __init__(self, level: int = logging.INFO, window_title: str = "Python log", logger: logging.Logger = None, min_width: int = 800, min_height: int = 400, maximum_block_count: int = 1000) -> None: super().__init__() self.setStyleSheet(LOGEDIT_STYLESHEET) self.handler = HtmlColorHandler(self.log_message, level) self.may_close = False self.set_may_close(self.may_close) self.setWindowTitle(window_title) if min_width: self.setMinimumWidth(min_width) if min_height: self.setMinimumHeight(min_height) log_group = StyledQGroupBox("Log") log_layout_1 = QVBoxLayout() log_layout_2 = QHBoxLayout() self.log = QPlainTextEdit() # QPlainTextEdit better than QTextEdit because it supports # maximumBlockCount while still allowing HTML (via appendHtml, # not insertHtml). self.log.setReadOnly(True) self.log.setLineWrapMode(QPlainTextEdit.NoWrap) self.log.setMaximumBlockCount(maximum_block_count) 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() log_layout_1.addWidget(self.log) log_layout_1.addLayout(log_layout_2) log_group.setLayout(log_layout_1) main_widget = QWidget(self) self.setCentralWidget(main_widget) main_layout = QVBoxLayout(main_widget) main_layout.addWidget(log_group) self.emit_msg.connect(self.log_internal) if logger: logger.addHandler(self.get_handler()) def get_handler(self) -> logging.Handler: return self.handler def set_may_close(self, may_close: bool) -> None: # log.debug("LogWindow: may_close({})".format(may_close)) self.may_close = may_close # return if may_close: self.setWindowFlags(self.windowFlags() | Qt.WindowCloseButtonHint) else: self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) self.show() # ... or it will be hidden (in a logical not a real way!) by # setWindowFlags(), and thus mess up the logic for the whole Qt app # exiting (since qt_app.exec_() runs until there are no more windows # being shown). 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) # noinspection PyPep8Naming def closeEvent(self, event: QCloseEvent) -> None: """Trap exit.""" if not self.may_close: # log.debug("LogWindow: ignore closeEvent") event.ignore() else: # log.debug("LogWindow: accept closeEvent") event.accept() def log_message(self, html: str) -> None: # Jump threads via a signal self.emit_msg.emit(html) @pyqtSlot(str) def log_internal(self, html: str) -> None: # self.log.moveCursor(QTextCursor.End) # self.log.insertHtml(html) self.log.appendHtml(html) # self.scroll_to_end_of_log() # ... unnecessary; if you're at the end, it scrolls, and if you're at # the top, it doesn't bug you. @pyqtSlot() def exit(self) -> None: # log.debug("LogWindow: exit") self.may_close = True # closed = QMainWindow.close(self) # log.debug("closed: {}".format(closed)) QMainWindow.close(self) @pyqtSlot() def may_exit(self) -> None: # log.debug("LogWindow: may_exit") self.set_may_close(True)
class TextboxWindow(QDialog): def __init__(self, text="", file_extension="txt", file_type="Text file", encoding="utf-8"): super().__init__() self.text = text self.file_extension = file_extension self.file_type = file_type self.encoding = encoding self.control_held = False self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) vbox = QVBoxLayout() self.macrotext = QPlainTextEdit() self.macrotext.setPlainText(text) self.macrotext.selectAll() self.btn_apply = QToolButton() self.btn_apply.setText(tr("TextboxWindow", "Apply")) self.btn_apply.setToolButtonStyle(Qt.ToolButtonTextOnly) self.btn_apply.clicked.connect(self.on_apply) self.btn_cancel = QToolButton() self.btn_cancel.setText(tr("TextboxWindow", "Cancel")) self.btn_cancel.setToolButtonStyle(Qt.ToolButtonTextOnly) self.btn_cancel.clicked.connect(self.on_cancel) self.btn_copy = QToolButton() self.btn_copy.setText(tr("TextboxWindow", "Copy")) self.btn_copy.setToolButtonStyle(Qt.ToolButtonTextOnly) self.btn_copy.clicked.connect(self.on_copy) self.btn_paste = QToolButton() self.btn_paste.setText(tr("TextboxWindow", "Paste")) self.btn_paste.setToolButtonStyle(Qt.ToolButtonTextOnly) self.btn_paste.clicked.connect(self.on_paste) bottom_buttons = QHBoxLayout() bottom_buttons.setContentsMargins(0, 0, 0, 0) bottom_buttons.addWidget(self.btn_copy) bottom_buttons.addWidget(self.btn_paste) bottom_buttons.addSpacing(15) bottom_buttons.addStretch() bottom_buttons.addWidget(self.btn_apply) bottom_buttons.addWidget(self.btn_cancel) self.bottom_widget = QWidget() self.bottom_widget.setLayout(bottom_buttons) vbox.addWidget(self.macrotext, stretch=1) vbox.addWidget(self.bottom_widget) self.setLayout(vbox) self.setWindowFlags(self.windowFlags()) def on_apply(self): self.accept() def on_cancel(self): self.reject() def on_select_all(self): self.macrotext.selectAll() def on_copy(self): self.macrotext.copy() def on_paste(self): self.macrotext.paste() def on_export(self): dialog = QFileDialog() dialog.setDefaultSuffix(self.file_extension) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setNameFilters( ["{} (*.{})".format(self.file_type, self.file_extension)]) if dialog.exec_() == QDialog.Accepted: with open(dialog.selectedFiles()[0], "wb") as outf: outf.write(self.macrotext.toPlainText().encode(self.encoding)) def on_import(self): dialog = QFileDialog() dialog.setDefaultSuffix(self.file_extension) dialog.setAcceptMode(QFileDialog.AcceptOpen) dialog.setNameFilters( ["{} (*.{})".format(self.file_type, self.file_extension)]) if dialog.exec_() == QDialog.Accepted: with open(dialog.selectedFiles()[0], "rb") as inf: self.macrotext.setPlainText(inf.read().decode(self.encoding)) def getText(self): return self.macrotext.toPlainText() def keyPressEvent(self, ev): if ev.key() == Qt.Key_Escape: self.reject() if ev.key() == Qt.Key_Control: self.control_held = True if self.control_held: if ev.key() == Qt.Key_O: self.on_import() if ev.key() == Qt.Key_S: self.on_export() def keyReleaseEvent(self, ev): if ev.key() == Qt.Key_Control: self.control_held = False