Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Console: Port to Qt #1161

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions MAVProxy/modules/mavproxy_qt_console/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
from MAVProxy.modules.lib import mp_module
from MAVProxy.modules.mavproxy_qt_console.ui_qt_console import Ui_QtConsole
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QTimer
from PySide6.QtGui import QColor
import sys
import threading
from MAVProxy.modules.lib import multiproc
import multiprocessing
import time
from MAVProxy.modules.lib.wxconsole_util import Text, Value
import socket
import errno
from MAVProxy.modules.lib import textconsole

class QtConsoleWindow(QMainWindow):
def __init__(self, parent):
super(QtConsoleWindow, self).__init__()
self._parent = parent
self._ui = Ui_QtConsole()
self._ui.setupUi(self)

self._timer = QTimer(self)
self._timer.timeout.connect(self.update)
self._timer.start(200)

self._ui.actionShow_Map.triggered.connect(self.show_map)

def update(self):
'''Slot called by QTimer at a specified interval'''
if self._parent.close_event.wait(0.001):
self._timer.stop()
self.close()
return

try:
poll_success = self._parent.child_pipe_recv.poll()
if not poll_success:
return
except socket.error as e:
if e.errno == errno.EPIPE:
self._timer.stop()
return
else:
raise e

try:
msg = self._parent.child_pipe_recv.recv()
except EOFError:
self._timer.stop()
return

if isinstance(msg, Text):
self._ui.textEdit.setTextColor(QColor(msg.fg))
self._ui.textEdit.setTextBackgroundColor(QColor(msg.bg))
self._ui.textEdit.append(msg.text)

def show_map(self):
self._parent.child_pipe_send.send("# module load map")

def closeEvent(self, event) -> None:
"""Handles the cross button on the UI"""
if not self._parent.close_event.is_set():
self._parent.child_pipe_send.send("# module unload qt_console")
return super().closeEvent(event)

class QtConsole(textconsole.SimpleConsole):
def __init__(self, mpstate) -> None:
super(QtConsole, self).__init__()
self.mpstate = mpstate
self.parent_pipe_recv, self.child_pipe_send = multiproc.Pipe(duplex=False)
self.child_pipe_recv,self.parent_pipe_send = multiproc.Pipe(duplex=False)

# For quitting cleanly
self.close_event = multiproc.Event()
self.close_event.clear()

# main process in which GUI (child) lives
self.child = multiprocessing.Process(target=self.child_task)
self.child.start()

# This class (parent) doesn't need the child pipes
self.child_pipe_send.close()
self.child_pipe_recv.close()

# Thread that listens to clicks etc. from the GUI
t = threading.Thread(target=self.watch_thread)
t.daemon = True
t.start()

def watch_thread(self):
'''watch for menu events from child'''
from MAVProxy.modules.lib.mp_settings import MPSetting
try:
while True:
msg = self.parent_pipe_recv.recv()
if msg.startswith("#"): # Header for command packet
self.mpstate.functions.process_stdin(msg[2:])
# print(msg)
# if isinstance(msg, win_layout.WinLayout):
# win_layout.set_layout(msg, self.set_layout)
# elif self.menu_callback is not None:
# self.menu_callback(msg)
time.sleep(0.1)
except EOFError:
pass

def child_task(self):
'''Main Process in which the Qt GUI lives'''
self.parent_pipe_send.close() # Good sense to close pipes that are not used by this process
self.parent_pipe_recv.close()
app = QApplication.instance()
if app == None:
app = QApplication()

window = QtConsoleWindow(self)
window.show()
app.exec()

def write(self, text, fg='black', bg='white'):
'''write to the console'''
try:
self.parent_pipe_send.send(Text(text, fg, bg))
except Exception:
pass

def writeln(self, text, fg='black', bg='white'):
'''write to the console with linefeed'''
if not isinstance(text, str):
text = str(text)
self.write(text, fg=fg, bg=bg)

def close(self):
'''close the console'''
self.close_event.set()
if self.child.is_alive():
self.child.join()

class QtConsoleModule(mp_module.MPModule):
def __init__(self, mpstate):
super().__init__(mpstate, "qt_console", "GUI Console (Qt)", public=True, multi_vehicle=True)
self.add_command('qt_console', self.cmd_qt_console, "qt console module", ['add','list','remove'])
self.mpstate.console = QtConsole(mpstate)

def cmd_qt_console(self, args):
pass

def mavlink_packet(self, packet):
# print("Packet recieved")
return super().mavlink_packet(packet)

def unload(self):
'''unload module'''
self.mpstate.console.close()
self.mpstate.console = textconsole.SimpleConsole()

def init(mpstate):
'''initialise module'''
return QtConsoleModule(mpstate)
92 changes: 92 additions & 0 deletions MAVProxy/modules/mavproxy_qt_console/qt_console.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QtConsole</class>
<widget class="QMainWindow" name="QtConsole">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>799</width>
<height>308</height>
</rect>
</property>
<property name="windowTitle">
<string>Qt Console</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QTabWidget" name="tabWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>40</y>
<width>791</width>
<height>221</height>
</rect>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Messages</string>
</attribute>
<widget class="QTextEdit" name="textEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>791</width>
<height>191</height>
</rect>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Tab 2</string>
</attribute>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>799</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFIle">
<property name="title">
<string>MAVProxy</string>
</property>
<addaction name="actionSettings"/>
<addaction name="actionShow_Map"/>
<addaction name="actionShow_HUD"/>
</widget>
<addaction name="menuFIle"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionSettings">
<property name="text">
<string>Settings</string>
</property>
</action>
<action name="actionShow_Map">
<property name="text">
<string>Show Map</string>
</property>
</action>
<action name="actionShow_HUD">
<property name="text">
<string>Show HUD</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
82 changes: 82 additions & 0 deletions MAVProxy/modules/mavproxy_qt_console/ui_qt_console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'qt_console.ui'
##
## Created by: Qt User Interface Compiler version 6.4.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QCursor, QFont, QFontDatabase, QGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QMainWindow, QMenu, QMenuBar,
QSizePolicy, QStatusBar, QTabWidget, QTextEdit,
QWidget)

class Ui_QtConsole(object):
def setupUi(self, QtConsole):
if not QtConsole.objectName():
QtConsole.setObjectName(u"QtConsole")
QtConsole.resize(799, 308)
self.actionSettings = QAction(QtConsole)
self.actionSettings.setObjectName(u"actionSettings")
self.actionShow_Map = QAction(QtConsole)
self.actionShow_Map.setObjectName(u"actionShow_Map")
self.actionShow_HUD = QAction(QtConsole)
self.actionShow_HUD.setObjectName(u"actionShow_HUD")
self.centralwidget = QWidget(QtConsole)
self.centralwidget.setObjectName(u"centralwidget")
self.tabWidget = QTabWidget(self.centralwidget)
self.tabWidget.setObjectName(u"tabWidget")
self.tabWidget.setGeometry(QRect(0, 40, 791, 221))
self.tab = QWidget()
self.tab.setObjectName(u"tab")
self.textEdit = QTextEdit(self.tab)
self.textEdit.setObjectName(u"textEdit")
self.textEdit.setGeometry(QRect(0, 0, 791, 191))
self.textEdit.setReadOnly(True)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QWidget()
self.tab_2.setObjectName(u"tab_2")
self.tabWidget.addTab(self.tab_2, "")
QtConsole.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(QtConsole)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 799, 22))
self.menuFIle = QMenu(self.menubar)
self.menuFIle.setObjectName(u"menuFIle")
QtConsole.setMenuBar(self.menubar)
self.statusbar = QStatusBar(QtConsole)
self.statusbar.setObjectName(u"statusbar")
QtConsole.setStatusBar(self.statusbar)

self.menubar.addAction(self.menuFIle.menuAction())
self.menuFIle.addAction(self.actionSettings)
self.menuFIle.addAction(self.actionShow_Map)
self.menuFIle.addAction(self.actionShow_HUD)

self.retranslateUi(QtConsole)

self.tabWidget.setCurrentIndex(0)


QMetaObject.connectSlotsByName(QtConsole)
# setupUi

def retranslateUi(self, QtConsole):
QtConsole.setWindowTitle(QCoreApplication.translate("QtConsole", u"Qt Console", None))
self.actionSettings.setText(QCoreApplication.translate("QtConsole", u"Settings", None))
self.actionShow_Map.setText(QCoreApplication.translate("QtConsole", u"Show Map", None))
self.actionShow_HUD.setText(QCoreApplication.translate("QtConsole", u"Show HUD", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("QtConsole", u"Messages", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("QtConsole", u"Tab 2", None))
self.menuFIle.setTitle(QCoreApplication.translate("QtConsole", u"MAVProxy", None))
# retranslateUi