Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
OK, I wanted to make an python application that allows me to send a message over LAN.
Here's the code that "works" "locally" (imagine that I'm forming 2 of my fingers to make an " on those 2 words)
username = input("username: ")
run = 1
while run == 1:
message = input(username + ": ")
if message == "exit":
print(username + " left")
run = 0
else:
print(username + ": " + message)
My question how do I send the variable "message" over LAN?
I wan't it to send the variable message over LAN to another PC and print it on it and the other way around.
Here are several files that may help you with developing a messaging system for your LAN.
Simple_Server.py
#! /usr/bin/env python3
import socket, select
def main():
a = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)] # socket array
a[0].bind(('', 8989))
a[0].listen(5)
while True:
for b in select.select(a, [], [])[0]: # ready socket
if b is a[0]:
a.append(b.accept()[0])
else:
try:
c = b.recv(1 << 12) # sent message
except socket.error:
b.shutdown(socket.SHUT_RDWR)
b.close()
a.remove(b)
else:
for d in (d for d in a[1:] if d is not b): # message sink
d.sendall(c)
if __name__ == '__main__':
main()
MultichatClient.py
#! /usr/bin/env python3
from safetkinter import *
from tkinter.constants import *
import socket
import sys
class MultichatClient(Frame):
after_handle = None
def __init__(self, master, remote_host):
super().__init__(master)
self.message_area = ScrolledText(self, width=81, height=21,
wrap=WORD, state=DISABLED)
self.message_area.grid(sticky=NSEW, columnspan=2)
self.send_area = Entry(self)
self.send_area.bind('<Return>', self.keyPressed)
self.send_area.grid(sticky=EW)
b = Button(self, text='Send', command=self.mouseClicked)
b.grid(row=1, column=1)
self.send_area.focus_set()
try:
self.remote = socket.create_connection((remote_host, 8989))
except socket.gaierror:
print('Could not find host {}.'.format(remote_host))
except socket.error:
print('Could not connect to host {}.'.format(remote_host))
else:
self.remote.setblocking(False)
self.after_handle = self.after_idle(self.dataready)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
#classmethod
def main(cls, args):
root = Tk()
root.title('MultichatClient version 1.0')
m = cls(root, args[0])
m.grid(sticky=NSEW)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
return 1
def dataready(self):
try:
s = self.remote.recv(1 << 12).decode()
except socket.error:
pass
else:
self.message_area['state'] = NORMAL
self.message_area.insert(END, s)
self.message_area['state'] = DISABLED
self.message_area.see(END)
self.after_handle = self.after(100, self.dataready)
def destroy(self):
if self.after_handle:
self.after_cancel(self.after_handle)
super().destroy()
def mouseClicked(self, e=None):
self.remote.sendall(self.send_area.get().encode() + b'\r\n')
self.send_area.delete(0, END)
keyPressed = mouseClicked
if __name__ == '__main__':
sys.exit(MultichatClient.main(sys.argv[1:]))
Simple_Client.pyw
#! /usr/bin/env python3
"""Provide a GUI for easy interactions with Multichat servers.
This program is an example of a first attempt at implementing a client
for interacting with a Multichat server through purely graphical means."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower#gmail.com>'
__date__ = '11 October 2012'
__version__ = 1, 0, 0
################################################################################
from tkinter.messagebox import *
from tkinter.constants import *
from safetkinter import *
import logging
import traceback
import _thread
import socket
import os
import traceback
import sys
import threadbox
################################################################################
class SimpleClient(Frame):
"SimpleClient(master, **kw) -> SimpleClient instance"
#classmethod
def main(cls):
"Create a GUI root and demonstrate the SimpleClient widget."
root = Tk()
root.title('Chat Client')
root.minsize(675, 450)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.bind_all('<Control-Key-a>', cls.handle_control_a)
frame = cls(root)
frame.grid(sticky=NSEW)
root.mainloop()
#staticmethod
def handle_control_a(event):
"Process Ctrl-A commands by widget type."
widget = event.widget
if isinstance(widget, Text):
widget.tag_add(SEL, 1.0, END + '-1c')
return 'break'
if isinstance(widget, Entry):
widget.selection_range(0, END)
return 'break'
def __init__(self, master, **kw):
"Initialize the SimpleClient instance with the widgets it contains."
super().__init__(master, **kw)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
# Build Widgets
self.output_area = ScrolledText(self, width=25, height=4, wrap=WORD)
self.input_area = Entry(self)
self.corner = Sizegrip(self)
# Place Widgets
self.output_area.grid(row=0, column=0, columnspan=2, sticky=NSEW)
self.input_area.grid(row=1, column=0, sticky=EW)
self.corner.grid(row=1, column=1, sticky=SE)
# Setup Widgets
self.output_area['state'] = DISABLED
self.input_area.bind('<Return>', self.send)
self.after_idle(self.connect)
def connect(self):
"Try connecting to a server to begin chatting."
self.connection = Connector(self, 'Chat Client').connection
if self.connection is None:
self._root().destroy()
else:
self.connection.setblocking(False)
self.after_idle(self.update)
def send(self, event):
"Send a message across the connection from the given widget."
self.connection.sendall(event.widget.get().encode() + b'\r\n')
event.widget.delete(0, END)
def update(self):
"Update the output area with any incoming messages."
self.output_area['state'] = NORMAL
try:
self.output_area.insert(END, self.connection.recv(1 << 12).decode())
except socket.error:
pass
else:
self.output_area.see(END)
finally:
self.output_area['state'] = DISABLED
self.after(100, self.update)
################################################################################
def start_thread(function, *args, **kwargs):
"Start a new thread of execution while logging any errors."
_thread.start_new_thread(log_errors, (function, args, kwargs))
def log_errors(function, args=(), kwargs={}):
"Execute a function with its arguments and log any exceptions."
try:
function(*args, **kwargs)
except SystemExit:
pass
except:
basename = os.path.basename(sys.argv[0])
filename = os.path.splitext(basename)[0] + '.log'
logging.basicConfig(filename=filename)
logging.error(traceback.format_exc())
################################################################################
class Dialog(Toplevel): # Copies tkinter.simpledialog.Dialog
"Dialog(parent, title=None) -> Dialog instance"
def __init__(self, parent, title=None):
"Initialize a Dialog window that takes focus away from the parent."
super().__init__(parent)
self.withdraw()
if parent.winfo_viewable():
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.grid(sticky=NSEW, padx=5, pady=5)
self.buttonbox()
if not self.initial_focus:
self.initial_focus = self
self.protocol('WM_DELETE_WINDOW', self.cancel)
if self.parent is not None:
self.geometry('+{}+{}'.format(parent.winfo_rootx() + 50,
parent.winfo_rooty() + 50))
self.deiconify()
self.initial_focus.focus_set()
try:
self.wait_visibility()
except tkinter.TclError:
pass
else:
self.grab_set()
self.wait_window(self)
def destroy(self):
"Destruct the Dialog window."
self.initial_focus = None
super().destroy()
def body(self, master):
"Create the body of this Dialog window."
pass
def buttonbox(self):
"Create the standard buttons and Dialog bindings."
box = Frame(self)
w = Button(box, text='OK', width=10, command=self.ok, default=ACTIVE)
w.grid(row=0, column=0, padx=5, pady=5)
w = Button(box, text='Cancel', width=10, command=self.cancel)
w.grid(row=0, column=1, padx=5, pady=5)
self.bind('<Return>', self.ok)
self.bind('<Escape>', self.cancel)
box.grid()
def ok(self, event=None):
"Validate and apply the changes made by this Dialog."
if not self.validate():
self.initial_focus.focus_set()
return
self.withdraw()
self.update_idletasks()
try:
self.apply()
finally:
self.cancel()
def cancel(self, event=None):
"Close the Dialong window and return to its parent."
if self.parent is not None:
self.parent.focus_set()
self.destroy()
def validate(self):
"Verify that the Dialog is in a valid state."
return True
def apply(self):
"Make any changes the Dialog wishes to accomplish."
pass
################################################################################
class Connector(Dialog):
"Connector(parent, title=None) -> Connector instance"
def body(self, master):
"Customize the Dialog window with some custom widgets."
self.connection = None
self.resizable(False, False)
# Build Widgets
self.prompt = Label(master, text='Enter server IP address:')
self.address = Entry(master)
# Place Widgets
self.prompt.grid(sticky=W, padx=30, pady=2)
self.address.grid(sticky=W, padx=30)
def buttonbox(self):
"Redefine the buttons at the bottom of the window."
w = Button(self, text='Connect', width=10, command=self.ok,
default=ACTIVE)
w.grid(sticky=E, padx=5, pady=5)
self.bind('<Return>', self.ok)
self.bind('<Escape>', self.cancel)
def validate(self):
"Ask a Consumator to make a connection with the given address."
c = Consumator(self, 'Chat Client', (self.address.get(), 8989))
if c.connection is None:
Message(self, icon=WARNING, type=OK, title='Warning',
message='Could not connect to address!').show()
return False
self.connection = c.connection
return True
################################################################################
class Consumator(Dialog):
"Consumator(parent, title, address) -> Consumator instance"
def __init__(self, parent, title, address):
"Initialize the Consumator with the server's address."
self.server_address = address
super().__init__(parent, title)
def body(self, master):
"Create the widgets for this Dialog and start the connection process."
self.connection = None
self.resizable(False, False)
# Build Widgets
self.message = Label(master, text='Trying to connect to address ...')
self.progress = Progressbar(master, orient=HORIZONTAL)
# Place Widgets
self.message.grid(sticky=W, padx=10, pady=2)
self.progress.grid(sticky=EW, padx=10, pady=2)
# Setup Widgets
self.progress.configure(mode='indeterminate', maximum=30)
self.progress.start()
result = []
start_thread(self.connect, result)
self.after_idle(self.poll, result)
def buttonbox(self):
"Cancel the creation of the buttons at the bottom of this Dialog."
pass
#threadbox.MetaBox.thread
def connect(self, result):
"Try connecting to the server address that was given."
try:
result.append(socket.create_connection(self.server_address, 10))
except socket.timeout:
result.append(None)
def poll(self, result):
"Find out if the any connection information is available yet."
if result:
self.connection = result[0]
self.cancel()
else:
self.after(100, self.poll, result)
################################################################################
if __name__ == '__main__':
log_errors(SimpleClient.main)
affinity.py
"""Allow a simple way to ensure execution is confined to one thread.
This module defines the Affinity data type that runs code on a single thread.
An instance of the class will execute functions only on the thread that made
the object in the first place. The class is useful in a GUI's main loop."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower#gmail.com>'
__date__ = '4 June 2012'
__version__ = 1, 0, 0
################################################################################
import sys
import _thread
import queue
################################################################################
def slots(names=''):
"Sets the __slots__ variable in the calling context with private names."
sys._getframe(1).f_locals['__slots__'] = \
tuple('__' + name for name in names.replace(',', ' ').split())
################################################################################
class Affinity:
"Affinity() -> Affinity instance"
slots('thread, action')
def __init__(self):
"Initializes instance with thread identity and job queue."
self.__thread = _thread.get_ident()
self.__action = queue.Queue()
def __call__(self, func, *args, **kwargs):
"Executes function on creating thread and returns result."
if _thread.get_ident() == self.__thread:
while not self.__action.empty():
self.__action.get_nowait()()
return func(*args, **kwargs)
delegate = _Delegate(func, args, kwargs)
self.__action.put_nowait(delegate)
return delegate.value
################################################################################
class _Delegate:
"_Delegate(func, args, kwargs) -> _Delegate instance"
slots('func, args, kwargs, mutex, value, error')
def __init__(self, func, args, kwargs):
"Initializes instance from arguments and prepares to run."
self.__func = func
self.__args = args
self.__kwargs = kwargs
self.__mutex = _thread.allocate_lock()
self.__mutex.acquire()
def __call__(self):
"Executes code with arguments and allows value retrieval."
try:
self.__value = self.__func(*self.__args, **self.__kwargs)
self.__error = False
except:
self.__value = sys.exc_info()[1]
self.__error = True
self.__mutex.release()
#property
def value(self):
"Waits for value availability and raises or returns data."
self.__mutex.acquire()
if self.__error:
raise self.__value
return self.__value
threadbox.py
"""Provide a way to run instance methods on a single thread.
This module allows hierarchical classes to be cloned so that their instances
run on one thread. Method calls are automatically routed through a special
execution engine. This is helpful when building thread-safe GUI code."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower#gmail.com>'
__date__ = '9 October 2012'
__version__ = 1, 0, 1
################################################################################
import functools
import affinity
################################################################################
class _object: __slots__ = '_MetaBox__exec', '__dict__'
################################################################################
class MetaBox(type):
"MetaBox(name, bases, classdict, old=None) -> MetaBox instance"
__REGISTRY = {object: _object}
__SENTINEL = object()
#classmethod
def clone(cls, old, update=()):
"Creates a class preferring thread affinity after update."
classdict = dict(old.__dict__)
classdict.update(update)
return cls(old.__name__, old.__bases__, classdict, old)
#classmethod
def thread(cls, func):
"Marks a function to be completely threaded when running."
func.__thread = cls.__SENTINEL
return func
def __new__(cls, name, bases, classdict, old=None):
"Allocates space for a new class after altering its data."
assert '__new__' not in classdict, '__new__ must not be defined!'
assert '__slots__' not in classdict, '__slots__ must not be defined!'
assert '__module__' in classdict, '__module__ must be defined!'
valid = []
for base in bases:
if base in cls.__REGISTRY:
valid.append(cls.__REGISTRY[base])
elif base in cls.__REGISTRY.values():
valid.append(base)
else:
valid.append(cls.clone(base))
for key, value in classdict.items():
if callable(value) and (not hasattr(value, '_MetaBox__thread') or
value.__thread is not cls.__SENTINEL):
classdict[key] = cls.__wrap(value)
classdict.update({'__new__': cls.__new, '__slots__': (), '__module__':
'{}.{}'.format(__name__, classdict['__module__'])})
cls.__REGISTRY[object() if old is None else old] = new = \
super().__new__(cls, name, tuple(valid), classdict)
return new
def __init__(self, name, bases, classdict, old=None):
"Initializes class instance while ignoring the old class."
return super().__init__(name, bases, classdict)
#staticmethod
def __wrap(func):
"Wraps a method so execution runs via an affinity engine."
#functools.wraps(func)
def box(self, *args, **kwargs):
return self.__exec(func, self, *args, **kwargs)
return box
#classmethod
def __new(meta, cls, *args, **kwargs):
"Allocates space for instance and finds __exec attribute."
self = object.__new__(cls)
if 'master' in kwargs:
self.__exec = kwargs['master'].__exec
else:
valid = tuple(meta.__REGISTRY.values())
for value in args:
if isinstance(value, valid):
self.__exec = value.__exec
break
else:
self.__exec = affinity.Affinity()
return self
safetkinter.py
"""Register tkinter classes with threadbox for immediate usage.
This module clones several classes from the tkinter library for use with
threads. Instances from these new classes should run on whatever thread
the root was created on. Child classes inherit the parent's safety."""
__author__ = 'Stephen "Zero" Chappell <Noctis.Skytower#gmail.com>'
__date__ = '4 June 2012'
__version__ = 1, 0, 0
################################################################################
import time
import tkinter.filedialog
import tkinter.font
import tkinter.messagebox
import tkinter.scrolledtext
import tkinter.ttk
import threadbox
################################################################################
tkinter.NoDefaultRoot()
#threadbox.MetaBox.thread
def mainloop(self):
"Creates a synthetic main loop so that threads can still run."
while True:
try:
self.update()
except tkinter.TclError:
break
else:
time.sleep(tkinter._tkinter.getbusywaitinterval() / 1000)
threadbox.MetaBox.clone(tkinter.Misc, {'mainloop': mainloop})
################################################################################
OldButton = threadbox.MetaBox.clone(tkinter.Button)
Canvas = threadbox.MetaBox.clone(tkinter.Canvas)
OldFrame = threadbox.MetaBox.clone(tkinter.Frame)
Menu = threadbox.MetaBox.clone(tkinter.Menu)
PhotoImage = threadbox.MetaBox.clone(tkinter.PhotoImage)
Spinbox = threadbox.MetaBox.clone(tkinter.Spinbox)
StringVar = threadbox.MetaBox.clone(tkinter.StringVar)
Text = threadbox.MetaBox.clone(tkinter.Text)
Tk = threadbox.MetaBox.clone(tkinter.Tk)
Toplevel = threadbox.MetaBox.clone(tkinter.Toplevel)
################################################################################
Button = threadbox.MetaBox.clone(tkinter.ttk.Button)
Checkbutton = threadbox.MetaBox.clone(tkinter.ttk.Checkbutton)
Entry = threadbox.MetaBox.clone(tkinter.ttk.Entry)
Frame = threadbox.MetaBox.clone(tkinter.ttk.Frame)
Label = threadbox.MetaBox.clone(tkinter.ttk.Label)
Labelframe = threadbox.MetaBox.clone(tkinter.ttk.Labelframe)
Progressbar = threadbox.MetaBox.clone(tkinter.ttk.Progressbar)
Radiobutton = threadbox.MetaBox.clone(tkinter.ttk.Radiobutton)
Scale = threadbox.MetaBox.clone(tkinter.ttk.Scale)
Scrollbar = threadbox.MetaBox.clone(tkinter.ttk.Scrollbar)
Sizegrip = threadbox.MetaBox.clone(tkinter.ttk.Sizegrip)
Treeview = threadbox.MetaBox.clone(tkinter.ttk.Treeview)
################################################################################
Directory = threadbox.MetaBox.clone(tkinter.filedialog.Directory)
Font = threadbox.MetaBox.clone(tkinter.font.Font)
Message = threadbox.MetaBox.clone(tkinter.messagebox.Message)
ScrolledText = threadbox.MetaBox.clone(tkinter.scrolledtext.ScrolledText)
Related
Sorry for the confusing title basically what I'm trying to figure out how to import a frame from one script into another. I'm not sure how I would call it since it has so many functions. Here are my two scripts:
File Name - wrapper:
import tkinter as tk
import workingcatch
from tkinter import *
root = tk.Tk()
outputframe = LabelFrame(master=root, width=800, height=700) #where i want to import the script
outputframe.pack(side=LEFT, padx=10,pady=10)
root.geometry('1280x720')
root.mainloop()
File Name - workingcatch:
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
import tkinter as tk
import logging
import time
import sys
info = logging.getLogger(__name__).info
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
platform = windows = 'mswin'
# define dummy subprocess to generate some output
cmd = [sys.executable or "python", "-u", "-c", """
import itertools, time
for i in itertools.count():
print(i)
time.sleep(0.5)
exit()
""", "exit"]
class OStream(Thread):
enable_print = True
def handel_line(self, line):
if platform == windows:
# Windows uses: "\r\n" instead of "\n" for new lines.
line = line.replace(b"\r\n", b"\n")
if self.enable_print:
info("got: %r", line)
if self.stream_print is not None:
self.stream_print(line)
def stop(self):
self.alive = False
def run(self):
while self.alive:
for s in self.ostrams:
line = s.stdout.readline()
if line:
self.handel_line(line)
time.sleep(0.2)
info("OStream Exit")
def pipe_proc(self, stream):
self.ostrams.append(stream)
def stream_callback(self, func):
self.stream_print = func
def __init__(self):
self.ostrams = []
self.alive = True
self.stream_print = None
Thread.__init__(self)
class Scrolable_Frame(tk.Frame):
def get(self):
return self.interior
def __init__(self, master):
tk.Frame.__init__(self, master)
self.scroll = tk.Scrollbar(self)
self.scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas = tk.Canvas(
self, bd=0, highlightthickness=0,
yscrollcommand=self.scroll.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
self.scroll.config(command=self.canvas.yview)
self.canvas.xview_moveto(0)
self.canvas.yview_moveto(0)
self.interior = tk.Frame(self.canvas)
interior_id = self.canvas.create_window(
0, 0, window=self.interior, anchor=tk.NW
)
def _configure_interior(_):
size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
self.canvas.config(width=self.interior.winfo_reqwidth())
self.interior.bind('<Configure>', _configure_interior)
def _configure_canvas(_):
if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
self.canvas.itemconfigure(interior_id, width=self.canvas.winfo_width())
self.canvas.bind('<Configure>', _configure_canvas)
def _on_mousewheel(event):
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), 'units')
self.canvas.bind_all("<MouseWheel>", _on_mousewheel)
class ShowProcessOutputDemo(tk.Tk):
def __init__(self):
"""Start subprocess, make GUI widgets."""
tk.Tk.__init__(self)
self.geometry('300x200+500+300')
self.protocol("WM_DELETE_WINDOW", self.stop)
self.proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
self.ostream = OStream()
self.ostream.pipe_proc(self.proc)
self.ostream.stream_callback(self.add_label)
self.ostream.start()
self.exit_button = tk.Button(
self, text="Stop subprocess", command=self.stop
)
self.exit_button.pack(pady=20)
self.scolable_frame = Scrolable_Frame(self)
self.scolable_frame.pack(
expand=True, fill=tk.BOTH, pady=20, padx=20
)
def add_label(self, line):
line_text = 'OStream line content: {0}'.format(line[:-1].decode())
tk.Label(
self.scolable_frame.get(), text=line_text
).pack(anchor=tk.CENTER, expand=True, fill=tk.X)
def stop(self):
"""Stop subprocess and quit GUI."""
self.ostream.stop()
self.proc.kill()
self.proc.stdout.close()
self.proc.wait(timeout=2)
info("GUI Exit")
self.quit()
if __name__ == '__main__':
app = ShowProcessOutputDemo()
app.mainloop()
So what I'm trying to do is get the output from "workingcatch" into 'wrapper's frame.
Thanks for reading
Is there a way to set the background color of a tkinter simpledialog window?
I am trying to implement a darkmode theme by changing background colors and am struggling with getting the simpledialog & filedialog windows to follow suit.
My current approach is to check right after creating a new toplevel, whether darkmode should apply, but I couldn't find any information in the Tkinter Dialogs documentation regarding options for the simpledialogs
top = tk.Toplevel()
if self.config["global"]["darkmode"] == "True":
top.configure(bg='black')
Simpledialog call:
cur_row = tk.simpledialog.askinteger("Row Selection", msg_string, parent = parent)
There isn't an existing argument, but I did some digging through the code for tkinter.simpledialog and it turns out that askinteger is based on the Toplevel class anyway so if you change tkinter/simpledialog.py to the code at the end of this answer, it adds a bg parameter to simpledialog that you can use as follows:
cur_row = tk.simpledialog.askinteger("Row Selection", msg_string, parent = parent, bg = "black")
I hope this helps :)
Here is the full code:
#
# An Introduction to Tkinter
#
# Copyright (c) 1997 by Fredrik Lundh
#
# This copyright applies to Dialog, askinteger, askfloat and asktring
#
# fredrik#pythonware.com
# http://www.pythonware.com
#
"""This modules handles dialog boxes.
It contains the following public symbols:
SimpleDialog -- A simple but flexible modal dialog box
Dialog -- a base class for dialogs
askinteger -- get an integer from the user
askfloat -- get a float from the user
askstring -- get a string from the user
"""
from tkinter import *
from tkinter import messagebox
import tkinter # used at _QueryDialog for tkinter._default_root
class SimpleDialog:
def __init__(self, master,
text='', buttons=[], default=None, cancel=None,
title=None, class_=None):
if class_:
self.root = Toplevel(master, class_=class_)
else:
self.root = Toplevel(master)
if title:
self.root.title(title)
self.root.iconname(title)
self.message = Message(self.root, text=text, aspect=400)
self.message.pack(expand=1, fill=BOTH)
self.frame = Frame(self.root)
self.frame.pack()
self.num = default
self.cancel = cancel
self.default = default
self.root.bind('<Return>', self.return_event)
for num in range(len(buttons)):
s = buttons[num]
b = Button(self.frame, text=s,
command=(lambda self=self, num=num: self.done(num)))
if num == default:
b.config(relief=RIDGE, borderwidth=8)
b.pack(side=LEFT, fill=BOTH, expand=1)
self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
self._set_transient(master)
def _set_transient(self, master, relx=0.5, rely=0.3):
widget = self.root
widget.withdraw() # Remain invisible while we figure out the geometry
widget.transient(master)
widget.update_idletasks() # Actualize geometry information
if master.winfo_ismapped():
m_width = master.winfo_width()
m_height = master.winfo_height()
m_x = master.winfo_rootx()
m_y = master.winfo_rooty()
else:
m_width = master.winfo_screenwidth()
m_height = master.winfo_screenheight()
m_x = m_y = 0
w_width = widget.winfo_reqwidth()
w_height = widget.winfo_reqheight()
x = m_x + (m_width - w_width) * relx
y = m_y + (m_height - w_height) * rely
if x+w_width > master.winfo_screenwidth():
x = master.winfo_screenwidth() - w_width
elif x < 0:
x = 0
if y+w_height > master.winfo_screenheight():
y = master.winfo_screenheight() - w_height
elif y < 0:
y = 0
widget.geometry("+%d+%d" % (x, y))
widget.deiconify() # Become visible at the desired location
def go(self):
self.root.wait_visibility()
self.root.grab_set()
self.root.mainloop()
self.root.destroy()
return self.num
def return_event(self, event):
if self.default is None:
self.root.bell()
else:
self.done(self.default)
def wm_delete_window(self):
if self.cancel is None:
self.root.bell()
else:
self.done(self.cancel)
def done(self, num):
self.num = num
self.root.quit()
class Dialog(Toplevel):
'''Class to open dialogs.
This class is intended as a base class for custom dialogs
'''
def __init__(self, parent, title = None, bg = None):
'''Initialize a dialog.
Arguments:
parent -- a parent window (the application window)
title -- the dialog title
'''
Toplevel.__init__(self, parent, bg = bg)
self.withdraw() # remain invisible for now
# If the master is not viewable, don't
# make the child transient, or else it
# would be opened withdrawn
if parent.winfo_viewable():
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
self.buttonbox()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
if self.parent is not None:
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.deiconify() # become visible now
self.initial_focus.focus_set()
# wait for window to appear on screen before calling grab_set
self.wait_visibility()
self.grab_set()
self.wait_window(self)
def destroy(self):
'''Destroy the window'''
self.initial_focus = None
Toplevel.destroy(self)
#
# construction hooks
def body(self, master):
'''create dialog body.
return widget that should have initial focus.
This method should be overridden, and is called
by the __init__ method.
'''
pass
def buttonbox(self):
'''add standard button box.
override if you do not want the standard buttons
'''
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
#
# standard button semantics
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
try:
self.apply()
finally:
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
if self.parent is not None:
self.parent.focus_set()
self.destroy()
#
# command hooks
def validate(self):
'''validate the data
This method is called automatically to validate the data before the
dialog is destroyed. By default, it always validates OK.
'''
return 1 # override
def apply(self):
'''process the data
This method is called automatically to process the data, *after*
the dialog is destroyed. By default, it does nothing.
'''
pass # override
# --------------------------------------------------------------------
# convenience dialogues
class _QueryDialog(Dialog):
def __init__(self, title, prompt,
initialvalue=None,
minvalue = None, maxvalue = None,
parent = None, bg = None):
if not parent:
parent = tkinter._default_root
self.prompt = prompt
self.minvalue = minvalue
self.maxvalue = maxvalue
self.initialvalue = initialvalue
Dialog.__init__(self, parent, title, bg = bg)
def destroy(self):
self.entry = None
Dialog.destroy(self)
def body(self, master):
w = Label(master, text=self.prompt, justify=LEFT)
w.grid(row=0, padx=5, sticky=W)
self.entry = Entry(master, name="entry")
self.entry.grid(row=1, padx=5, sticky=W+E)
if self.initialvalue is not None:
self.entry.insert(0, self.initialvalue)
self.entry.select_range(0, END)
return self.entry
def validate(self):
try:
result = self.getresult()
except ValueError:
messagebox.showwarning(
"Illegal value",
self.errormessage + "\nPlease try again",
parent = self
)
return 0
if self.minvalue is not None and result < self.minvalue:
messagebox.showwarning(
"Too small",
"The allowed minimum value is %s. "
"Please try again." % self.minvalue,
parent = self
)
return 0
if self.maxvalue is not None and result > self.maxvalue:
messagebox.showwarning(
"Too large",
"The allowed maximum value is %s. "
"Please try again." % self.maxvalue,
parent = self
)
return 0
self.result = result
return 1
class _QueryInteger(_QueryDialog):
errormessage = "Not an integer."
def getresult(self):
return self.getint(self.entry.get())
def askinteger(title, prompt, **kw):
'''get an integer from the user
Arguments:
title -- the dialog title
prompt -- the label text
**kw -- see SimpleDialog class
Return value is an integer
'''
d = _QueryInteger(title, prompt, **kw)
return d.result
class _QueryFloat(_QueryDialog):
errormessage = "Not a floating point value."
def getresult(self):
return self.getdouble(self.entry.get())
def askfloat(title, prompt, **kw):
'''get a float from the user
Arguments:
title -- the dialog title
prompt -- the label text
**kw -- see SimpleDialog class
Return value is a float
'''
d = _QueryFloat(title, prompt, **kw)
return d.result
class _QueryString(_QueryDialog):
def __init__(self, *args, **kw):
if "show" in kw:
self.__show = kw["show"]
del kw["show"]
else:
self.__show = None
_QueryDialog.__init__(self, *args, **kw)
def body(self, master):
entry = _QueryDialog.body(self, master)
if self.__show is not None:
entry.configure(show=self.__show)
return entry
def getresult(self):
return self.entry.get()
def askstring(title, prompt, **kw):
'''get a string from the user
Arguments:
title -- the dialog title
prompt -- the label text
**kw -- see SimpleDialog class
Return value is a string
'''
d = _QueryString(title, prompt, **kw)
return d.result
if __name__ == '__main__':
def test():
root = Tk()
def doit(root=root):
d = SimpleDialog(root,
text="This is a test dialog. "
"Would this have been an actual dialog, "
"the buttons below would have been glowing "
"in soft pink light.\n"
"Do you believe this?",
buttons=["Yes", "No", "Cancel"],
default=0,
cancel=2,
title="Test Dialog")
print(d.go())
print(askinteger("Spam", "Egg count", initialvalue=12*12))
print(askfloat("Spam", "Egg weight\n(in tons)", minvalue=1,
maxvalue=100))
print(askstring("Spam", "Egg label"))
t = Button(root, text='Test', command=doit)
t.pack()
q = Button(root, text='Quit', command=t.quit)
q.pack()
t.mainloop()
test()
I just added by own buttonbox method, found the children and configured them.
class bug_dialog(tk.simpledialog.Dialog):
def buttonbox(self):
super().buttonbox()
for _ in self.children.values(): _.configure(bg='light green')
self.configure(bg='light green')
I'm working on a little project and made a little on-screen keyboard as a tkinter Toplevel
my application is buildt like this:
Root-Window (Tk-Widget)
input 1 (Entry-Widget)
input 2 (Entry-Widget)
input 3 (Text-Widget)
on_screen-keyboard (Toplevel-Widget)
the Toplevel-Widget contains Buttons, with callbacks that should enter text in the entries (just like keyboard-Buttons)
What I want is a communication between children of the keyboard/the keyboard and the last active input-Widget. My Problem is, that I don't know, how to say the keyboard, to which input-Widget it should send the message.
import tkinter as tk
from tkinter import ttk
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.active_input = tk.Variable(value=None)
ttk.Button(self, text="show Keyboard", command=lambda: Keyboard(self)).pack()
self.text = tk.StringVar(value="")
self.input1 = ttk.Entry(self)
self.input1.bind("<FocusIn>", lambda e: self.active_input.set(self.input1))
self.input2 = ttk.Entry(self)
self.input2.bind("<FocusIn>", lambda e: self.active_input.set(self.input2))
self.input3 = tk.Text(self, height=3, width=15)
self.input3.bind("<FocusIn>", lambda e: self.active_input.set(self.input3))
self.input1.pack()
self.input3.pack()
self.input2.pack()
class Keyboard(tk.Toplevel):
OPENED = False
NAME = "- Keyboard -"
NUM = [{"text":"1", "width":1},
{"text":"2", "width":1},
{"text":"3", "width":2}]
CHAR= [{"text":"A", "width":1},
{"text":"B", "width":1},
{"text":"C", "width":2}]
def __init__(self, master):
if not Keyboard.OPENED:
Keyboard.OPENED = True
print("keyboard opened!")
self.master = master
tk.Toplevel.__init__(self, master)
self.title(self.NAME)
self.protocol("WM_DELETE_WINDOW", self.close)
self.keyb_nb = ttk.Notebook(self)
self.keyb_nb.pack()
self.num_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.num_tab, Keyboard.NUM,2)
self.keyb_nb.add(self.num_tab, text="123")
self.char_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.char_tab, Keyboard.CHAR, 2)
self.keyb_nb.add(self.char_tab, text="ABC")
def createPad(self, master, pad:list, max_col):
self.co_count = 0
self.ro = 1
for button in pad:
button["id"] = ttk.Button(master, width=6*button["width"], text=button["text"], command=self.bclicked(button))
if self.co_count >= max_col:
self.ro = self.ro + 1
self.co_count = 0
button["id"].grid(row=self.ro, columnspan=button["width"], column=self.co_count)
self.co_count = self.co_count+button["width"]
def bclicked(self, button:dict):
"""
reciver = self.master.active_input #I think the Problem here is, that the variable contains a string, not a widget
reciever.focus_force()
reciever.insert(index=tk.INSERT, string=button["text"])
"""
pass
def close(self):
Keyboard.OPENED = False
self.destroy()
print("keyboard closed!")
root = MainWindow()
root.mainloop()
Here the init of the Mainwindow and the bclicked of the Keyboard class are important...
the code is debug-ready
I would prefer a solution, similar to the communication in the internet (sender=button, receiver-id, message), but very welcome every working solution
btw: I'm also looking for a solution, how I don't have to force the input to focus and the Toplevel stays an the highest layer of the screen (that if I focus the Tk-Widget/one of the inputs/the button, the keyboard will stay in front of it)
SUMMARY: how do I find out, which of the 3 input-widgets was active at last, when the keyboard-toplevel has already the focus?
I may made more changes than needed, but mainly focus on the method keyboard_triger() and pass_key_to_master(), this two use the idea that the variable master implements, having access to call methods out of scope.
Olso the method set_focused_object() stores a reference to the last object beeng focused, note than it stores the widget and not the event, it's easyer than searching each time the object
import tkinter as tk
from tkinter import ttk
class MainWindow(tk.Tk):
def keyboard_triger(self, key):
# to identify wath object is just use
# isinstance(self.active_input, ttk.Entry)
self.active_input.insert(tk.END, key)
def new_keyboard(self):
Keyboard(self)
def set_focused_object(self, event):
self.active_input = event.widget
def __init__(self):
tk.Tk.__init__(self)
self.active_input = None
ttk.Button(self, text="Show Keyboard", command=self.new_keyboard).pack()
self.input1 = ttk.Entry(self)
self.input1.bind("<FocusIn>", self.set_focused_object)
self.input1.pack()
self.input2 = ttk.Entry(self)
self.input2.bind("<FocusIn>", self.set_focused_object)
self.input2.pack()
self.input3 = tk.Text(self, height=3, width=15)
self.input3.bind("<FocusIn>", self.set_focused_object)
self.input3.pack()
class Keyboard(tk.Toplevel):
def pass_key_to_master(self, key):
self.master.keyboard_triger(key)
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.title('Keyboard')
# this way of agruping keys stores the kwags
# of the drawing method
keys = {
'A': {'x': 0, 'y': 0},
'B': {'x': 20, 'y': 20},
'C': {'x': 50, 'y': 50}
}
# expected structure
# {string key: reference to the button}
self.buttons = {}
for i in keys:
self.buttons[i] = tk.Button( # i=i is required to make a instance
self, text=i, command=lambda i=i: self.pass_key_to_master(i)
)
self.buttons[i].place(**keys[i])
if __name__ == '__main__':
root = MainWindow()
root.mainloop()
Your code maybe could have a better construction.(But I didn't revise your code construction.)
Followed by your code,I use a global variable.And fix some errors in your code.And it could work normally in my computer.
import tkinter as tk
from tkinter import ttk
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.active_input = tk.Variable(value=None)
ttk.Button(self, text="show Keyboard", command=lambda: Keyboard(self)).pack()
global focusedWidget
focusedWidget = None
self.text = tk.StringVar(value="")
self.input1 = ttk.Entry(self)
self.input1.bind("<FocusIn>", self.getFocusWidget)
self.input2 = ttk.Entry(self)
self.input2.bind("<FocusIn>", self.getFocusWidget)
self.input3 = tk.Text(self, height=3, width=15)
self.input3.bind("<FocusIn>", self.getFocusWidget)
self.input1.pack()
self.input3.pack()
self.input2.pack()
def getFocusWidget(self,event): # this function could be a static function
global focusedWidget
focusedWidget = event.widget
class Keyboard(tk.Toplevel):
OPENED = False
NAME = "- Keyboard -"
NUM = [{"text":"1", "width":1},
{"text":"2", "width":1},
{"text":"3", "width":2}]
CHAR= [{"text":"A", "width":1},
{"text":"B", "width":1},
{"text":"C", "width":2}]
def __init__(self, master):
if not Keyboard.OPENED:
Keyboard.OPENED = True
print("keyboard opened!")
self.master = master
tk.Toplevel.__init__(self, master)
self.title(self.NAME)
self.protocol("WM_DELETE_WINDOW", self.close)
self.keyb_nb = ttk.Notebook(self)
self.keyb_nb.pack()
self.num_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.num_tab, Keyboard.NUM,2)
self.keyb_nb.add(self.num_tab, text="123")
self.char_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.char_tab, Keyboard.CHAR, 2)
self.keyb_nb.add(self.char_tab, text="ABC")
def createPad(self, master, pad:list, max_col):
self.co_count = 0
self.ro = 1
for button in pad:
button["id"] = ttk.Button(master, width=6*button["width"], text=button["text"], command=lambda button=button:self.bclicked(button)) # this lambda expression has some errors.
if self.co_count >= max_col:
self.ro = self.ro + 1
self.co_count = 0
button["id"].grid(row=self.ro, columnspan=button["width"], column=self.co_count)
self.co_count = self.co_count+button["width"]
def bclicked(self, button:dict):
global focusedWidget
"""
reciver = self.master.active_input #I think the Problem here is, that the variable contains a string, not a widget
reciever.focus_force()
reciever.insert(index=tk.INSERT, string=button["text"])
"""
if not focusedWidget: # If user hasn't click a entry or text widget.
print("Please select a entry or text")
return
if focusedWidget.widgetName=='ttk::entry': # use if statement to check the type of selected entry.
focusedWidget.insert(index=tk.INSERT,string=button["text"])
else:
focusedWidget.insert("end",button["text"])
def close(self):
Keyboard.OPENED = False
self.destroy()
print("keyboard closed!")
root = MainWindow()
root.mainloop()
I am working on front-end where it is necessary to change the hand cursor to a busy cursor on a button click. But the code i have is throwing "type error". I just want the code to display a button which on clicking changes from hand cursor to busy cursor and then back to normal cursor.
Here's the code i have tried so far:
import threading
from threading import Thread
from threading import Event
import queue
sem=threading.Semaphore()
def setup_for_long_running_task(self):
print("start")
self.f1.config(cursor="wait") # Set the cursor to busy
sem.acquire()
return_que = queue.Queue(1)
workThread = Thread(target=lambda q, w_self: \
q.put(self.long_running_task()),
args=return_que)
workThread.start()
self.f1.after(5000,use_results_of_long_running_task(self,workThread,return_que)) # 500ms is half a second
sem.release()
print("stop")
def long_running_task(self):
Event().wait(3.0) # Simulate long running task
def use_results_of_long_running_task(self, workThread,return_que):
ThreadRunning = 1
while ThreadRunning:
Event().wait(0.1) # this is set to .1 seconds. Adjust for your process
ThreadRunning = workThread.is_alive()
while not return_que.empty():
return_list = return_que.get()
self.f1.config(cursor="")
The error message:
TypeError: <lambda>() argument after * must be an iterable, not Queue.
Exception in thread Thread-7:
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\threading.py", line 917, in
_bootstrap_inner
self.run()
File "C:\ProgramData\Anaconda3\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
TypeError: <lambda>() argument after * must be an iterable, not Queue
Thread needs args as tuple or list (iterable objects), even if you have only one argument
args=(return_que,)
Your lambda expects two arguments lambda q, w_self: but you have only one element in args
args=(return_que, ???)
but I don't know what you want to use as w_self.
below a script that use a threat and change cursor type when you launch thread and after it is end. It simulate a countdown with a time interval of 1 second.
I' ve set the parent cursor but if you keep a reference to another widget, such as a button you can make the same job.
Calling MyThread class force to set cursor type as
self.parent.config(cursor="watch")
and when thread is end, self.check = False we reset cursor type
self.parent.config(cursor="")
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import threading
import queue
import time
class MyThread(threading.Thread):
def __init__(self,parent, queue, count):
threading.Thread.__init__(self)
self.parent = parent
self.parent.config(cursor="watch")
self.queue = queue
self.check = True
self.count = count
def stop(self):
self.check = False
def run(self):
while self.check:
if self.count <1:
self.parent.config(cursor="")
self.check = False
else:
self.count -= 1
time.sleep(1)
self.queue.put(self.count)
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.parent.config(cursor="")
self.queue = queue.Queue()
self.my_thread = None
self.spins = tk.IntVar()
self.count = tk.IntVar()
self.spins.set(5)
self.init_ui()
def init_ui(self):
f = ttk.Frame()
ttk.Label(f, text = "Set count").pack()
tk.Spinbox(f, from_=2, to=20, textvariable= self.spins).pack()
ttk.Label(f, text = "Get count").pack()
ttk.Label(f, textvariable = self.count).pack()
w = ttk.Frame()
self.start = ttk.Button(w, text="Start", command=self.start_count).pack()
ttk.Button(w, text="Stop", command=self.stop_count).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def start_count(self):
if (threading.active_count()!=0):
self.my_thread = MyThread(self.parent, self.queue,self.spins.get())
self.my_thread.start()
self.on_periodic_call()
def stop_count(self):
if self.my_thread is not None:
if(threading.active_count()!=1):
self.my_thread.stop()
def on_periodic_call(self):
self.on_check_queue()
if self.my_thread.is_alive():
self.after(1, self.on_periodic_call)
else:
pass
def on_check_queue(self):
while self.queue.qsize():
try:
self.count.set(self.queue.get(0))
except queue.Empty:
pass
def on_close(self):
if self.my_thread is not None:
if(threading.active_count()!=1):
self.my_thread.stop()
self.parent.on_exit()
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_style()
self.set_title()
Main(self)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Simple App')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel("Simple App", "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()
So I am trying to build a GUI where I enter some information, clear the entry fields, and then add new entry fields. However, when I try to clear the frame from the root via grid_remove, the application freezes. The relevant code is below.
import tkinter
from threading import Thread
class PinGui(tkinter.Frame):
def __init__(self, client):
self.client = client
self.root = client.root
self.add_pin = client.add_pin
self.end = client.end
tkinter.Frame.__init__(self, self.root)
self.grid_widgets()
self.grid_buttons()
self.bind_keys()
self.grid(padx=32, pady=32)
def grid_buttons(self, b1='Add', b2='Reset', b3='Quit'):
self.addButton = tkinter.Button(self, text=b1, command=self.validate)
self.resetButton = tkinter.Button(self, text=b2, command=self.reset)
self.quitButton = tkinter.Button(self, text=b3, command=self.end)
self.buttons = [self.addButton, self.resetButton, self.quitButton]
for i in range(3): self.buttons[i].grid(row=i, column=11)
def grid_widgets(self):
widths = [3,3,4,4,6]
self.pin_vars = []
self.pin_fields = []
for i in range(5):
self.pin_vars.append(tkinter.StringVar())
self.pin_fields.append(
tkinter.Entry(self,width=widths[i], textvariable=self.pin_vars[i])
)
self.pin_fields[i].grid(row=0, column=2*i, padx=3)
self.pin_fields[0].focus_set()
def bind_keys(self):
self.root.bind_all("<Return>", self.validate)
self.root.bind_all("<Escape>", self.end)
def validate(self, args=None):
self.client.pin = []
for field in self.pin_fields:
self.client.pin.append(field.get())
Thread(target=self.add_pin).start()
def ungrid(self):
for field in self.pin_fields: field.grid_remove()
for button in self.buttons: button.grid_remove()
self.display.grid_remove()
And:
class PinClient:
def __init__(self):
self.root = tkinter.Tk()
self.gui = PinGui(self)
self.pins = []
def add_pin(self):
self.gui.reset()
if 'display' in self.__dict__:
self.pins.append(self.pin)
self.display.add_pin(self.pin)
self.ping("Enter PIN for Comp %s:" % len(self.display.col1))
if len(self.display.col1) > 5:
self.end() # THIS IS WHERE IT FREEZES
else:
self.subject = self.pin
self.display = Display(self.root, self.pin)
self.display.grid(row=1, padx=32, pady=32)
self.ping("Enter PIN for Comp 1:")
def ping(self, msg):
self.gui.d_var.set(msg)
def end(self, args=None):
self.gui.ungrid()
class Display(tkinter.Frame):
def __init__(self, master, pin):
tkinter.Frame.__init__(self, master)
self.pin = pin
self.col1 = []
self.col2 = []
self.col1.append(tkinter.Label(self, text="Subject:"))
self.col2.append(tkinter.Label(self, text=self.pin))
self.grid_widgets()
def grid_widgets(self):
self.ungrid()
for i in range(len(self.col1)):
self.col1[i].grid(row=i, column=0)
self.col2[i].grid(row=i, column=1)
def ungrid(self):
for i in range(len(self.col1)):
self.col1[i].grid_remove()
self.col2[i].grid_remove()
def add_pin(self, pin):
self.col1.append(tkinter.Label(self, text="Comp %s:" % len(self.col1)))
self.col2.append(tkinter.Label(self, text=pin))
i = len(self.col1)
self.col1[i-1].grid(row=i, column=0)
self.col2[i-1].grid(row=i, column=1)
It seems to be somehow related to the threading, but I haven't been able to find any reason this should freeze. Any help is greatly appreciated!
Tkinter is not thread safe. If you do anything in a thread other than the main thread that touches a GUI object then you will get unpredictable results. Almost certainly, it is threading that is causing your problem, since you are trying to access widgets from the worker thread.