Sean Evans
Sean Evans

Reputation: 3

PyQt Signal positional argument disappearing

I'm writing a small application in Python 3.6 and PyQt 5.13.1 to compare file checksums for directories across multiple remote servers. I'm trying to split out the main logic of my application into its own thread, and from that thread use a threadpool to create several more threads to SSH into the remote servers and get information back.

Everything is mostly working, except when the signal I have made for the SSH threads is called, one of the positional arguments ends up missing and I get the following error:

TypeError: ssh_result() missing 1 required positional argument: 'message'

Here is a stripped down version of what I currently have, but is exhibiting the same issue:

import paramiko
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, QThreadPool, QRunnable, pyqtSignal, pyqtSlot, QObject


class SSHWorkerSignals(QObject):
    result = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject')


class SSHWorker(QRunnable):
    def __init__(self):
        super(SSHWorker, self).__init__()
        self.ssh_signals = SSHWorkerSignals()
        self.ssh_server = 'test.server.corp'

    @pyqtSlot()
    def run(self):
        try:
            ssh_client = paramiko.SSHClient()
            ssh_client.load_system_host_keys()
            ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
            ssh_client.connect(self.ssh_server, port=22, username='test', password='test')
            stdin, stdout, stderr = ssh_client.exec_command('uname -a')
            std_out = str(stdout.read())
            self.ssh_signals.result.emit(self.ssh_server, std_out)
        except Exception as e:
            self.ssh_signals.result.emit(self.ssh_server, str(e))
        finally:
            ssh_client.close()


class CompareWorker(QObject):
    done = pyqtSignal()

    def __init__(self):
        super(CompareWorker, self).__init__()
        self.ssh_threadpool = QThreadPool()
        self.ssh_threadpool.setMaxThreadCount(10)

    @pyqtSlot()
    def execute(self):
        ssh_worker = SSHWorker()
        ssh_worker.ssh_signals.result.connect(MainWindow.ssh_result)
        self.ssh_threadpool.start(ssh_worker)


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.button_compare = QPushButton('Compare')
        self.button_compare.clicked.connect(self.compare)
        self.setCentralWidget(self.button_compare)

    def compare(self):
        compare_thread = QThread(self)
        self.compare_worker = CompareWorker()

        compare_thread.started.connect(self.compare_worker.execute)
        self.compare_worker.done.connect(compare_thread.quit)
        self.compare_worker.done.connect(self.compare_worker.deleteLater)

        self.compare_worker.moveToThread(compare_thread)
        compare_thread.start()

    def ssh_result(self, server, message):
        print('server: ', server, ', result: ', message)


if __name__ == '__main__':
    app = QApplication([])

    window = MainWindow()
    window.show()

    app.exec_()

If I change the ssh_result() function so that message is an optional argument (def ssh_result(self, server, message=''):), I can see that the first positional argument is disappearing, leaving what should be the second argument to be the first instead, like so:

server:  [Errno 11001] getaddrinfo failed , result:

Can anyone help me figure out why this is happening?

Upvotes: 0

Views: 834

Answers (1)

eyllanesc
eyllanesc

Reputation: 244301

The main problem is that MainWindow.ssh_result is not a method that belongs to some instance, so the first "self" parameter does not exist. I also don't see the need for the creation of CompareWorker.

Considering the above, the solution is:

# ...
class SSHWorkerSignals(QObject):
    result = pyqtSignal(
        "PyQt_PyObject", "PyQt_PyObject"
    )  # or pyqtSignal(object, object)


class SSHWorker(QRunnable):
    # ...


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.button_compare = QPushButton("Compare")
        self.button_compare.clicked.connect(self.compare)
        self.setCentralWidget(self.button_compare)

        self.ssh_threadpool = QThreadPool()
        self.ssh_threadpool.setMaxThreadCount(10)

    def compare(self):
        ssh_worker = SSHWorker()
        ssh_worker.ssh_signals.result.connect(self.ssh_result)
        self.ssh_threadpool.start(ssh_worker)

    def ssh_result(self, server, message):
        print("server: ", server, ", result: ", message)


# ...

Upvotes: 1

Related Questions