Skip to content

Instantly share code, notes, and snippets.

@daegontaven daegontaven/MVCE_main.py Secret
Last active Sep 12, 2017

Embed
What would you like to do?
PyQt5 Python Console
import sys
from code import InteractiveConsole
from queue import Queue, Empty, Full
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot, QThread, QObject, pyqtSignal, QTimer
from PyQt5.QtGui import QTextOption, QTextCursor
from PyQt5.QtWidgets import QApplication
__author__ = "daegontaven"
__copyright__ = "daegontaven"
__license__ = "gpl3"
class ConsoleStream(QObject):
"""
Custom StreamIO class that handles when send data
to console_log
"""
written = pyqtSignal(str)
def __init__(self, parent=None, buffer=False):
super(ConsoleStream, self).__init__(parent)
self.buffer = buffer
if buffer:
self.buffer = Queue(500)
# SingleShot Flush
self.timer = QTimer(self)
self.timer.setInterval(100)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.process)
def write(self, string):
"""
Overrides the parent write method and emits a signal
meant to be received by interpreters. If the buffer
is enabled, then it will add the string to a Queue
where it will wait to be emitted.
:param string: single write output from stdout
"""
if self.buffer:
if self.buffer.empty():
self.timer.start()
try:
self.buffer.put(string, block=False)
self.timer.start()
except Full:
self.flush()
self.timer.stop()
self.write(string)
else:
self.written.emit(string)
def get(self):
"""
Tries to get and emit string from the buffer
if it's available.
"""
try:
string = self.buffer.get(block=False)
self.written.emit(string)
except Empty:
pass
def process(self):
"""
Determines when to flush the buffer.
"""
if self.buffer.not_empty:
self.flush()
self.timer.stop()
elif self.buffer.empty():
self.timer.stop()
def flush(self):
"""
Flushes the buffer.
"""
lines = []
for line in range(self.buffer.qsize()):
try:
line = self.buffer.get(block=False)
lines.append(line)
continue
except Empty:
break
if lines:
self.written.emit(''.join(lines))
class PythonInterpreter(QObject, InteractiveConsole):
"""
A reimplementation of the builtin InteractiveConsole to
work with threads.
"""
push_command = pyqtSignal(str)
multi_line = pyqtSignal(bool)
output = pyqtSignal(str)
error = pyqtSignal(str)
def __init__(self, parent=None):
super(PythonInterpreter, self).__init__(parent)
self.locals = {}
InteractiveConsole.__init__(self, self.locals)
self.stream = ConsoleStream(self, buffer=True)
self.push_command.connect(self.command)
def write(self, string):
self.error.emit(string)
def runcode(self, code):
"""
Overrides and captures stdout and stdin from
InteractiveConsole.
"""
sys.stdout = self.stream
sys.stderr = self.stream
sys.excepthook = sys.__excepthook__
result = InteractiveConsole.runcode(self, code)
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
return result
@pyqtSlot(str)
def command(self, command):
"""
:param command: line retrieved from console_input on
returnPressed
"""
result = self.push(command)
self.multi_line.emit(result)
class MainWindow:
"""
The main GUI window. Opens maximized.
"""
def __init__(self):
self.ui = uic.loadUi("main.ui")
self.ui.showMaximized()
# Console Properties
self.ui.console_log.document().setMaximumBlockCount(500)
self.ui.console_log.setWordWrapMode(QTextOption.WrapAnywhere)
self.ps1 = '>>>'
self.ps2 = '...'
self.ui.console_prompt.setText(self.ps1)
# Controls
self.cursor = self.ui.console_log.textCursor()
self.scrollbar = self.ui.console_log.verticalScrollBar()
# Spawn Interpreter
self.thread = QThread()
self.thread.start()
self.interpreter = PythonInterpreter()
self.interpreter.moveToThread(self.thread)
# Interpreter Signals
self.ui.console_input.returnPressed.connect(self.send_console_input)
self.interpreter.stream.written.connect(self.send_console_log)
self.interpreter.error.connect(self.send_console_log)
self.interpreter.multi_line.connect(self.prompt)
def prompt(self, multi_line):
"""
Sets what prompt to use.
"""
if multi_line:
self.ui.console_prompt.setText(self.ps2)
else:
self.ui.console_prompt.setText(self.ps1)
def send_console_input(self):
"""
Send input grabbed from the QLineEdit prompt to the console.
"""
command = self.ui.console_input.text()
self.ui.console_input.clear()
self.interpreter.push_command.emit(str(command))
def send_console_log(self, output):
"""
Set the output from InteractiveConsole in the QTextEdit.
Auto scroll scrollbar.
"""
# Move cursor
self.cursor.movePosition(QTextCursor.End)
self.ui.console_log.setTextCursor(self.cursor)
# Insert text
self.ui.console_log.insertPlainText(output)
# Move scrollbar
self.scrollbar.setValue(self.scrollbar.maximum())
def main():
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>main_window</class>
<widget class="QMainWindow" name="main_window">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<widget class="QWidget" name="central_widget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="console_layout">
<item>
<widget class="QPlainTextEdit" name="console_log">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="console_prompt">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="console_input">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menu_bar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="status_bar"/>
</widget>
<resources/>
<connections/>
</ui>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.