简体   繁体   中英

wxPython and Autobahn websockets

Im trying to create GUI app for my test project, based on Python/Twisted/Autobahn.ws. Im following article as like there: wxPython and Twisted and try to connect my application to the server. But at the start i'll get an error:

Unhandled Error
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/twisted/python/log.py", line 88, in callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
  File "/usr/local/lib/python2.7/dist-packages/twisted/python/log.py", line 73, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "/usr/local/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/local/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
    return func(*args,**kw)
--- <exception caught here> ---
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/_threadedselect.py", line 283, in _doReadOrWrite
    why = getattr(selectable, method)()
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 593, in doConnect
    self._connectDone()
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 607, in _connectDone
    self.protocol = self.connector.buildProtocol(self.getPeer())
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/base.py", line 1071, in buildProtocol
    return self.factory.buildProtocol(addr)
  File "/usr/local/lib/python2.7/dist-packages/twisted/protocols/policies.py", line 171, in buildProtocol
    return self.protocol(self, self.wrappedFactory.buildProtocol(addr))
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/protocol.py", line 123, in buildProtocol
    p = self.protocol()
exceptions.AttributeError: GUIClientProtocol instance has no __call__ method

How i can fix this issue? Need to define call method in my class or maybe i have better solution for this?

Code: 1) client_gui:

import wx
from twisted.internet import wxreactor
wxreactor.install()

from twisted.internet import reactor, ssl
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS

from gui.filemanager import CloudStorage

class GUIClientProtocol(WebSocketClientProtocol):

    def __init__(self, gui):
        self.gui = gui

if __name__ == '__main__':
    app = wx.App(False)
    frame = CloudStorage(None, -1, 'CloudStorage')
    frame.Show()

    host_url = "wss://%s:%s" % ("localhost", 9000)
    # create a WS server factory with our protocol
    factory = WebSocketClientFactory(host_url, debug = False)
    factory.protocol = GUIClientProtocol(frame)
    # SSL client context: using default
    if factory.isSecure:
        contextFactory = ssl.ClientContextFactory()
    else:
        contextFactory = None

    reactor.registerWxApp(app)
    connectWS(factory, contextFactory)
    reactor.run()

2) server.py

import sys
import datetime
import pickle
from json import dumps, loads
from hashlib import sha256
from time import gmtime, strftime
from subprocess import Popen, PIPE, STDOUT

import sqlalchemy
import sqlalchemy.exc
from sqlalchemy import and_, func, asc
from sqlalchemy.orm import sessionmaker
from twisted.internet import reactor, ssl
from twisted.internet.task import deferLater
from twisted.python import log, logfile
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol, listenWS

from balancer.balancer import Balancer
from db.tables import File as FileTable
from db.tables import Users, FileServer, FileSpace, Catalog
from flask_app import app
from utils import commands


log_file = logfile.LogFile("service.log", ".")
log.startLogging(log_file)
engine = sqlalchemy.create_engine('postgresql://user:password@localhost/csan', pool_size=20, max_overflow=0)


def checkServerStatus(ip, port):
    p = Popen(["python", "statuschecker.py", str(ip), str(port)], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
    result = p.communicate()[0].replace('\n', '')
    return result.split('|')


class DFSServerProtocol(WebSocketServerProtocol):

    ... # ~450 lines of code 

if __name__ == '__main__':
    if len(sys.argv) > 1 and sys.argv[1] == 'debug':
        log.startLogging(sys.stdout)
        debug = True
        port = int(sys.argv[2])
    else:
        debug = False
        port = int(sys.argv[1])

    contextFactory = ssl.DefaultOpenSSLContextFactory('web/keys/server.key', 'web/keys/server.crt')

    server_addr = "wss://localhost:%d" % (port)
    factory = WebSocketServerFactory(server_addr, debug = debug, debugCodePaths = debug)
    factory.protocol = DFSServerProtocol
    factory.setProtocolOptions(allowHixie76 = True)

    listenWS(factory, contextFactory)

    # Flask with SSL under Twisted
    resource = WSGIResource(reactor, reactor.getThreadPool(), app)
    site = Site(resource)
    reactor.listenSSL(8080, site, contextFactory)
    # reactor.listenTCP(8080, web)
    reactor.run()

Update #1: client_gui.py looks like that:

import wx
from twisted.internet import wxreactor
wxreactor.install()

from twisted.internet import reactor, ssl
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS

from gui.filemanager import CloudStorage

class GUIClientProtocol(WebSocketClientProtocol):

    def __init__(self):
        WebSocketClientProtocol.__init__(self)
        self.gui = None

    def connectionMade(self):
        self.gui = self.factory._frame
        self.gui.Show()

if __name__ == '__main__':
    app = wx.App(False)
    frame = CloudStorage(None, -1, 'CloudStorage')

    # create a WS server factory with our protocol
    host_url = "wss://%s:%s" % ("localhost", 9000)
    factory = WebSocketClientFactory(host_url)
    factory.protocol = GUIClientProtocol
    factory._frame = frame

    # SSL client context: using default
    if factory.isSecure:
        contextFactory = ssl.ClientContextFactory()
    else:
        contextFactory = None

    reactor.registerWxApp(app)
    connectWS(factory, contextFactory)
    reactor.run()

Update #2:
code of my CloudStorage class:

import wx
import os
import time

ID_BUTTON=100
ID_EXIT=200
ID_SPLITTER=300

class MyListCtrl(wx.ListCtrl):
    def __init__(self, parent, id):
        wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT)

        files = os.listdir('.')
        images = ['images/empty.png', 'images/folder.png', 'images/source_py.png',
        'images/image.png', 'images/pdf.png', 'images/up16.png']

        self.InsertColumn(0, 'Name')
        self.InsertColumn(1, 'Ext')
        self.InsertColumn(2, 'Size', wx.LIST_FORMAT_RIGHT)
        self.InsertColumn(3, 'Modified')

        self.SetColumnWidth(0, 220)
        self.SetColumnWidth(1, 70)
        self.SetColumnWidth(2, 100)
        self.SetColumnWidth(3, 420)

        self.il = wx.ImageList(16, 16)
        for i in images:
            self.il.Add(wx.Bitmap(i))
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)

        j = 1
        self.InsertStringItem(0, '..')
        self.SetItemImage(0, 5)

        for i in files:
            (name, ext) = os.path.splitext(i)
            ex = ext[1:]
            size = os.path.getsize(i)
            sec = os.path.getmtime(i)
            self.InsertStringItem(j, name)
            self.SetStringItem(j, 1, ex)
            self.SetStringItem(j, 2, str(size) + ' B')
            self.SetStringItem(j, 3, time.strftime('%Y-%m-%d %H:%M',
        time.localtime(sec)))

            # if os.path.isdir(i):
            #     self.SetItemImage(j, 1)
            # elif ex == 'py':
            #     self.SetItemImage(j, 2)
            # elif ex == 'jpg':
            #     self.SetItemImage(j, 3)
            # elif ex == 'pdf':
            #     self.SetItemImage(j, 4)
            # else:
            #     self.SetItemImage(j, 0)

            if (j % 2) == 0:
                self.SetItemBackgroundColour(j, '#e6f1f5')
            j = j + 1

class CloudStorage(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, -1, title)

        self.splitter = wx.SplitterWindow(self, ID_SPLITTER, style=wx.SP_BORDER)
        self.splitter.SetMinimumPaneSize(50)

        p1 = MyListCtrl(self.splitter, -1)
        p2 = MyListCtrl(self.splitter, -1)
        self.splitter.SplitVertically(p1, p2)

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_SPLITTER_DCLICK, self.OnDoubleClick, id=ID_SPLITTER)

        filemenu= wx.Menu()
        filemenu.Append(ID_EXIT,"E&xit"," Terminate the program")
        editmenu = wx.Menu()
        netmenu = wx.Menu()
        showmenu = wx.Menu()
        configmenu = wx.Menu()
        helpmenu = wx.Menu()

        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File")
        menuBar.Append(editmenu, "&Edit")
        menuBar.Append(netmenu, "&Net")
        menuBar.Append(showmenu, "&Show")
        menuBar.Append(configmenu, "&Config")
        menuBar.Append(helpmenu, "&Help")
        self.SetMenuBar(menuBar)
        self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT)

        tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER |
        wx.TB_FLAT | wx.TB_TEXT)
        # tb.AddSimpleTool(10, wx.Bitmap('images/previous.png'), 'Previous')
        # tb.AddSimpleTool(20, wx.Bitmap('images/up.png'), 'Up one directory')
        # tb.AddSimpleTool(30, wx.Bitmap('images/home.png'), 'Home')
        # tb.AddSimpleTool(40, wx.Bitmap('images/refresh.png'), 'Refresh')
        # tb.AddSeparator()
        # tb.AddSimpleTool(50, wx.Bitmap('images/write.png'), 'Editor')
        # tb.AddSimpleTool(60, wx.Bitmap('images/terminal.png'), 'Terminal')
        # tb.AddSeparator()
        # tb.AddSimpleTool(70, wx.Bitmap('images/help.png'), 'Help')
        tb.Realize()

        self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)

        button1 = wx.Button(self, ID_BUTTON + 1, "F3 View")
        button2 = wx.Button(self, ID_BUTTON + 2, "F4 Edit")
        button3 = wx.Button(self, ID_BUTTON + 3, "F5 Copy")
        button4 = wx.Button(self, ID_BUTTON + 4, "F6 Move")
        button5 = wx.Button(self, ID_BUTTON + 5, "F7 Mkdir")
        button6 = wx.Button(self, ID_BUTTON + 6, "F8 Delete")
        button7 = wx.Button(self, ID_BUTTON + 7, "F9 Rename")
        button8 = wx.Button(self, ID_EXIT, "F10 Quit")

        self.sizer2.Add(button1, 1, wx.EXPAND)
        self.sizer2.Add(button2, 1, wx.EXPAND)
        self.sizer2.Add(button3, 1, wx.EXPAND)
        self.sizer2.Add(button4, 1, wx.EXPAND)
        self.sizer2.Add(button5, 1, wx.EXPAND)
        self.sizer2.Add(button6, 1, wx.EXPAND)
        self.sizer2.Add(button7, 1, wx.EXPAND)
        self.sizer2.Add(button8, 1, wx.EXPAND)

        self.Bind(wx.EVT_BUTTON, self.OnExit, id=ID_EXIT)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.splitter,1,wx.EXPAND)
        self.sizer.Add(self.sizer2,0,wx.EXPAND)
        self.SetSizer(self.sizer)

        size = wx.DisplaySize()
        self.SetSize(size)

        self.sb = self.CreateStatusBar()
        self.sb.SetStatusText(os.getcwd())
        self.Center()
        self.Show(True)

    def OnExit(self,e):
        self.Close(True)

    def OnSize(self, event):
        size = self.GetSize()
        self.splitter.SetSashPosition(size.x / 2)
        self.sb.SetStatusText(os.getcwd())
        event.Skip()

    def OnDoubleClick(self, event):
        size =  self.GetSize()
        self.splitter.SetSashPosition(size.x / 2)

if __name__ == '__main__':
    app = wx.App(0)
    CloudStorage(None, -1, 'CloudStorage')
    app.MainLoop()

Update: I have created a complete example of using wxPython with Autobahn to create WebSocket enabled UIs.

The problem is the line

factory.protocol = GUIClientProtocol(frame)

This sets factory.protocol to an instance of GUIClientProtocol . But it needs to be the class.

Now, you apparently want to have frame accessible from within GUIClientProtocol . There are (at least) two options.

Option 1:

factory.protocol = GUIClientProtocol
factory._frame = frame

and then access as self.factory._frame from the (running) protocol instance.

Option 2: Implement Factory.buildProtocol .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM