Tkinter textvariable does not work in a secondary window? - python-3.x

Because when I use textvariable from a secondary window called from a command in another window the variable.set () is not reflected in that secondary window.
example:
import tkinter as tk
def test():
ven=tk.Tk()
v1=tk.StringVar()
v1.set('TEST')
print(v1.get())
tk.Label(ven, textvariable=v1).pack()
ven.mainloop()
win=tk.Tk()
tk.Button(text='BOTON',command=test).pack()
win.mainloop()
In this case the message 'TEST' set through 'set' is not registered in the Label textvariable..
Why does this happen?

Your problem comes from the fact that you have several Tk instances running simultaneously. Tkinter is based on the the Tk gui framework which is a tcl library. Therefore each Tk instance is not just a window, it's also a tcl interpreter, therefore, when you have several Tk instances, they cannot share StrinVar because the value of the StrinVar is defined in one interpreter (here win) which does not communicate with the other one (ven).
To avoid this kind of issue, just don't use several Tk instances, use Toplevel windows instead:
import tkinter as tk
def test():
ven = tk.Toplevel(win)
v1 = tk.StringVar(win)
v1.set('TEST')
print(v1.get())
tk.Label(ven, textvariable=v1).pack()
win = tk.Tk()
tk.Button(text='BOTON', command=test).pack()
win.mainloop()

Related

Can you have two instances of tkinter in Python?

Is it possible to have two instances of tkinter ?
import tkinter as tk
import tkinter as sk
root = tk.Tk()
root2 = sk.Tk()
....some window with tk
....some window with sk
root.mainloop()
root2.mainloop()
then have a Toplevel() in both instances.
You can, but the way it works likely won't be like what you expect. Importing it twice isn't the problem (but neither is it the solution). No matter how you import it or how often you import it, creating more than one instance of Tk is the problem. Each instance is backed by a separate internal interpreter. Images and variables and widgets created in one won't exist in the other.
If you need more than one window, it's usually best if second and subsequent windows are instances of Toplevel.
By creating 2 instances or tkinter I can close one instance and all top Levels of that instance. Leaving the second Instance open and running with it's Toplevel instances. This is useful in the sense where I use it for a User preference file. Where it checks for a userfile database for keeping track of variables. I use a csv file to save user variable using pandas. This in turn keeps all the form information in my app safe from being erased after closing the application or accidentally closing the window. Adding AtExit saves the info closing the addinfo window and continues to run the main application. That was my reasoning for having asked the question. I have since found that using multiple Toplevel(s) is a better choice as it will also produce the same result. So my menu items all have a separate instance definition and can be closed in the same manner ,making error checks with each closed window.
from tkinter import Tk,Label,Entry,Button,Toplevel
root=Tk()
def about():
ab=Toplevel()
# About stuff for this window
ab.mainloop()
def info():
inf=Toplevel()
# Information Stuff for this window.
inf.mainloop()
def getUserInfo():
# User info using pandas
getUserInfo.mainloop()
root.mainloop()

Can You Implement OSX-Native Tab+Space Navigation with ttk.Button()?

Summarize the Problem
I am looking to use tkinter to implement the actions of tabbing and using the spacebar to cycle and activate UI elements, respectively. I am hoping to do so in a way that mimics the native behavior of OSX, as seen in the attachment below. This works fine on most other widgets in ttk.
This is made up of the following:
Allow components to be "focused" by the user using the tab key
Cause components to become visually highlighted when focused
Component can be triggered using the spacebar while focused
And the tricky part, it uses (close to) the native GUI appearance of the Operating System
Other StackOverflow Questions About This
The closest answer on this site I could find was this question. It may be important to note that the question is asked about Python 2.7.9. This question is interesting, because it suggests that changing a tk.Button() to ttk.Button() alleviates the issue, while when testing the code, I have found the opposite to be true. A tk.Button() will (inelegantly) highlight on OSX, whereas a ttk.Button() provides no visual feedback. Below is the code (with imports modified to run in 2021 on Python 3.X)
import tkinter as tk
from tkinter import ttk
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
entry1 = tk.Entry(self)
entry1.pack()
entry2 = tk.Entry(self)
entry2.pack()
# CHANGE BELOW LINE TO A ttk.Button() TO TEST
button1 = tk.Button(self, text="Test", command=self.yup)
button1.pack()
def yup(self):
print("yup")
root = tk.Tk()
app = App(root).pack()
root.mainloop()
My Attempted Solutions
Solution One: Implement buttons normally using ttk.Button() with ttk.Style() to indicate focus
Pros:
Uses native OS style
Accepts focus via tab and activation via spacebar
Cons:
Does not visually indicate focus unless forced to with ttk.Style()
To the extent of my knowledge cannot be given a highlighted border by ttk.Style(), so we must settle for colored text
Example Code:
from tkinter import Tk
from tkinter import ttk
root = Tk()
def one():
print("one")
def two():
print("two")
style = ttk.Style()
style.configure('C.TButton')
style.map('C.TButton',
foreground = [('pressed','red'),('focus','blue')],
background = [('pressed','!disabled','red'),('focus','white')],
relief=[('pressed', 'sunken'),
('!pressed', 'raised')])
# Define lower GUI
B1 = ttk.Button(root, text='Button One', command=one, style='C.TButton')
B1.pack(padx=20, pady=(20,0))
B2 = ttk.Button(root, text='Button Two', command=two, style='C.TButton')
B2.pack(padx=20, pady=10)
root.mainloop()
Solution Two: Use tk.Button() instead
Pros:
Accepts focus via tab and activation via spacebar
Natively highlights button using a border
Cons:
Does not look that appealing, border is misaligned and a plain rectangle
I cannot get many parameters to work properly on OSX, particularly activebackground and highlightthickness, limiting aesthetic options.
Example Code:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
def one():
print("one")
def two():
print("two")
B1 = tk.Button(root, text='Button One', command=one)
B1.pack(padx=20, pady=(20,0))
B2 = tk.Button(root, text='Button Two', command=two)
B2.pack(padx=20, pady=10)
root.mainloop()
Solution Three: Use the tkmacosx library's Button()
Pros:
Custom made for this problem
Highlights on tab-press with OSX-like style
Really, just everything I'm looking for
Cons:
Does not trigger button on spacebar
This last part is interesting, because based on the documentation (takefocus), this should be the expected behavior. On my machine, this is not the case (Catalina 10.15.7)
Example Code:
from tkinter import Tk
from tkmacosx import Button
root = Tk()
def one():
print("one")
def two():
print("two")
B1 = Button(root, text='Button One', command=one)
B1.pack(padx=20, pady=(20,0))
B2 = Button(root, text='Button Two', command=two)
B2.pack(padx=20, pady=10)
root.mainloop()
Concluding
Historically I understand that tkinter and OSX have not always played together perfectly, and that if I want more precise native control, I might switch to QT. I am immensely thankful for the existence of tkinter, and hope I am not asking too much.
I do want to be sure however that I'm not making an error before I try forking a repo or pursuing other solutions.
Regarding tkmacosx, it seems like this solution should be working the way it is described in the documentation, and I was hoping to get confirmation of this problem from another user, to see if it would be appropriate to file an issue on the github page.
Thank you very much for reading this post. Please feel free to ask for any additional info!

Is it possible to emulate Tk receive in tkinter?

I have an application that supports communicating to other Tk applications using the send method. In Tcl/Tk it's easy to call a user subroutine in one Tk app by calling send() from another Tk app and specifying the receiving app and subroutine name and args. In Perl it's possible to do the same with a little more work (overriding the definition of Tk::Receive to act on an incoming message from another app's send method).
Does anyone know to use tkinter to receive external messages coming from Tk send? I'd like to have a subroutine that is called to process incoming messages while in mainloop().
Thanks #jasonharper, the following worked in python 3.6:
from tkinter import *
root = Tk()
def some_func(*args):
print("Got args: " + ",".join(args))
# Create a command called my_func that calls some_func
root.tk.createcommand("my_func", some_func)
root.mainloop()
E.g. if I did send tk my_func hey from another Tk app, assuming the default tkinter appname tk, I would see Got args: hey from the above code

How to play sounds on python with tkinter

I have been working on a sort of 'Piano' on python. I have used tkinter as the ui,
and this is how it goes:
from tkinter import*
tk =Tk()
btn = Button(tk, text="c note", command = play)
How do I define the function play to play a sound when I press it?
Please Help.
Add these two pieces of code:
from winsound import *
&
command = lambda: Playsound("click_one.wav", SND_FILENAME)
If you don't like lambda then you can define a function before the creation of the button:
def play():
return PlaySound("click_one.wav", SND_FILENAME)
You can also define a lambda function:
play = lambda: PlaySound("click_one.wav", SND_FILENAME)
You can use pygame! It will not create a different window.
Check out Pygame's official site for more amazing functions like getting length.
There are two types of sound you can play sound or even music. Each supports pros and cons.
Tkinter doesn't support audio. You can even use pyglet or other modules.
Example code:
import pygame
from tkinter import *
root = Tk()
pygame.init()
def play():
pygame.mixer.music.load("Musics/example.mp3") #Loading File Into Mixer
pygame.mixer.music.play() #Playing It In The Whole Device
Button(root,text="Play",command=play).pack()
root.mainloop()

Adding items to a Listbox using Entries from another class

I have created two classes, one is in People.py (which will be the parent class) which contains a list box that can be populated by just opening a file and adding content to the list box line by line.
Another class is in Names.py (which I want it to be child class), which contains entries of first name, last name, and a combo box for titles that should (will implement once question/problem is answered) go into the list in the main window where the class is People. I am trying to use an OOP model. Right now, it's not fully OOP, but I will refactor the code later.
I have tried posting this code before but people are having trouble running it due to indentation problems, so I'm providing the links to the classes. In Dropbox Name Class and People Class:
Note: I'm running this in a Linux environment, so you may have to modify the file choosing line in the People class if using Windows (or another OS).
f = os.path.expanduser('~/Desktop')
Actually, you still have a problem of inconsistent use of tabs and spaces, which I solved, but maybe other people cannot solve it.
First of all, you should name your files/modules with lower cases (by convention, and you should follow it!). Then, in Names.py you are doing from Tkinter import * and then from Tkinter import Tk, which does not make any sense: with the first you are already importing Tk.
Your problem is the following:
People.people_list.insert(Tk.END, FirstName.get()) AttributeError:
'module' object has no attribute 'people_list'
In fact, you are trying to access an inexistent attribute of the module People called people_list, which is a local variable to some functions, from what I have been seeing.
If you want to fill a Listbox which is a property of some Toplevel A, with the input from another Toplevel B, you should pass a reference of the Toplevel A to B, maybe during its construction.
Here you have an example:
from tkinter import * # python 3
class Other(Toplevel):
"""Note that the constructor of this class
receives a parent called 'master' and a reference to another Toplevel
called 'other'"""
def __init__(self, master, other):
Toplevel.__init__(self, master)
self.other = other # other Toplevel
self.lab = Label(self, text="Insert your name: ")
self.lab.grid(row=0, column=0)
self.entry = Entry(self)
self.entry.grid(row=0, column=1)
# When you click the button you call 'self.add'
self.adder = Button(self, text='Add to Listbox', command=self.add)
self.adder.grid(row=1, column=1)
def add(self):
"""Using a reference to the other window 'other',
I can access its attribute 'listbox' and insert a new item!"""
self.other.listbox.insert("end", self.entry.get())
class Main(Toplevel):
"""Window with a Listbox"""
def __init__(self, master):
Toplevel.__init__(self, master)
self.people = Label(self, text='People')
self.people.grid(row=0)
self.listbox = Listbox(self)
self.listbox.grid(row=1)
if __name__ == "__main__":
root = Tk()
root.withdraw() # hides Tk window
main = Main(root)
Other(root, main) # passing a reference of 'main'
root.mainloop()
I noticed also that you are using 2 instance of Tk for each of your windows, which is bad. You should use just one instance of Tk for every application. If you want to use multiple windows, just use Toplevels, as I mentioned.
In general, your structure is not so good. You should start by creating simple good applications, and then pass to big ones once you grasp the basic concepts.

Resources