This question already has an answer here:
When should I use root.update() in tkInter for python
(1 answer)
Closed 1 year ago.
Beginner programmer here currently trying to learn Tkinter for a school assignment.
I have a GUI class that stores the Tkinter labels etc, the labels are innitiated like this:
# GUI for Player 1
self.player_1_name_field = Label(
self.root,
text="Player 1",
font=GUI_Settings.player_information_font,
anchor=W,
background=GUI_Settings.playerfield_active_color
)
I then create a Game() object that looks like this:
class Game():
def __init__(self):
self.GUI = GUI()
self.GUI.initializeBoard()
self.GUI.root.mainloop()
When I run the code, the labels do get created and are where they are supposed to be, but are completely black. Once I move or resize the window it instantly becomes how I want it to be, it just behaves weird when at the start of the code
The interesting thing is that I also have a Canvas and a List that work perfectly fine, only the Labels are not cooperative
If you need further info, just ask for it!
Thank you!
Edit 1: I have a function called drawWindow() that redraws the chessboard when I re-configure the window. In the init of the GUI class I set self.root.bind("<Configure>", self.drawWindow). If I remove that line of code, the Labels work but the Canvas doesn't anymore. I'm so confused. For anyone wanting to take a look at my tiny code: https://codeshare.io/DZYzyZ
See comment of Thingamabobs
The issue is self.root.update(). Remove this line and you'll be fine.
When should I use root.update() in tkInter for python.
This works but you shouldn't do it
This is a tricky issue. Your problem come from the bind of the configure event. Bind to the root window, it is applied to all sub-widgets of the window, which cause the bug (I don't know why yet).
This will solve your issue (line 202):
self.chessboard.bind("<Configure>", self.drawWindow)
instead of:
self.root.bind("<Configure>", self.drawWindow)
Result without moving or resizing the window:
I found the information here (french forum).
Related
I am trying to make a keylogger using Tkinter, but the binds are not working at all. I think the bind isn't calling the subprogram, but it provides no error so that is just an educated guess.
I started this project 3 days ago and expected it to be done yesterday. But this issue kept on cropping up, I am using python 3.6.1.
Here is what I have already tried
Using lambda
Putting "command =" before the function
Changing "def keypress():" to "def keypress(event)"
Making the bind in a frame
Binding each individual button the keyboard
I have even copied someone's keylogger code and came across the same issue (yes it was python-3.x)
It is made even more frustrating by the number of answers on forums that don't work and the days of googling and looking at the documentation.
import tkinter
from tkinter import *
window = Tk()
window.title("Test")
window.config(bg = "white")
frame = Frame(window, width = 1000, height = 1000)
frame.place(x = 0, y = 0)
def keypress(event):
print("pressed", repr(event.char)) #changed repr to str and also tried deleting it
frame.bind("<Key>", lambda: keypress(event)) #other variations of this line include frame.bind("<key>", keypress), frame.bind("<key>", keypress()), frame.bind("<key>", keypress(event))
The expected input is just
>>> Pressed [the key that I pressed]
but the output that you actually get is...
>>>
Nothing.
Any and all help would be wonderful, thanks in advance!
You don't need lambda if you aren't passing any arguments.
frame.bind("<Key>", keypress)
Also, keybindings only work if the widget has the keyboard focus. By default a frame does not get the keyboard focus. If you want to bind to a frame, you must force it to have keyboard focus:
frame.focus_set()
I created a Frame, gave it a menubar. Works just fine. The purpose of the entry in the menubar is to open a new frame, in which u can change some settings. The creation of the new Window works also. However I can't create widgets on the new created window. I tried it with a Button and got a
TclError: can't invoke "button" command: application has been destroyed
I tried to google it and found Cannot invoke button command: application has been destroyed which didn't quite helped me.
Further I found a solution were u have to create a parent class (which inherrits from Frame) and than create all other Frames within it, but on the first view it looked pretty complicated. Especially because the creation of the second window seems to work in the first place.
I know this is probably a really basic question, so thanks in advance for your time
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
perfFrame.mainloop()
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
# Button(perfFrame, text='Abbrechen', command=perfFrame.destroy, width=37).grid(row=0 ,column=1 )
class perfSettingsValue:
def __init__(self):
self.bvhSteps = 0
def getValues(self):
pass
#Hauptfenster
root = Tk(className="BoneMapping & SkeletonEstimation")
root.configure(bg='#F2F2F2')
root.geometry("1300x600")
myPerfSettingValue = perfSettingsValue()
menubar = Menu(root)
sdmenu = Menu(menubar, tearoff=0)
sdmenu.add_command(label="Performanz", command=perfSettings)
menubar.add_cascade(label='Einstellungen',menu=sdmenu)
root.config(menu=menubar)
The key problem here is that you are trying to add a button after starting the mainloop which effectively blocks the execution of the program. The error you are getting is because the line that adds the button gets executed after the window has been closed.
Your problem will be solved if you modify your function like this:
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
perfFrame.mainloop()
This is not the only problem though. Instead of creating a new instance of Tk, you should create a new Toplevel instance, which will, in your case, act just as a Tk instance, but have a lot less tendency to cause trouble.
Finally, you should consider reading on the object oriented approach to designing tkinter applications. There are far too many variants of that to be appropriately elaborated here but I certainly recommend you take the effort to learn to use one of them. It will make your code more comprehensible and maintainable. My usual approach is to create a class that inherits from Toplevel or Tk for every type of window I am going to use.
The following code exhibits a problem I do not understand:
from Tkinter import *
root = Tk()
cheese_var = IntVar()
parrot_var = IntVar(value=1)
check_menu = Menu(tearoff=0)
check_menu.add_checkbutton(label="Cheese", variable=cheese_var)
check_menu.add_checkbutton(label="Parrot", variable=parrot_var)
count = 0
class Top():
def __init__(self):
global count
count += 1
self.tl = Toplevel(root)
Label(self.tl, text="Window " + str(count)).pack()
self.mb = Menubutton(self.tl, text="Push Me", bg='pink')
self.menu = Menu(self.mb, tearoff=0)
self.menu.add_cascade(label="Choices", menu=check_menu)
self.menu.add_command(label="New Window", command=new_top)
self.mb.config(menu=self.menu)
self.mb.pack()
def new_top():
Top()
Top()
root.mainloop()
The menu brought up by the menu button in the created top level window initially behaves as expected. Clicking on the New Window command there creates a new such window, which also behaves as expected. Indeed, as long as you keep creating new top level windows, everything continues to work as expected. However, once you delete (close) any one of those windows, then, in a subsequently created new window, the Choices cascade on the new menu is not functional. (It is still OK in the windows created before the closing of one.)
The situation in which I initially encountered this symptom was much more complex, but I was able to simplify it down to the above example which exhibits the issue. I have discovered that I can avoid the problem by having each instance of Top create its own check_menu as an attribute; but I do not understand why this should be necessary. Please point me the way if there is one to avoid the problem without such replication of a cascade menu used in multiple windows.
Unfortunately, I don't think it is possible to do what you want. I'll try to explain as best as I can.
When you first run the script, check_menu is created and works fine for the first window. As you create more windows, check_menu is simply shared between them. However, when you close one of them, check_menu (and everything under it) is destroyed. So, when you create a new window after that, check_menu no longer exists and it doesn't show.
However, the script doesn't throw an error because, for some reason, Tkinter allows you to assign menus to things that aren't menus. Believe it or not, none of the following code:
self.menu.add_cascade(label="Choices", menu=None)
self.menu.add_cascade(label="Choices", menu=1)
self.menu.add_cascade(label="Choices", menu="")
will break the script. Each line simply does nothing but create an empty cascade "Choices".
That is basically what is happening. After closing one window, check_menu and everything under it is destroyed. Yet, Tkinter doesn't throw an error but instead assigns a menu to something that is no longer a menu (as far as what it is assigning the menu to, I believe it is using the old instance of check_menu, which was destroyed).
To solve this problem, recreate check_menu and everything under it each time you call Top. In other words, put the code for check_menu (and its options) in the __init__ method of Top. That way, each time Top is called, check_menu will exist.
Hope this helps (and that I explained it sufficiently :)
I'm trying to remove a Tkinter progress bar widget from an active window (after the GUI window using Tkinter has been initialized). I'm using a Tkinter Frame for my window. I've initialized the progress bar as pb, as below.
pb = ttk.Progressbar(root,orient ="horizontal",length = 540, mode ="determinate")
And then I've tried two different methods to get rid of the progress bar. The line below causes the window to freeze and stop responding when I try to use it after the GUI is initialized.
pb.pack_forget()
The line below causes only a middle section of the progress bar to disappear, but you can still see the two sides of it.
pb.destroy()
Is there any way I could get this widget to disappear after the Frame has been initialized?
The specific answer to your question is that pack_forget, grid_forget or grid_remove are what you want if you want to make a widget temporarily invisible. Which one you choose depends on if you're using grid or pack, and whether or not you want grid to remember where it was so you can later put it back in the same spot.
destroy is what you want to call if you want to literally destroy the widget.
When used properly, none of those methods will cause your program to freeze. Without seeing your code, it's impossible to know what the root cause of the problem is.
Sorry for my bad English.
This code worked for me. I simply follow Oakley's instruction.
def progressBar(*args, **kwargs):
def progress(currentValue):
progressbar["value"] = currentValue
maxValue = 100
progressbar = ttk.Progressbar((kwargs), orient="horizontal", length=150, mode="determinate", takefocus=True)
progressbar.pack(side=tk.BOTTOM)
currentValue = 0
progressbar["value"] = currentValue
progressbar["maximum"] = maxValue
divisions = 10
for i in range(divisions):
currentValue = currentValue + 10
progressbar.after(500, progress(currentValue))
progressbar.update() # Force an update of the GUI
progressbar.destroy()
Hence I simply tried progressbar.destroy() outside of the loader loop. So after
complete the loading, it will disappear from the main App window.
Thank you, Bryan Oakley sir.
Every result I've gotten from searching this question up has to do with changing the border color for Tkinter widget's WITHIN the main app window, or changing the background color and stuff.
What I'm looking to do is actually change the windows border color itself. Which I have marked in this picture here
I read some stuff concerning 'Styles' but it didn't seem to be what I was looking for.
Can anyone help point me to a tutorial for changing this, or explain how it can be done?
Or if there is a question that asked exactly the same question I was asking that is answered point me to it.
As I see you are using windows.
This color is set by the theme you are currently using. It is the same for every window.
So I cross out the possibility of only using the Tkinter module for this.
Tkinter is responsible for what is in the window but the window manager decides about the border. For example in Ubuntu the window would look totally different.
I guess, you would need some windows specific calls for that.
You can remove the border with root.overrideredirect(1) if I remember correctly.
PS: put "windows" into the tags of this question.
Hi I was looking for this answer too, found it after like 80 minutes searching, Only work, to activate DWMWA_USE_IMMERSIVE_DARK_MODE
found it here: Can I change the title bar in Tkinter?
I didnt found dwmwindowattribute in dwmwindowattribute that affect Top bar color sadly :(.
For border colour:
It should be possible to call DWMWA_BORDER_COLOR, but honestly I dont know how, there is some article calling it in C++ here:
change-the-color-of-the-title-bar-caption-of-a-win32-application
Tried this but doesnt work:
set_window_attribute(hwnd, 22, '0x000000FF', 4)
Here is working code for pure black top:
import tkinter as tk
import ctypes as ct
def dark_title_bar(window):
"""
MORE INFO:
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
"""
window.update()
set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
get_parent = ct.windll.user32.GetParent
hwnd = get_parent(window.winfo_id())
value = 2
value = ct.c_int(value)
set_window_attribute(hwnd, 20, ct.byref(value),
4)
root = tk.Tk()
root.title("Crystaly Ball")
root.geometry("1400x900")
root.configure(background="#222246")
dark_title_bar(root)
root.mainloop()