Search code examples
pythonwxpythonsingle-instance

Single Instance Application - Passing commands


I'm using wxPython, and I'm trying to make a single instance application. As far as this goes, there is no problem. My problem is passing commands to the already existing instance.
So lets say that when I double click a file that is associated with my application, it reads the file and displays, here is an example:

import wx
import sys
import os
import SocketServer
import socket
import threading
class MyFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(320, 350))

        getfile = os.path.abspath(sys.argv[1])
        print getfile
        fopen = open (getfile, 'r')
        fread = fopen.read()

        panel = wx.Panel(self, -1)
        wx.StaticText(panel, -1, fread, (45, 25), style=wx.ALIGN_CENTRE)
        self.Centre()
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024)
        cur_thread = threading.currentThread()

        # do something with the request
        print "work"
        # could instead of the length of the input, could return error codes, more
        # information (if the request was a query), etc.  Using a length function
        # as a simple example
        response = 'string length: %d' % len(data)

        print 'responding to',data,'with',response
        self.request.send(response)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    stopped = False
    allow_reuse_address = True

    def serve_forever(self):
        while not self.stopped:
            self.handle_request()

    def force_stop(self):
        self.server_close()
        self.stopped = True

def client(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    sock.send(message)
    response = sock.recv(1024)
    print "Received: %s" % response
    sock.close()

def start_server(host, port):

    server = ThreadedTCPServer((host, port), ThreadedTCPRequestHandler)
    ip, port = server.server_address

    # Start a thread with the server -- that thread will then start one
    # more thread for each request
    server_thread = threading.Thread(target=server.serve_forever)
    # Exit the server thread when the main thread terminates
    server_thread.setDaemon(True)
    server_thread.start()

    return server
class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame(None, -1, 'test')
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

def main():
    app = MyApp(0)
    app.MainLoop()

if __name__ == '__main__':
    HOST, PORT = socket.gethostname(), 61955

    server = None
    try:
        client(HOST, PORT, ' '.join(sys.argv))
        sys.exit()
    except socket.error:
        server = start_server(HOST, PORT)
        main()

This works just fine, but now I want to make it a single instance application. And I know that there are several ways to accomplish this, and in my case, I found that listening to a defined port is probably the best option. But even using such option, I have no idea on how I could pass the information of the file or, atleast, the name to the original instance and display it.

So the application should be:

  • A single instance application.
  • It should be able to read the double-clicked files.
  • If an instance is already open, then pass the information or file name to the original instance so it can refresh and display the file's data automatically.

Also, I didn't include any code for listening to a defined port because I really don't know much about it, so any help on this subject is also greatly appreciated.

EDIT: Code edited with "listening to a defined port", it is taken from an example. Note that print "work" does send to the running instance, I just don't know how to make it work in my occasion (because of my plain ignorance).


Solution

  • This is the solution:

    import wx
    import sys
    import os
    import SocketServer
    import socket
    import threading
    class MyFrame(wx.Frame):
        def __init__(self, parent, id, title):
            wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(320, 350))
    
            getfile = os.path.abspath(sys.argv[1])
            print getfile
            fopen = open (getfile, 'r')
            fread = fopen.read()
    
            panel = wx.Panel(self, -1)
            self.static = wx.StaticText(panel, -1, fread, (45, 25), style=wx.ALIGN_CENTRE)
            self.Centre()
    class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
        def handle(self):
            data = self.request.recv(1024)
            cur_thread = threading.currentThread()
    
            data = data.split()
            gfile = os.path.abspath(data[-1])
            fopen = open(gfile, 'r')
            fread = fopen.read()
            self.server.app.static.SetLabel(fread)
            #Note to the self.server.app
            response = 'string length: %d' % len(data)
    
            print 'responding to',data,'with',response
            self.request.send(response)
    class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
        stopped = False
        allow_reuse_address = True
    
        def serve_forever(self):
            while not self.stopped:
                self.handle_request()
    
        def force_stop(self):
            self.server_close()
            self.stopped = True
    
    def client(ip, port, message):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((ip, port))
        sock.send(message)
        response = sock.recv(1024)
        print "Received: %s" % response
        sock.close()
    
    def start_server(host, port):
    
        server = ThreadedTCPServer((host, port), ThreadedTCPRequestHandler)
        ip, port = server.server_address
    
        # Start a thread with the server -- that thread will then start one
        # more thread for each request
        server_thread = threading.Thread(target=server.serve_forever)
        # Exit the server thread when the main thread terminates
        server_thread.setDaemon(True)
        server_thread.start()
    
        return server
    class MyApp(wx.App):
        def OnInit(self):
            frame = MyFrame(None, -1, 'test')
            frame.Show(True)
            server.app = frame
            #Note the server.app
            self.SetTopWindow(frame)
            return True
    
    def main():
        app = MyApp(0)
        app.MainLoop()
    
    if __name__ == '__main__':
        HOST, PORT = socket.gethostname(), 61955
    
        server = None
        try:
            client(HOST, PORT, ' '.join(sys.argv))
            sys.exit()
        except socket.error:
            server = start_server(HOST, PORT)
            main()
    

    Essentially I had to bind the server.app with my frame:

    class MyApp(wx.App):
        def OnInit(self):
            frame = MyFrame(None, -1, 'test')
            frame.Show(True)
            server.app = frame
            #Note the server.app
    

    So I could call later on:

    data = self.request.recv(1024)
    cur_thread = threading.currentThread()
    
    data = data.split()
    gfile = os.path.abspath(data[-1])
    fopen = open(gfile, 'r')
    fread = fopen.read()
    self.server.app.static.SetLabel(fread)
    

    The important here is the self.server.app, although what I wanted was to pass the commands to the running instance. And the data does this for me. Let's say I open up the already open application with the file test.app then the data value will be:

    \pathtoyourapplication\application.py test.app
    

    So I grabbed its location and read, and that's pretty much it for my stupidity, hope this might be helpful for someone, in some way.