Search code examples
pythonpyqt5mitmproxy

Python PyQt5 Add List Item from another Py-File


when clicking a button in the PyQt5 Ui iam starting a mitmproxy. When the proxy is started, i try to change a listWidget with Data from the Proxy.

main.py

    import sys
    
    from PyQt5 import QtWidgets
    from PyQt5.QtWidgets import QApplication, QMainWindow
    from MainWindow import Ui_MainWindow
    
    class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    
        def __init__(self, *args, obj=None, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
            self.setupUi(self)
    
            self.pushButton.clicked.connect(self.Start)
    
        def Start(self):
            x = threading.Thread(target=self.StartMITM)
            x.start()
    
        def StartMITM(self):
            os.system("mitmweb -s mitmproxy.py -q --no-web-open-browser")
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        win = MainWindow()
        win.show()
        sys.exit(app.exec_())

mitmproxy.py - this is the error part

    from mitmproxy import http
    from main import MainWindow
    
    def response(flow):
        MainWindow.listWidget.addItem(flow.request.pretty_url)

Can I connect to the Widgets from another File?


Solution

  • It has 2 independent processes: The GUI and the mitmproxy script. And communicating both processes does not imply importing modules since you would be creating another widget, also objects should not be accessed through classes (I recommend you check your basic python notes).

    In this the solution is to use some Inter process communication (IPC), in this case you can use Qt Remote Objects (QtRO):

    main.py

    from functools import cached_property
    
    from PyQt5 import QtCore, QtRemoteObjects, QtWidgets
    
    
    class Bridge(QtCore.QObject):
        messageChanged = QtCore.pyqtSignal(str)
    
        @QtCore.pyqtSlot(str)
        def add_message(self, message):
            self.messageChanged.emit(message)
    
    
    class MitmwebManager(QtCore.QObject):
        logChanged = QtCore.pyqtSignal(str)
    
        def __init__(self, parent=None):
            super().__init__(parent)
            # self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
            self.process.readyReadStandardOutput.connect(self.handle_log)
            self.process.setProgram("mitmweb")
    
        @cached_property
        def process(self):
            return QtCore.QProcess()
    
        def start(self, arguments):
            self.process.setArguments(arguments)
            self.process.start()
    
        def stop(self):
            self.process.kill()
    
        def handle_log(self):
            data = self.process.readAllStandardOutput()
            codec = QtCore.QTextCodec.codecForName("UTF-8")
            message = codec.toUnicode(data)
            self.logChanged.emit(message)
    
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.button = QtWidgets.QPushButton("Start", checkable=True)
            self.listwidget = QtWidgets.QListWidget()
            self.logview = QtWidgets.QPlainTextEdit(readOnly=True)
    
            central_widget = QtWidgets.QWidget()
            self.setCentralWidget(central_widget)
            lay = QtWidgets.QGridLayout(central_widget)
            lay.addWidget(self.button, 0, 0, 1, 2)
            lay.addWidget(self.listwidget, 1, 0)
            lay.addWidget(self.logview, 1, 1)
    
            self.register_node = QtRemoteObjects.QRemoteObjectRegistryHost(
                QtCore.QUrl("local:registry")
            )
            self.source_node = QtRemoteObjects.QRemoteObjectHost(
                QtCore.QUrl("local:replica"), QtCore.QUrl("local:registry")
            )
            self.source_node.enableRemoting(self.bridge, "bridge")
    
            self.button.toggled.connect(self.handle_toggled)
            self.mitmweb.logChanged.connect(self.handle_log_changed)
            self.bridge.messageChanged.connect(self.handle_message_changed)
    
            self.resize(640, 480)
    
        @cached_property
        def mitmweb(self):
            return MitmwebManager()
    
        @cached_property
        def bridge(self):
            return Bridge()
    
        def handle_toggled(self, checked):
            if checked:
                self.mitmweb.start(["-s", "script.py", "--no-web-open-browser"])
                self.button.setText("Stop")
            else:
                self.mitmweb.stop()
                self.button.setText("Start")
    
        def handle_log_changed(self, message):
            self.logview.insertPlainText(message)
    
        def closeEvent(self, event):
            super().closeEvent(event)
            self.mitmweb.stop()
    
        def handle_message_changed(self, message):
            item = QtWidgets.QListWidgetItem(message)
            self.listwidget.addItem(item)
    
    
    def main():
        app = QtWidgets.QApplication([])
    
        view = MainWindow()
        view.show()
    
        app.exec_()
    
    
    if __name__ == "__main__":
        main()
    

    script.py

    from mitmproxy import http
    
    from PyQt5 import QtCore, QtRemoteObjects
    
    
    class Bridge(QtCore.QObject):
        def __init__(self, bridge, parent=None):
            super().__init__(parent)
            self._message = ""
            self._bridge = bridge
    
            self.bridge.initialized.connect(self.handle_initialized)
    
        @property
        def bridge(self):
            return self._bridge
    
        @property
        def message(self):
            return self._message
    
        def send_message(self, message):
            self._message = message
    
        @QtCore.pyqtSlot()
        def handle_initialized(self):
            self.bridge.add_message(self._message)
            QtCore.QTimer.singleShot(0, QtCore.QCoreApplication.quit)
    
    
    def send_qt(message):
        qt_app = QtCore.QCoreApplication([])
        replica_node = QtRemoteObjects.QRemoteObjectNode(QtCore.QUrl("local:registry"))
        replica_bridge = replica_node.acquireDynamic("bridge")
        bridge = Bridge(replica_bridge)
        bridge.send_message(message)
        qt_app.exec_()
    
    
    def response(flow):
        send_qt(flow.request.pretty_url)