how do i add a label to tkinter through after the mainloop()?

Advertisements

I need to create a client-server registry system. The client is supposed to enter their username and password, and the server should confirm it. The GUI should be made in tkinter.
Problem is, I need to add labels and such after checking for a command from the server.
I’m very sorry if my understanding of this is wrong, as I’m used to Pygame, where everything works in 1 while(true) loop.

This is the client code. Protocol is a different file which contains some kind of command check.
The server is not a part of the problem. It sends "registered" when the client’s request is confirmed.

import socket
import sys
import tkinter as tk
from tkinter.simpledialog import askstring
from tkinter import *
from tkinter import messagebox
import time
from queue import Queue
import threading
import protocol


data_queue = Queue()  # queue where threads can send data from one to another
IP = "127.0.0.1"
PORT = 1234
BIG_BUFFER = 256
stop_event = threading.Event()


def packed(cmd):
    return cmd.encode()


def on_closing(top, client, data_thread):
    if messagebox.askokcancel("Quit", "Do you want to close the application?"):
        client.send(packed("exit"))
        stop_event.set()
        top.destroy()
        data_thread.join()
        client.close()


def receive_data_from_server(client):  # receives data from server through another thread
    while not stop_event.isSet():
        try:
            server_cmd = client.recv(BIG_BUFFER).decode()
            if server_cmd:
                print(server_cmd)
                if protocol.check_cmd(server_cmd):
                    data_queue.put(server_cmd)  # sends to main thread
                else:
                    print("invalid cmd: " + server_cmd)
        except Exception as err:
            print(err)


def password_err():
    print("h")


def register_user(client, username, password1, password2):
    if password1 == password2:
        client.send(packed("add-" + username + "-" + password1))
    else:
        password_err()


def success_reg(frame):
    frame.insert(tk.END, "Success!")
    frame.pack()


def main():
    try:
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((IP, PORT))  # connect to server

    except Exception as error:
        print(error)  # could be an error connecting to server or establishing the socket

    top = tk.Tk()
    top.title("Registration")
    top.geometry("800x400")
    top.protocol("WM_DELETE_WINDOW", lambda: on_closing(top, client, data_thread))

    registration_frame = tk.Frame(top)
    registration_frame.pack()

    register_label = tk.Label(registration_frame, text="Registration")
    register_label.pack()

    username_label = tk.Label(registration_frame, text="Enter username:")
    username_label.pack()
    username_entry = tk.Entry(registration_frame)
    username_entry.pack()

    password_label = tk.Label(registration_frame, text="Enter password:")
    password_label.pack()
    password_entry = tk.Entry(registration_frame, show="*")  # "show" hides the password
    password_entry.pack()

    check_password_label = tk.Label(registration_frame, text="Confirm password:")
    check_password_label.pack()
    check_password_entry = tk.Entry(registration_frame, show="*")  # "show" hides the password
    check_password_entry.pack()

    # creates register button
    register_button = tk.Button(registration_frame, text="Register", command=lambda: register_user(
        client, username_entry.get(), password_entry.get(), check_password_entry.get()))
    register_button.pack()

    # data thread listening to server
    data_thread = threading.Thread(target=receive_data_from_server, args=(client,))
    data_thread.daemon = True
    data_thread.start()

    top.mainloop() 

    while not stop_event.isSet():
        if not data_queue.empty():
            server_cmd = data_queue.get()
            if server_cmd == "registered":
                success_reg(registration_frame) 


if __name__ == '__main__':
    main()

Thanks in advance.

>Solution :

In your main() function you just creating all the GUI elements and then, using top.mainloop() start the GUI application. There is no loop for recieving and processing server messages. And it cannot be there, because after top.mainloop() is called, main() function completely freezes at that line.

In order to make GUI react to messages from server, usually you should make separate thread, where messages queue will be read in infinite loop.

In main() function you’ve already created thread data_thread. But, inside receive_data_from_server instead of collecting messages to data_queue you can simply write logic for GUI to react to messaages. Also, you should pass GUI elements to receive_data_from_server you want to make reactable to messages (in order to make them reachable for the function).

Here is quick example how this should work (I also got rid of several indentation levels by rewriting your code, keeping the logic same):

def receive_data_from_server(client, window):  # receives data from server through another thread
    while not stop_event.isSet():
        try:
            server_cmd = client.recv(BIG_BUFFER).decode()

            if not server_cmd:
                continue

            if protocol.check_cmd(server_cmd):
                print("invalid cmd: " + server_cmd)
                continue

            if server_cmd == "REGISTER":
                new_label = tk.Label(window, text="You've successfully registered!")
            elif server_cmd == "ANYTHING_ELSE":
                new_label = tk.Label(window, text="Something happened...")
            elif server_cmd == "SOMETHING_ELSE":
                new_label = tk.Label(window, text="Something else happened...")

            new_label.pack()

        except Exception as err:
            print(err)

        # A little sleep is good to prevent infinite loop from spamming        
        # You should import it: from time import sleep
        sleep(0.5)

And after that, you should change data_thread creation process, passing GUI elements that should be reachable. Here I pass only the top window of the application:

data_thread = threading.Thread(target=receive_data_from_server, args=(client, top))

Leave a ReplyCancel reply