I am trying to get the position of the menu that is the child of the option menu widget in tkinter. I want to do this so that I can know when the menu has been shifted due to location on the screen (see picture 1)
However, when I try and use the menu widget winfo_rootx or winfo_x they both just show position 0 regardless of where the menu actually is. See example code:
from tkinter import Tk, Frame, BOTH, Menu, Label, SUNKEN, X, BOTTOM
import tkinter as tk
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background = "white")
self.parent = parent
self.parent.geometry("400x100")
vals = ["1","2","3","4","5","6","7"]
var = tk.StringVar()
var.set("1")
option = tk.OptionMenu(root,var,*vals)
option.pack()
self.t = option.children["menu"]
tk.Menu
#Do I need to unbind
#t.bind("<<MenuSelect>>", self.test_func)
self.t.bind("<<MenuSelect>>", self.test_func)
def test_func(self,event = None):
print(self.t.winfo_children())
print("x,y position",event.widget.winfo_x(),event.widget.winfo_y())
print("x,y root position",event.widget.winfo_rootx(),event.widget.winfo_rooty())
#if self.parent.call(event.widget,"index","active") == 0:
#print(self.t)
root = tk.Tk()
Application(root)
root.mainloop()
I have potentially other options to try and fix this but it seems that there should be a way to get the menu widgets position correctly. My other way is rather hacky and uses the position and lengths of the widgets to try and calculate the adjusted position.
Related
I made a button that plus the x axis with 50 if i press it.If i press the button tho it doesn't move,yes it changes the value but doesn't move. I tried a while loop but it crashed the program.
In general we use pack and grid geometry managers to handle widget placement in Tk. However, if you want to explicitly control the placement then you can use place which allows you to specify the location in either pixel or relative coordinates.
Here is an example of a frame with a button inside that moves when you click the button. Note that the button is positioned relative to its parent container so moves with the frame.
import tkinter as tk
import tkinter.ttk as ttk
class App(ttk.Frame):
def __init__(self, master, **kwargs):
super(App, self).__init__(master=master, **kwargs)
master.wm_geometry('640x480')
self.frame = f = tk.Frame(self, width=200, height=80, relief=tk.SUNKEN, borderwidth=2)
b = ttk.Button(f, text="Move", command=self.move_frame)
b.place(x=2, y=2)
f.place(x=2, y=2)
self.place(relheight=1.0, relwidth=1.0)
def move_frame(self):
x = self.frame.winfo_x()
x = x + 10
self.frame.place(x=x)
def main():
root = tk.Tk()
app = App(root)
root.mainloop()
if __name__ == '__main__':
main()
Basically I want to be able to type something in an entry widget and when I am done typing I want to be able to click anywhere on the application to stop typing. As of right now it wants me to constantly type something into the entry box. Anyone know of a way to stop this?
import tkinter as tk
class window2:
def __init__(self, master1):
self.panel2 = tk.Frame(master1)
self.panel2.grid()
self.button1 = tk.Button(self.panel2,text="Button")
self.button1.grid()
self.text1 = tk.Entry(self.panel2)
self.text1.grid()
self.text1.focus()
root1 = tk.Tk()
root1.geometry("750x500")
window2(root1)
root1.mainloop()
I would build this as an inherited class for Tk and then bind mouse button 1 to change focus to whatever widget was clicked.
import tkinter as tk
class window2(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("750x500")
panel2 = tk.Frame(self)
panel2.grid()
tk.Button(panel2,text="Button").grid()
text1 = tk.Entry(panel2)
text1.grid()
text1.focus()
self.bind("<1>", self.set_focus)
def set_focus(self, event=None):
x, y = self.winfo_pointerxy()
self.winfo_containing(x, y).focus()
window2().mainloop()
I'm trying to make a custom Tkinter widget by subclassing a Frame to make a scrollable widget that contains items. I have 2 windows, my root window and a toplevel window and I need my custom widget to pack to the toplevel window.
My problem is, that despite making 'top' the parent of my custom widget, it still packs in the root window. I've tried other widgets and they pack to the toplevel window fine.
my code:
from tkinter import *
root = Tk()
root.config(bg="#000000")
root.wm_attributes("-alpha","0.7")
top = Toplevel()
top.config(bg="#000001")
top.wm_attributes("-topmost",1)
top.wm_attributes("-transparentcolor","#000001")
top.wm_title("TOPLEVEL")
class Scrollygrid(Frame):
def __init__(self, parent, columns, h, w):
super(Scrollygrid, self).__init__()
self.scrollbar = Scrollbar(self)
self.scrollbar.pack(side = RIGHT, fill = Y)
self.area = Canvas(self, yscrollcommand=self.scrollbar.set, width = w, height = h, bg = "#000001", bd = 0, highlightthickness = 0)
self.gridframe = Frame(height = h*10, width = w, bg = "#FF0000")
self.gridframe.pack_propagate(0)
self.area.create_window((0, 0), window = self.gridframe)
for i in range(500):
Label(self.gridframe, text = i).pack()
self.area.pack()
self.area.config(scrollregion = (self.area.bbox("all")))
self.scrollbar.config(command = self.area.yview)
def onScroll(event):
self.area.yview_scroll(int(-1*(event.delta/60)), "units")
self.area.bind_all("<MouseWheel>", onScroll)
self.scrollbar.pack_forget() #scroll wheel still works!
testgrid = Scrollygrid(top, 1,root.winfo_screenheight()-80,root.winfo_screenwidth()-(root.winfo_screenwidth()/10))
testgrid.pack(side = RIGHT, anchor = CENTER)
root.mainloop()
the transparent color and alpha level have been left in to make it more immediately obvious which window is which.
top isn't the parent of your custom widget. You're never passing it. Your Scrollygrid is being passed a parent parameter and that's it. Nothing tells it to assign it as the parent as an attribute even. So the parent defaults to the Tk instance, root.
Replace:
super(Scrollygrid, self).__init__()
with:
super(Scrollygrid, self).__init__(parent)
In order to pass the given widget argument as the parent to the superclass, Frame. Essentially making the Toplevel, top your custom class' parent. Upon which you'll get further error(s) but the parent is correctly assigned. Verify by:
print(repr(self.winfo_toplevel()))
I'm trying to clear a tkinter window completely. However, I need a way to clear every widget on the window all at once without using pack.forget().
You could use a simple recursive loop to list all the children wigets of your main window :
def all_children (window) :
_list = window.winfo_children()
for item in _list :
if item.winfo_children() :
_list.extend(item.winfo_children())
return _list
Then just use this list :
widget_list = all_children(window)
for item in widget_list:
item.pack_forget()
What you need to do is set a frame to your main window and place everything that is to be cleared out at some point inside that frame. Then you simply do frame_name.destroy()
The following example has a button that creates a frame with several label widgets and a button.
The button calls a method that will destroy the frame and everything in it.
Then you can create the frame again with the first button.
Let me know if you have any question:
import tkinter as tk
class ExampleApp(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.some_frame = None
tk.Button(self.master, text="Create new frame with widgets!", command = self.create_stuff).pack()
def create_stuff(self):
if self.some_frame == None:
self.some_frame = tk.Frame(self.master)
self.some_frame.pack()
for i in range(5):
tk.Label(self.some_frame, text = "This is label {}!".format(i+1)).pack()
tk.Button(self.some_frame, text="Destroy all widgets in this frame!",
command= self.destroy_some_frame).pack()
def destroy_some_frame(self):
self.some_frame.destroy()
self.some_frame = None
root = tk.Tk()
my_example = ExampleApp(root)
root.mainloop()
You can use destroy method for each widget for example if it's a button you write btn1.destroy() and do it for all widgets. The forget method isn't recommended for it only remove the widgets from appearance.
i am going to create an tkinter gui app, and i know how i want it to look. but after playing around with tkinter, i found no way to toggle between screens when you press buttons down at the bottom. i know it does nothing but below is the simple layout i want to have, and switch between "myframe1" and "myframe2" kind of like the Apple App Store layout. is this possible?
from tkinter import *
tk = Tk()
tk.geometry("300x300")
myframe1 = Frame(tk,background="green",width=300,height=275)
myframe1.pack()
myframe2 = Frame(tk,background="cyan",width=300,height=275)
myframe2.pack()
btnframe = Frame(tk)
btn1 = Button(btnframe,text="screen1",width=9)
btn1.pack(side=LEFT)
btn2 = Button(btnframe,text="screen2",width=9)
btn2.pack(side=LEFT)
btn3 = Button(btnframe,text="screen3",width=9)
btn3.pack(side=LEFT)
btn4 = Button(btnframe,text="screen4",width=9)
btn4.pack(side=LEFT)
myframe1.pack()
btnframe.pack()
tk.mainloop()
something for you to get started with:
def toggle(fshow,fhide):
fhide.pack_forget()
fshow.pack()
btn1 = Button(btnframe,text="screen1", command=lambda:toggle(myframe1,myframe2),width=9)
btn1.pack(side=LEFT)
btn2 = Button(btnframe,text="screen2",command=lambda:toggle(myframe2,myframe1),width=9)
btn2.pack(side=LEFT)
Are you looking for something like a tabbed widget? You could use forget and pack as suggested here
Here is a class that I use in my code that works:
class MultiPanel():
"""We want to setup a pseudo tabbed widget with three treeviews. One showing the disk, one the pile and
the third the search results. All three treeviews should be hooked up to exactly the same event handlers
but only one of them should be visible at any time.
Based off http://code.activestate.com/recipes/188537/
"""
def __init__(self, parent):
#This is the frame that we display
self.fr = tki.Frame(parent, bg='black')
self.fr.pack(side='top', expand=True, fill='both')
self.widget_list = []
self.active_widget = None #Is an integer
def __call__(self):
"""This returns a reference to the frame, which can be used as a parent for the widgets you push in."""
return self.fr
def add_widget(self, wd):
if wd not in self.widget_list:
self.widget_list.append(wd)
if self.active_widget is None:
self.set_active_widget(0)
return len(self.widget_list) - 1 #Return the index of this widget
def set_active_widget(self, wdn):
if wdn >= len(self.widget_list) or wdn < 0:
logger.error('Widget index out of range')
return
if self.widget_list[wdn] == self.active_widget: return
if self.active_widget is not None: self.active_widget.forget()
self.widget_list[wdn].pack(fill='both', expand=True)
self.active_widget = self.widget_list[wdn]