pradyunsg
pradyunsg

Reputation: 19456

Using sockets with wxpython

I have a TCP server and a TCP client.
I want to make a GUI version of the code below, using .
I have the GUI interface script ready, but, I am having problems merging the two scripts.

How do I merge my socket script(s) and my GUI?

My socket server

from socket import *
from time import ctime
import random

bufsiz = 1024
port = random.randint(1025,36000)
host = 'localhost'
addr = (host, port)
print 'Port:',port

tcpServer = socket(AF_INET , SOCK_STREAM)
tcpServer.bind(addr)
tcpServer.listen(5)
try:
    while True:
        print 'Waiting for connection..'
        tcpClient, caddr = tcpServer.accept()
        print 'Connected To',caddr

        while True:
            data = tcpClient.recv(bufsiz)
            if not data:
                break
            tcpClient.send('[%s]\nData\n%s' % (ctime(),data))
            print data
        tcpClient.close()

except KeyboardInterrupt:
    tcpServer.close()

raw_input('Enter to Quit')

My GUI script (made using wxglade)

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# generated by wxGlade 0.6.5 (standalone edition) on Mon Feb 18 19:50:59 2013

import wx

# begin wxGlade: extracode
# end wxGlade


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MyFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.chat_log = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY)
        self.text_send = wx.TextCtrl(self, -1, "")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_TEXT_ENTER, self.text_e, self.text_send)
        # end wxGlade

    def __set_properties(self):
        # begin wxGlade: MyFrame.__set_properties
        self.SetTitle("frame_1")
        self.SetSize((653, 467))
        self.chat_log.SetMinSize((635, 400))
        self.text_send.SetMinSize((635, -1))
        self.text_send.SetFocus()
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MyFrame.__do_layout
        sizer_1 = wx.FlexGridSizer(1, 1, 1, 1)
        sizer_1.Add(self.chat_log, 0, 0, 0)
        sizer_1.Add(self.text_send, 0, wx.ALL, 1)
        self.SetSizer(sizer_1)
        self.Layout()
        # end wxGlade

    def text_e(self, event):  # wxGlade: MyFrame.<event_handler>
        text = self.text_send.GetValue()
        self.chat_log.AppendText("\n"+text)
        self.text_send.SetValue("")
        event.Skip()

# end of class MyFrame

class MyMenuBar(wx.MenuBar):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MyMenuBar.__init__
        wx.MenuBar.__init__(self, *args, **kwds)
        self.File = wx.Menu()
        self.Append(self.File, "File")
        self.View = wx.Menu()
        self.Append(self.View, "View")

        self.__set_properties()
        self.__do_layout()
        # end wxGlade

    def __set_properties(self):
        # begin wxGlade: MyMenuBar.__set_properties
        pass
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MyMenuBar.__do_layout
        pass
        # end wxGlade

# end of class MyMenuBar
if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame_1 = MyFrame(None, -1, "")
    app.SetTopWindow(frame_1)
    frame_1.Show()
    app.MainLoop()

Upvotes: 2

Views: 2316

Answers (1)

Fenikso
Fenikso

Reputation: 9451

In a nutshell: Encapsulate your first script into a function. Import your function into the wxPython app. Call the function from some event handler. Communicate back your function response to the GUI.

However, you would need to redesign your software so it does not contain infinite loop. Event handlers should run for short time only. Another way would be to run your function in a separate thread, communicate the response with GUI and add ability to terminate the thread from your main GUI thread.

Something along these lines:

import wx
from socket import *
from time import ctime
import random
import threading

bufsiz = 1024
port = random.randint(1025,36000)
host = 'localhost'
addr = (host, port)

class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.panel = wx.Panel(self)
        self.text = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE)

        self.sizer = wx.BoxSizer()
        self.sizer.Add(self.text, 1, wx.ALL | wx.EXPAND, 5)

        self.panel.SetSizerAndFit(self.sizer)  
        self.Show()

        self.thread = threading.Thread(target=self.Server)
        self.thread.start()

    def Print(self, text):
        wx.CallAfter(self.text.AppendText, text + "\n")

    def Server(self):
        self.Print("Port: {}".format(port))

        tcpServer = socket(AF_INET , SOCK_STREAM)
        tcpServer.bind(addr)
        tcpServer.listen(5)
        try:
            while True:
                self.Print("Waiting for connection...")
                tcpClient, caddr = tcpServer.accept()
                self.Print("Connected To {}".format(caddr))

                while True:
                    data = tcpClient.recv(bufsiz)
                    if not data:
                        break
                    tcpClient.send('[%s]\nData\n%s' % (ctime(), data))
                    self.Print(data)
                tcpClient.close()

        except KeyboardInterrupt:
            tcpServer.close()

app = wx.App(False)
win = MainWindow(None)
app.MainLoop()

This however does not terminate another thread upon exit. tcpServer.accept() is blocking operation. You may want to look into this answer how to connect to socket in a non-blocking way. Then you would be able to easily terminate your thread using some kins of shared flag.

Upvotes: 2

Related Questions