views:

179

answers:

3

I'm kind of lost on how to approach this problem, I'd like to write a GUI ideally using Tkinter with python, but I initially started with Qt and found that the problem extends either with all GUI frameworks or my limited understanding.

The data in this case is coming from a named pipe, and I'd like to display whatever comes through the pipe into a textbox. I've tried having one thread listen on the pipe and another create the GUI, but in both cases one thread always seems to hang or the GUI never gets created.

Any suggestions?

A: 

When I did something like this I used a separate thread listening on the pipe. The thread had a pointer/handle back to the GUI so it could send the data to be displayed.

I suppose you could do it in the GUI's update/event loop, but you'd have to make sure it's doing non-blocking reads on the pipe. I did it in a separate thread because I had to do lots of processing on the data that came through.

Oh and when you're doing the displaying, make sure you do it in non-trivial "chunks" at a time. It's very easy to max out the message queue (on Windows at least) that's sending the update commands to the textbox.

Dashogun
A: 

In the past when I've had GUI's reading data off of external things (eg: ethernet sockets), I've had a separate thread that handles servicing the external thing, and a timed callback (generally set to something like half a second) to update the GUI widget that displays the external data.

T.E.D.
A: 

Here is the way I would do it (on windows):

import wx, wx.lib.newevent, threading
import win32event, win32pipe, win32file, pywintypes, winerror


NewMessage, EVT_NEW_MESSAGE = wx.lib.newevent.NewEvent()
class MessageNotifier(threading.Thread):
    pipe_name = r"\\.\pipe\named_pipe_demo"

    def __init__(self, frame):
        threading.Thread.__init__(self)
        self.frame = frame

    def run(self):
        open_mode = win32pipe.PIPE_ACCESS_DUPLEX | win32file.FILE_FLAG_OVERLAPPED
        pipe_mode = win32pipe.PIPE_TYPE_MESSAGE

        sa = pywintypes.SECURITY_ATTRIBUTES()
        sa.SetSecurityDescriptorDacl(1, None, 0)

        pipe_handle = win32pipe.CreateNamedPipe(
            self.pipe_name, open_mode, pipe_mode,
            win32pipe.PIPE_UNLIMITED_INSTANCES,
            0, 0, 6000, sa
        )

        overlapped = pywintypes.OVERLAPPED()
        overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None)

        while 1:
            try:
                hr = win32pipe.ConnectNamedPipe(pipe_handle, overlapped)
            except:
                # Error connecting pipe
                pipe_handle.Close()
                break

            if hr == winerror.ERROR_PIPE_CONNECTED:
                # Client is fast, and already connected - signal event
                win32event.SetEvent(overlapped.hEvent)

            rc = win32event.WaitForSingleObject(
                overlapped.hEvent, win32event.INFINITE
            )

            if rc == win32event.WAIT_OBJECT_0:
                try:
                    hr, data = win32file.ReadFile(pipe_handle, 64)
                    win32file.WriteFile(pipe_handle, "ok")
                    win32pipe.DisconnectNamedPipe(pipe_handle)
                    wx.PostEvent(self.frame, NewMessage(data=data))
                except win32file.error:
                    continue


class Messages(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        self.messages = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
        self.Bind(EVT_NEW_MESSAGE, self.On_Update)

    def On_Update(self, event):
        self.messages.Value += "\n" + event.data


app = wx.PySimpleApp()
app.TopWindow = Messages()
app.TopWindow.Show()
MessageNotifier(app.TopWindow).start()
app.MainLoop()

Test it by sending some data with:

import win32pipe

print win32pipe.CallNamedPipe(r"\\.\pipe\named_pipe_demo", "Hello", 64, 0)

(you also get a response in this case)

Toni Ruža