qt5reactor icon indicating copy to clipboard operation
qt5reactor copied to clipboard

autobahn wamp call works in onJoin but not in button event

Open malibu1966 opened this issue 4 years ago • 3 comments

Hi, I am attempting to use qt5reactor in order to do crossbar.io wamp calls from a pyqt5 application. I have it working to the point that I can do a wamp call from the onJoin callback but when I try to put the call in a button event I get an immediate 'TransportLost' exception and the call does not go through.

In the code below, the call in the onJoin callback works but the call in the message_to callback (from a button) does not.

import sys, json
import qt5reactor
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QLineEdit, QPushButton, QListView, QWidget
from PyQt5.QtCore import Qt
from autobahn.twisted.wamp import ApplicationSession
from autobahn.twisted.wamp import ApplicationRunner
from autobahn.wamp import auth
from autobahn.wamp.exception import SessionNotReady
from chatinclude import MessageModel, MessageDelegate

# Subclass QMainWindow to customise your application's main window
from twisted.internet.defer import inlineCallbacks


class MainWindow(QMainWindow, ApplicationSession):

    def __init__(self, *args, **kwargs):
        self.global_authid = "test-chat-client"
        self.global_key = "MYKEY"
        super(MainWindow, self).__init__()
        super(ApplicationSession, self).__init__(*args, **kwargs)
        # Layout the UI
        l = QVBoxLayout()

        self.message_input = QLineEdit("Enter message here")

        # Buttons for from/to messages.
        self.btn1 = QPushButton("<")
        self.btn2 = QPushButton(">")

        self.messages = QListView()
        self.messages.setResizeMode(QListView.Adjust)
        # Use our delegate to draw items in this view.
        self.messages.setItemDelegate(MessageDelegate())

        self.model = MessageModel()
        self.messages.setModel(self.model)

        self.btn1.pressed.connect(self.message_to)
        self.btn2.pressed.connect(self.message_from)

        l.addWidget(self.messages)
        l.addWidget(self.message_input)
        l.addWidget(self.btn1)
        l.addWidget(self.btn2)

        self.w = QWidget()
        self.w.setLayout(l)
        self.setCentralWidget(self.w)

    @inlineCallbacks
    def message_to(self):
        USER_ME=0
        try:
            res2 = yield self.call(u'biz.domain.register_chat')
            print("\nget data result: {}\n".format(res2))
            self.model.add_message(USER_ME, self.message_input.text())
        except Exception as e:
            print("get data call error: {0}".format(e))

    def message_from(self):
        USER_THEM=1
        self.model.add_message(USER_THEM, self.message_input.text())

    def onConnect(self):
        print("CONNECT", self.global_authid)
        self.join(u"unwait", ['wampcra'], self.global_authid)

    def onChallenge(self, challenge):
        print("CHALLENGE",challenge.method)
        if challenge.method == u"wampcra":
            signature = auth.compute_wcs(self.global_key.encode('utf8'),
                                         challenge.extra['challenge'].encode('utf8'))
            return signature.decode('ascii')
        else:
            raise Exception("don't know how to handle authmethod {}".format(challenge.method))
        return None

    @inlineCallbacks
    def onJoin(self, details):
        print("JOIN")
        try:
            res2 = yield self.call(u'biz.domain.register_chat')
            print("\nget data result: {}\n".format(res2))
        except Exception as e:
            print("get data call error: {0}".format(e))

    def onLeave(self, details):
        print("LEAVE")

    def closeEvent(self, event):
        pass


app = QApplication(sys.argv)
qt5reactor.install()
runner = ApplicationRunner(url='ws://IPADDR:9001/ws', realm='myrealm')

window = MainWindow()
window.show()
runner.run(MainWindow)
#app.exec_()

malibu1966 avatar Apr 22 '21 13:04 malibu1966

Sorry, this notification got lost in my pile. :[

Maybe a side note, maybe not... but I would tend to recommend away from multiple inheritance like this. Seems like a lot of stuff getting mixed together.

I can't say I have any familiarity with crossbar.io beyond having heard of it. Hopefully you have gotten some help from elsewhere by now, or solved this yourself. If not, could you share the full output error? Might help me understand which side of things the issue lies on.

altendky avatar May 19 '21 17:05 altendky

Thanks for the comment. There is very little documentation on how to do it, and this is done like the only demo I could find.

On Wed, May 19, 2021 at 2:57 PM Kyle Altendorf @.***> wrote:

Sorry, this notification got lost in my pile. :[

Maybe a side note, maybe not... but I would tend to recommend away from multiple inheritance like this. Seems like a lot of stuff getting mixed together.

I can't say I have any familiarity with crossbar.io beyond having heard of it. Hopefully you have gotten some help from elsewhere by now, or solved this yourself. If not, could you share the full output error? Might help me understand which side of things the issue lies on.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/twisted/qt5reactor/issues/58#issuecomment-844336907, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQOQRRJZRZXYAD2AO45NPCDTOP3XNANCNFSM43MOMIKQ .

malibu1966 avatar May 21 '21 17:05 malibu1966

I would also not do multiple inheritance like that. You're actually instantiating MainWindow twice in the above which seems likely to be the problem.

The Component interface is newer and probably more what you'll want here and can play nicely when you want to control the even-loop (as here). ApplicationRunner or autobahn.twisted.component.run are both designed as very high-level convenience APIs .. but here I think you want Qt to control the event-loop (and not Autobahn).

This is off the top of my head here, but approximately what you'll want is:

  • make a Component instance
  • hook up on_join and on_leave listeners to notify your Qt application when the wamp connection is available
  • call .start() on the Component instance

You'll likely want to disable the "chat" button when the WAMP connection isn't active. Component will re-connect automatically, so you'll get an on_leave and then (another) on_join if that happens.

Some pseudo-code:

comp = Component(...)
window = MainWindow()

@comp.on_join
def joined(session, details):
    window.set_wamp_connection(session)

@comp.on_leave
def left(session, reason):
    window.unset_wamp_connection()

window.show()
comp.start()
# ...
# app.exec_() or whatever runs the Qt main loop .. or just reactor.run()

meejah avatar May 21 '21 19:05 meejah