0

I am writing a GUI wrapper around Python’s SimpleHTTPServer. It looks like this:

enter image description here

The GUI uses tkinter. When I click on the OK button, it launches the web server.

The web server code is based on the article at https://docs.python.org/3/library/http.server.html. Part of it is:

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

It all works as expected so far, but when the https.server_forever() function runs, I get the spinning beachball, and I can’t close the window or quit. I can force quit and try again, which is not convenient. The server does do its job, however.

If I run the same code from the command line (a non-gui version), I can easily interrupt it with ctrl-c; I can catch that and exit more gracefully.

How can I interrupt the running server more politely from the server?

1
  • 1
    You may need to run the server in a thread. Also need to save the server instance in a global variable, for example httpd, so that you can terminate it using httpd.shutdown(). Commented Sep 2, 2021 at 9:57

1 Answer 1

2

You will need to run the server in a thread or a separate process, since both the web server and the UI need separate event loops.

If you want the server to communicate with the tkinter program, you'll need to set up a queue. As a general rule, you should only access tkinter objects from the thread that they were created in. However, I think it's safe to send virtual events from a worker thread to the GUI thread, which you can use to cause the GUI thread to read data from the queue.

For example, here's a simple threaded server. It must be passed the host and port, a reference to the root window, and a reference to the queue where information can be sent to the GUI.

class ExampleServer(threading.Thread):
    def __init__(self, host, port, gui, queue):
        threading.Thread.__init__(self)
        self.host = host
        self.port = port
        self.gui = gui
        self.queue = queue
        self.daemon = True

    def run(self):
        print(f"Listening on http://{self.host}:{self.port}\n")
        server = HTTPServer((self.host, self.port), ExampleHandler)
        server.serve_forever()

In your request handler, you can push items on the queue and then generate an event on the root window. It might look something like this:

class ExampleHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # handle the request
        ...

        # notify the UI
        self.queue.put("anything you want")
        self.gui.event_generate("<<DataAvailable>>")

Your gui also needs to take the queue as an argument and needs to set a binding to the virtual event. It might look something like this:

class ExampleGUI(tk.Tk):
    def __init__(self, queue):
        super().__init__()
        self.queue = queue
        # set up the rest of the GUI
        ...

        # bind to the virtual event
        self.bind("<<DataAvailable>>", self.poll_queue)

    def poll_queue(self, event):
        while not queue.empty():
            data = self.queue.get_nowait()
            # Do whatever you need to do with the data
            ...

    def start(self):
        self.mainloop()

Finally, you tie it all together with something like this:

if __name__ == "__main__":
    queue = queue.Queue()
    gui = ExampleGUI(queue)
    server = ExampleServer("localhost", 8910, gui, queue)
    server.start()
    gui.start()
Sign up to request clarification or add additional context in comments.

4 Comments

Should server.start() be executed before gui.start()?
@acw1668: I don't think it matters.
gui.start() is executing the tkinter mainloop() and it will block the application until the root window is closed. So server.start() will not be executed until root window is closed.
@acw1668: d'oh! Good point. I'll edit the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.