1

I am programming a socket application, I use build-in library socket to make the server, Thread and Tkinter to make GUI.

My problem is I do not know how to insert text into listbox of class MainPage (it is self.msg_list) in main_page.py from function __accept_incoming_connection in class Server in server.py.

Here's my project folder structure:

server
├── main.py
├── server.py
├── views
│   ├── setup_page.py
│   ├── main_page.py

main.py

import tkinter as tk
from tkinter import font as tkfont
from view.setup_page import SetupPage
from view.main_page import MainPage
from server import Server


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.title_font = tkfont.Font(family='Helvetica',
                                      size=18,
                                      weight='bold',
                                      slant='italic')

        container = tk.Frame(self)
        container.pack(side='top', fill='both', expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (SetupPage, MainPage):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky='nsew')

        self.show_frame('SetupPage')

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()


if __name__ == '__main__':
    server = Server()
    app = App()
    app.title('Server')
    app.geometry('600x450')
    app.mainloop()
    server.__del__()

server.py

from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread


HOST = ''
PORT = 3000
ADDRESS = (HOST, PORT)


class Server:
    __instance = None
    __is_running = False

    @staticmethod
    def get_instance():
        if Server.__instance is None:
            Server()
        return Server.__instance

    def __init__(self):
        if Server.__instance is not None:
            raise Exception("Server is singleton class")
        else:
            Server.__instance = self
            self.__server = socket(AF_INET, SOCK_STREAM)
            self.__server.bind(ADDRESS)
            self.__is_running = True

    def __del__(self):
        self.__is_running = False
        self.__accept_thread.join()
        self.__server.close()

    def __accept_incoming_connection(self):
        while self.__is_running:
            client, client_address = self.__server.accept()
            # I want to insert text into listbox of the MainPage (msg_list) here

    def listen(self, num_of_clients):
        self.__server.listen(num_of_clients)
        self.__accept_thread = Thread(target=self.__accept_incoming_connection)
        self.__accept_thread.start()

main_page.py

import tkinter as tk
from socket import AF_INET, socket, SOCK_STREAM


class MainPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.scrollbar = tk.Scrollbar(self)
        self.msg_list = tk.Listbox(self,
                                   height=15,
                                   width=50,
                                   yscrollcommand=self.scrollbar.set)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.msg_list.pack(side=tk.LEFT, fill=tk.BOTH)
        self.msg_list.pack()
1
  • You can try using root.after but multithreading with tkinter is tricky. Look at pypi.org/project/tkthread . Commented Mar 31, 2021 at 2:48

1 Answer 1

1

You can use queue.SimpleQueue to pass data from Server to MainPage so that MainPage can insert the data into the Listbox:

main.py:

from queue import SimpleQueue
...
class App(tk.Tk):
    def __init__(self, queue, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.queue = queue
        ...

if __name__ == "__main__":
    queue = SimpleQueue()
    server = Server(queue) # pass queue to Server
    app = App(queue) # pass queue to App
    ...

server.py:

class Server:
    ...
    def __init__(self, queue=None):
        ...
        self.queue = queue

    def __accept_incoming_connection(self):
        while self.__is_running:
            client, client_address = self.__server.accept()
            # I want to insert text into listbox of the MainPage (msg_list) here
            if self.queue:
                self.queue.put(client_address) # or whatever information you want
    ...

main_page.py:

class MainPage(tk.Frame):
    def __init__(self, parent, controller):
        ....
        # call a monitor task periodically
        self.monitor_queue()

    def monitor_queue(self):
        if not self.controller.queue.empty():
            caddr = self.controller.queue.get()
            self.msg_list.insert(tk.END, str(caddr))
            self.msg_list.see(tk.END)
        self.after(100, self.monitor_queue)

Below is an example using similar structure as your code:

import tkinter as tk
from queue import SimpleQueue
from socket import socket, AF_INET, SOCK_STREAM
import threading

HOST = ""
PORT = 3000
ADDRESS = (HOST, PORT)

class Server(threading.Thread):
   __instance = None

   @staticmethod
   def get_instance():
      if Server.__instance is None:
         Server()
      return Server.__instance

   def __init__(self, queue=None):
      if Server.__instance:
         raise Exception("Server is singleton class")
      super().__init__()
      Server.__instance = self
      self._server = socket(AF_INET, SOCK_STREAM)
      self._server.bind(ADDRESS)
      self._server.listen()

      self.queue = queue

   def set_queue(self, queue):
      self.queue = queue

   def run(self):
      print("Server started")
      while True:
         try:
            client, client_address = self._server.accept()
            if self.queue:
               self.queue.put(client_address)
         except Exception as e:
            print(e)
            break
      print("Server terminated")

   def terminate(self):
      self._server.close() # self._server.accept() will raise exception and break the while loop

class SetupPage(tk.Frame):
   def __init__(self, parent, controller):
      super().__init__(parent)
      self.controller = controller
      tk.Label(self, text="Setup Page").pack(padx=100, pady=50)
      tk.Button(self, text="Goto Main page", command=lambda: self.controller.show_frame("MainPage")).pack()

class MainPage(tk.Frame):
   def __init__(self, parent, controller):
      super().__init__(parent)
      self.controller = controller

      self.scrollbar = tk.Scrollbar(self)
      self.msg_list = tk.Listbox(self, width=50, height=15, yscrollcommand=self.scrollbar.set)

      self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
      self.msg_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

      self.monitor_queue()

   def monitor_queue(self):
      if not self.controller.queue.empty():
         caddr = self.controller.queue.get()
         self.msg_list.insert(tk.END, str(caddr))
         self.msg_list.see(tk.END)
      self.after(100, self.monitor_queue)

class App(tk.Tk):
   def __init__(self, queue):
      super().__init__()
      self.queue = queue

      container = tk.Frame(self)
      container.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
      container.grid_rowconfigure(0, weight=1)
      container.grid_columnconfigure(0, weight=1)

      self.frames = {}
      for F in (SetupPage, MainPage):
         frame = F(parent=container, controller=self)
         frame.grid(row=0, column=0, sticky="nsew")
         self.frames[F.__name__] = frame

      self.show_frame("SetupPage")

   def show_frame(self, page_name):
      frame = self.frames[page_name]
      frame.tkraise()

if __name__ == "__main__":
   queue = SimpleQueue()
   server = Server(queue)
   server.start()
   app = App(queue)
   app.title("Server")
   app.geometry("600x450")
   app.mainloop()
   server.terminate()
Sign up to request clarification or add additional context in comments.

Comments

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.