The title says it all really, I have a piece of code in which I would like to move the top-level in relation to its old position.
To do this I need to fetch the current position of the window, then set the new position using root.geometry('+x+y'). How can I find the current position of a TopLevel?
You can get the window's position through the winfo_x and winfo_y methods.
Below is a simple script to demonstrate*:
from tkinter import Tk, Button
root = Tk()
def click():
print(root.winfo_x(), root.winfo_y())
Button(text="Get position", command=click).grid()
root.mainloop()
*Note: I used a Tk window here instead of a Toplevel for the sake of simplicity. However, the same principle applies to the Toplevel.
Related
The code is simple. In tkinter I create a button which opens a new window. The difference in the pictures below might be hard to see but if you look closely you can see that in the first picture the root window is selected even though it's behind the new opened window.
In my actual program I use keybindings to operate the second window so it would be nice to instantly select this window so you don't have to click on it to use keys to operate it. How can I select the Toplevel window as soon as it opens?
from tkinter import *
def open_new_window():
top = Toplevel(root)
root = Tk()
Button(root, text="open new window", command=open_new_window).pack()
root.mainloop()
Possible duplicate of this question
Like acw1668 said, simply add top.focus() at the end of your function open_new_window
Your new open_new_window function will look like this:
def open_new_window():
top = Toplevel(root)
top.focus()
Hey so i'm making a program that has a checkbutton on the main window and a toplevel window that has one aswell. the problem is that for some reason the toplevel checkbutton affects the state of the main checkbutton, or the main checkbutton mimics the top level one (if you check/uncheck the toplevel one, the main one checks/unchecks aswell). Here's an example code which displays the problem:
import tkinter as tk
def toplevel():
top = tk.Toplevel()
top.geometry('200x50')
top_chekbutton = tk.Checkbutton(top, text='top')
top_chekbutton.pack()
top.mainloop()
main = tk.Tk()
main.geometry('200x50')
open_top = tk.Button(main, text='open top', command=toplevel)
main_checkbutton = tk.Checkbutton(main, text='main')
main_checkbutton.pack()
open_top.pack()
main.mainloop()
i didn't define the state variables because they don't seem to be the source of the problem. i'm using python 3.7.7 and tkinter 8.6 on win10.
plz help :(
As a general rule of thumb, every instance of Checkbutton should have a variable associated with it. If you don't, a default value will be used that is identical for all Checkbuttons. All widgets that share the same variable will display the same value.
You can verify this yourself by printing out the value of top_chekbutton.cget("variable") and main_checkbutton.cget("variable"). In both cases the value is "!checkbutton" (at least, with the version of python I'm using).
So, assign a variable for your checkbuttons, such as a BooleanVar, IntVar, or StringVar.
main_var = tk.BooleanVar(value=False)
main_checkbutton = tk.Checkbutton(main, text='main')
I was checking out Toplevel of tkinter. From what I've seen from effbot I can omit its parent argument.
1- When I only use Toplevel itself (commenting out root), it creates its own parent I believe since two windows appear and only destroys one after clicking button.
2- If I don't comment out Tk(), it works fine. Two windows, one root - one toplevel and destroys toplevel.
3- If I interchange root and toplevel, first toplevel creates two again(like in first case), then root will create another so three windows will appear and only toplevel gets destroyed.
import tkinter as tk
#root = tk.Tk()
top = tk.Toplevel()
#root.title("Foo")
top.title("Bar")
top.geometry("300x100")
tk.Button(top, text = "Destroy", command=top.destroy).pack()
top.mainloop()
Question is, is there a way to create toplevel before Tk() and get only one window or access its parent and destroy it?
p.s. I found these two questions Toplevel in Tkinter: Prevent Two Windows from Opening && tkinter child window opens two windows?. First question is in 2nd case which is not what I want, and second question has no answer yet and his problem kind of not reproducable.
Also, I tried to get its master value -to destroy is manually- like this but seems like that value is not stored in dictionary where options are stored.
btn = tk.Button(top, text = "Destroy", command=top.destroy)
btn.pack()
print (btn["text"])
>>> Destroy
print (btn["master"])
>>> _tkinter.TclError: unknown option "-master"
It's not that Toplevel creates it's own parent, any widget will create a root window if you don't create one first. There simply must be a root window before any other widget can exist -- that's why it's called a root window. So, to answer your specific question, no, there is no way to create an instance of Toplevel without creating a root window first.
I'm working on my first Python program and have little idea what I'm doing. I want to re-bind ctrl-a (control a) to select all text in a Text widget. The current binding is ctrl-/ (control /). The binding part jumps right to the function but the actual text selection doesn't work. Instead, the cursor jumps to the first character on the first line (like it should) and nothing else happens. I'm sure this is embaressingly easy to fix but after spending hour an hours on it, I can't figure out what's wrong.
Python 3, Windows:
from tkinter import *
# Select all the text in textbox (not working)
def select_all(event):
textbox.tag_add(SEL, "1.0", END)
textbox.mark_set(INSERT, "1.0")
textbox.see(INSERT)
# Open a window
mainwin = Tk()
# Create a text widget
textbox = Text(mainwin, width=40, height=10)
textbox.pack()
# Add some text
textbox.insert(INSERT, "Select some text then right click in this window")
# Add the binding
textbox.bind("<Control-Key-a>", select_all)
# Start the program
mainwin.mainloop()
So the new code is...
from tkinter import *
# Select all the text in textbox
def select_all(event):
textbox.tag_add(SEL, "1.0", END)
textbox.mark_set(INSERT, "1.0")
textbox.see(INSERT)
return 'break'
# Open a window
mainwin = Tk()
# Create a text widget
textbox = Text(mainwin, width=40, height=10)
textbox.pack()
# Add some text
textbox.insert(INSERT, "Select some text then right click in this window")
# Add the binding
textbox.bind("<Control-Key-a>", select_all)
textbox.bind("<Control-Key-A>", select_all) # just in case caps lock is on
# Start the program
mainwin.mainloop()
and yes it works flawlessly. Thank you, very much Bryan Oakley. Steven Rumbalski: that's a VERY good point, I've followed your advice as well.
You need to both do the selection and then inhibit the default action by having your function return the string "break".
This is due to how Tkinter processes events. It uses what it calls "bind tags". Even though it looks like you are binding to a widget, you are actually binding to a tag that is the name of the widget. There can also be bindings to the widget class, to the toplevel window that the widget is in, and the tag "all" (plus, you can invent your own tags if you wish).
The default ordering of these tags is from most-specific to least-specific, and events are processed in that order. Meaning, if you have a binding both on the widget (most specific) and the class (less specific), the binding will fire for the widget first, and then for the class binding (and then for the toplevel, and then for "all").
What this means is that by default, a binding on a widget augments rather than replaces a default binding. The good news is, you can inhibit any further bindings from firing by simply returning the string "break", which stops the chain of bindings from doing any more work.
You can do that with a module named pyautogui
Just run the command where you want to add the event,
import pyautogui
..., command=lambda *awargs:pyautogui.hotkey("ctrl","a")
Make sure you install the module. If you are on windows, install it by
pip install pyautogui
The tkinter MouseWheel event can only be bound to the root window. Thus the event position is also relative to the root position.
For a canvas zooming operation, I would like to get the MouseWheel-event with information about the cursor-position within the canvas. To calculate this I thought I would simply subtract the position of the canvas within the root window. Problem now is, that I can not find out the canvas position in the window.
I tried:
can.grid_bbox --> (0,0,0,0) ?
can.grid_info
can.grid_location
cget("offset")
pointerx
and some others I can't remember. Somehow I keep on missing it could someone give me a hint?
---edit---
To get the position of the mouseWheel event relative to the canvas, this approach seems to work:
def on_mouse_wheel(self, event):
xCan = event.x_root - self.can.winfo_rootx()
yCan = event.y_root - self.can.winfo_rooty()
You want to use the winfo_x and winfo_y methods to get the x/y position relative to the parent.