Good morning,
I am trying to make a Python-Excel (via VBA) communication to display messages during VBA run time using a python TKinter GUI.
The code can exit by 2 options, 1.- Identify a "STOP" message sent from VBA or, 2.- Have received no message in a certain timeout.
I am having problems with exiting gratefully in either of the 2 options. TKinter sometimes closes, but python code is left hanging in the CMD, or in Visual Studio IDE command line, so that I don’t get prompted for a next command, just keeps living inside the script forever, which is by no means the intended functionality. Code below.
import tkinter as tk
import socket
import threading
import sys
import time
from queue import Queue
g_strSERVER = "127.0.0.1"
g_lPORT = int(sys.argv[1])
g_lTIMEOUT = 2 # As low for testing purposes only
def F_WND_CenterWindow(oWindow, lTargetWidth, lTargetHeight):
lScreenWidth = oWindow.winfo_screenwidth()
lScreenHeight = oWindow.winfo_screenheight()
x = (lScreenWidth - lTargetWidth) // 2
y = (lScreenHeight - lTargetHeight) // 2
oWindow.geometry(f"{lTargetWidth}x{lTargetHeight}+{x}+{y}")
class clsMessageWindow:
def __init__(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.quit_me)
self.root.title("Log display")
print("setting log display")
# Set the window size (change these values as needed)
lTargetWindowWidth = 640
lTargetWindowHeight = 380
# Center the window on the screen and keep it above all windows
F_WND_CenterWindow(self.root, lTargetWidth=lTargetWindowWidth, lTargetHeight=lTargetWindowHeight)
self.root.wm_attributes("-topmost",1)
self.text_widget = tk.Text(self.root)
self.text_widget.pack()
self.queue = Queue()
self.socket_thread = threading.Thread(target=self.start_socket_server)
self.stop_flag = threading.Event() # Event to signal the thread to stop
self.socket_thread.start()
print("all setup with log display")
self.last_message_received_at = time.time()
def start_socket_server(self):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((g_strSERVER,g_lPORT))
server_socket.listen(1)
print(f"Server started listening {g_strSERVER}:{g_lPORT}")
while not self.stop_flag.is_set(): # Check the stop flag
try:
oClientSocket, _ = server_socket.accept()
strMessageData = oClientSocket.recv(1024).decode("utf-8")
arrSplitMessage = strMessageData.split('\n')
strMessage = arrSplitMessage[-1]
print(f"Received message: {strMessage}")
if strMessage == "STOP":
print("Received STOP")
self.stop_flag.set() # Set the stop flag to exit the loop
else:
print(f"Adding message to queue: {strMessage}")
self.queue.put(strMessage)
oClientSocket.close()
except Exception as e:
print(f"Error: {e}")
# Close the server socket when done
server_socket.close()
def process_queue(self):
while not self.queue.empty():
strMessage = self.queue.get()
self.add_message(strMessage)
self.last_message_received_at = time.time()
print(f"Checking if flag is set: {self.stop_flag.is_set()}")
if not self.stop_flag.is_set():
# Check the queue periodically if the stop flag is not set
self.root.after(100, self.process_queue)
current_time = time.time()
print("Elapsed time: ", current_time - self.last_message_received_at)
if current_time - self.last_message_received_at > g_lTIMEOUT:
self.stop_flag.set()
print("Over Timeout")
self.quit_me()
else:
# Exit the mainloop gracefully if the stop flag is set
self.quit_me()
def quit_me(self):
print("Exiting TKinter gratefully")
self.root.quit()
self.root.destroy()
def add_message(self, strMessage):
if len(strMessage) > 0:
self.text_widget.insert(tk.END, strMessage + "\n")
# Scroll to the end to show the message
self.text_widget.see(tk.END)
def run(self):
self.process_queue()
self.root.mainloop()
if __name__ == '__main__':
message_window = clsMessageWindow()
message_window.run()
Thanks
>Solution :
Your socket_thread is still working when you try to exit, which is causing the hang.
You can use a so-called daemonic thread which will be killed when your application exits. Setting this up should be as simple as:
self.socket_thread = threading.Thread(target=self.start_socket_server, daemon=True)