How can i move a frame with buttons in tkinter? - python

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()

Related

How to add hover feature for text description on a Tkinter button?

I want to add a hover feature on a Tkinter button where if the user hovers the mouse cursor then description text displays. I also want to add some delay for that description to appear so that it would not be intrusive.
I can try using the "<Enter>" and "<Leave>" binding of the button to a function and make some "Label" appear in some corner of the app. But this approach may not be the most elegant.
This can be done very easily with tkinter. By adding Enter and Leave events to whatever you want to add a tooltip to, we can easily show/hide whatever we want, wherever we want. In my example I use a stripped-down tk.Toplevel so we can have a simple fade animation, and the tooltip wont be confined to the root window.
#widgets.py
import tkinter as tk, tkinter.ttk as ttk
from typing import Union
Widget = Union[tk.Widget, ttk.Widget]
class ToolTip(tk.Toplevel):
#amount to adjust fade by on every animation frame
FADE_INC:float = .07
#amount of milliseconds to wait before next animation state
FADE_MS :int = 20
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master)
#make window invisible, on the top, and strip all window decorations/features
self.attributes('-alpha', 0, '-topmost', True)
self.overrideredirect(1)
#style and create label. you can override style with kwargs
style = dict(bd=2, relief='raised', font='courier 10 bold', bg='#FFFF99', anchor='w')
self.label = tk.Label(self, **{**style, **kwargs})
self.label.grid(row=0, column=0, sticky='w')
#used to determine if an opposing fade is already in progress
self.fout:bool = False
def bind(self, target:Widget, text:str, **kwargs):
#bind Enter(mouseOver) and Leave(mouseOut) events to the target of this tooltip
target.bind('<Enter>', lambda e: self.fadein(0, text, e))
target.bind('<Leave>', lambda e: self.fadeout(1-ToolTip.FADE_INC, e))
def fadein(self, alpha:float, text:str=None, event:tk.Event=None):
#if event and text then this call came from target
#~ we can consider this a "fresh/new" call
if event and text:
#if we are in the middle of fading out jump to end of fade
if self.fout:
self.attributes('-alpha', 0)
#indicate that we are fading in
self.fout = False
#assign text to label
self.label.configure(text=f'{text:^{len(text)+2}}')
#update so the proceeding geometry will be correct
self.update()
#x and y offsets
offset_x = event.widget.winfo_width()+2
offset_y = int((event.widget.winfo_height()-self.label.winfo_height())/2)
#get geometry
w = self.label.winfo_width()
h = self.label.winfo_height()
x = event.widget.winfo_rootx()+offset_x
y = event.widget.winfo_rooty()+offset_y
#apply geometry
self.geometry(f'{w}x{h}+{x}+{y}')
#if we aren't fading out, fade in
if not self.fout:
self.attributes('-alpha', alpha)
if alpha < 1:
self.after(ToolTip.FADE_MS, lambda: self.fadein(min(alpha+ToolTip.FADE_INC, 1)))
def fadeout(self, alpha:float, event:tk.Event=None):
#if event then this call came from target
#~ we can consider this a "fresh/new" call
if event:
#indicate that we are fading out
self.fout = True
#if we aren't fading in, fade out
if self.fout:
self.attributes('-alpha', alpha)
if alpha > 0:
self.after(ToolTip.FADE_MS, lambda: self.fadeout(max(alpha-ToolTip.FADE_INC, 0)))
#main.py ~ EXAMPLE USAGE OOP
import tkinter as tk
from widgets import ToolTip
class Root(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
#instantiate ToolTip
tt = ToolTip(self)
#create first button and bind a tooltip to it
btn = tk.Button(self, text='hover')
btn.grid(column=0, row=0)
tt.bind(btn, 'first button is hovered')
#create second button and bind a tooltip to it
btn2 = tk.Button(self, text='hover2')
btn2.grid(column=1, row=0)
tt.bind(btn2, 'second button is hovered')
if __name__ == "__main__":
root = Root()
root.title("ToolTip Example")
root.mainloop()
#main.py ~ EXAMPLE USAGE PROCEDURAL
import tkinter as tk
from widgets import ToolTip
if __name__ == "__main__":
root = tk.Tk()
root.title("ToolTip Example")
#instantiate ToolTip
tt = ToolTip(root)
#create first button and bind a tooltip to it
btn = tk.Button(root, text='hover')
btn.grid(column=0, row=0)
tt.bind(btn, 'first button is hovered')
#create second button and bind a tooltip to it
btn2 = tk.Button(root, text='hover2')
btn2.grid(column=1, row=0)
tt.bind(btn2, 'second button is hovered')
root.mainloop()
Here is a small snippet using Pmw (python mega widgets) for the tool tips.
Firstly start by installing it:
pip install Pmw
Then here is a snippet to understand what Pmw can do:
from tkinter import *
import Pmw
root = Tk()
Pmw.initialise(root) #initializing it in the root window
l = Label(root,text='Random Text')
l.pack()
b = Button(root,text='Hover me')
b.pack()
tooltip_1 = Pmw.Balloon(root) #Calling the tooltip
tooltip_1.bind(b,'This is the hover Text\nHope you get an idea of whats going on here.') #binding it and assigning a text to it
root.mainloop()
Hope this gives you a better idea. Keep in mind that Pmw could create a mess while converting the py to an exe later(if you have any intentions to). There is a way around in tho.
Cheers

Get tkinter window that triggered event?

When a resize event is triggered, how could I retrieve the toplevel, from which this event was triggered?
I have written a small programm, where the main window has a button that opens another window and each new window gets a binding for the resize method that currently prints the height and width of the window. In the main project, the toplevel is used as an index for lists to retrieve information for that specific window, so it would be ideal to be able to retrieve the toplevel as well. Is that possible, either directly or indirectly?
import tkinter as tk
class MyApp(tk.Frame):
def __init__(self, master = None):
self.main()
tk.Frame.__init__(self, master)
def main(self):
btn = tk.Button(root, text="New Window", command=self.neues_fenster)
btn.grid(row=0, column = 0)
def neues_fenster(self):
top = tk.Toplevel()
top.title("Some Window")
top.minsize(width = 150, height = 150)
top.bind("<Configure>", self.resize)
def resize(self, event):
print("width", event.width, "height", event.height)
if __name__=="__main__":
root = tk.Tk()
myapp = MyApp(master = root)
myapp.mainloop()
In effbot document,You could use event.widget to get the widget.(It is also okay even if it is toplevel).
All the possible attributes of event:

Deselect widget in callback function in TKinter

I used TKinter in python2.
My code is as follow.
from Tkinter import *
class Application(Frame):
def __init__(self, master=None,w=1000,h=600):
Frame.__init__(self, master)
self.createWidgets(master,w,h)
def getRadio(self,widget):
widget.deselect()
def createWidgets(self, master,w,h):
ConfigPane=Frame(master,bg='lightblue',width=int((w/6)*4),height=int(h/3),padx=5,pady=5)
DisplayPane=Frame(master,bg='DarkOliveGreen1',width=int((w/6)*4),height=int((h/3)*2),padx=5,pady=5)
HyperPane=Frame(master,bg='khaki1',width=int((w/6)*2),height=h,padx=5,pady=5)
# layout all of the main containers
root.grid_rowconfigure(0, weight=1)
root.grid_rowconfigure(1, weight=1)
ConfigPane.grid(row=0,column=0,columnspan=4,rowspan=1, sticky=W+N)
DisplayPane.grid(row=1,columnspan=4,rowspan=2, sticky=W+S)
HyperPane.grid(row=0,column=5,columnspan=2,rowspan=3, sticky=E+N+S)
# create the widgets for the top frame
var=StringVar()
RegNet = Radiobutton(ConfigPane, text='RegNet',variable=var,pady=10,padx=10,width=10,anchor='w',command=lambda:self.getRadio(RegNet))
RegNet.grid(row=0,column=0)
InceptionNet = Radiobutton(ConfigPane, text='InceptionNet',variable=var,pady=1,padx=10,width=10,anchor='w',command=lambda:self.getRadio(InceptionNet))
InceptionNet .grid(row=1,column=0)
ResNet = Radiobutton(ConfigPane, text='ResNet',variable=var,pady=8,padx=10,width=10,anchor='w',command=lambda:self.getRadio(ResNet))
ResNet.grid(row=2,column=0)
if __name__ == "__main__":
root = Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
root.geometry(str(width)+'x'+str(height))
app = Application(master=root,w=width,h=height)
app.master.title('Deep Learning Reconfigurable Platform')
app.mainloop()
root.destroy()
When I click the radiobutton, the black dot at the button is supposed to disappear, but it doesn't. How can I make it work?
Every radiobutton needs a distinct value, otherwise they all have the same value and thus show the same state. The value is how tkinter knows which button in a set of buttons should be selected.
You probably shouldn't be calling deselect in the callback -- that makes your buttons effectively useless, since any attempt by the user to choose one will cause whatever they clicked on to immediately be deselected. It will become impossible for them to select anything.
The key elements:
The "var", must be defined in scope of Class, if not it is destroyed when exiting the createWidget method and you would need to navigate through the widgets to get the selected one.
The RadioButtons are missing the value attribute, defining what is the value to be set to the "var" when selected.
Here is the updated code; updated for Python3 (changed the module tkinter name)
from tkinter import *
class Application(Frame):
var = None
def __init__(self, master=None,w=1000,h=600):
Frame.__init__(self, master)
self.var = IntVar()
self.var.set(1)
self.createWidgets(master, w, h)
def show_choice(self):
#for widget in widgets:
# widget.deselect()
print(self.var.get())
return
def createWidgets(self, master,w,h):
ConfigPane=Frame(master,bg='lightblue',width=int((w/6)*4),height=int(h/3),padx=5,pady=5)
DisplayPane=Frame(master,bg='DarkOliveGreen1',width=int((w/6)*4),height=int((h/3)*2),padx=5,pady=5)
HyperPane=Frame(master,bg='khaki1',width=int((w/6)*2),height=h,padx=5,pady=5)
# layout all of the main containers
root.grid_rowconfigure(0, weight=1)
root.grid_rowconfigure(1, weight=1)
ConfigPane.grid(row=0,column=0,columnspan=4,rowspan=1, sticky=W+N)
DisplayPane.grid(row=1,columnspan=4,rowspan=2, sticky=W+S)
HyperPane.grid(row=0,column=5,columnspan=2,rowspan=3, sticky=E+N+S)
# create the widgets for the top frame
RegNet = Radiobutton(ConfigPane, text='RegNet',variable=self.var,pady=10,padx=10,width=10,anchor='w', value=1, command=self.show_choice)
RegNet.grid(row=0,column=0)
InceptionNet = Radiobutton(ConfigPane, text='InceptionNet',variable=self.var,pady=1,padx=10,width=10,anchor='w', value=2, command=self.show_choice)
InceptionNet.grid(row=1,column=0)
ResNet = Radiobutton(ConfigPane, text='ResNet',variable=self.var,pady=8,padx=10,width=10,anchor='w', value=3, command=self.show_choice)
ResNet.grid(row=2,column=0)
if __name__ == "__main__":
root = Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
root.geometry(str(width)+'x'+str(height))
app = Application(master=root,w=width,h=height)
app.master.title('Deep Learning Reconfigurable Platform')
app.mainloop()
# root.destroy()

Tkinter Optionmenu "menu" position showing 0,0 always

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.

Tkinter notebook - Too many tabs for window width

I am having a problem with my first tkinter (Python 3) notebook app.
The canvas on which the data is displayed only needs to be 775px wide, by 480px high. This is all very well until the number of tabs makes the window wider than that. All the data is placed on one side and the other is a sea of emptyness. I have tried to make the notebook widget scrollable but I cannot get it to work.
Any advice would be greatly received.
#!/usr/bin/python
# Try to work with older version of Python
from __future__ import print_function
import sys
if sys.version_info.major < 3:
import Tkinter as tk
import Tkinter.ttk as ttk
else:
import tkinter as tk
import tkinter.ttk as ttk
#============================================================================
# MAIN CLASS
class Main(tk.Frame):
""" Main processing
"""
def __init__(self, root, *args, **kwargs):
tk.Frame.__init__(self, root, *args, **kwargs)
self.root = root
self.root_f = tk.Frame(self.root)
self.width = 700
self.height = 300
# Create a canvas and scroll bar so the notebook can be scrolled
self.nb_canvas = tk.Canvas(self.root_f, width=self.width, height=self.height)
self.nb_scrollbar = tk.Scrollbar(self.root_f, orient='horizontal')
# Configure the canvas and scrollbar to each other
self.nb_canvas.config(yscrollcommand=self.nb_scrollbar.set,
scrollregion=self.nb_canvas.bbox('all'))
self.nb_scrollbar.config(command=self.nb_canvas.xview)
# Create the frame for the canvas window, and place
self.nb_canvas_window = tk.Frame(self.nb_canvas, width=self.width, height=self.height)
self.nb_canvas.create_window(0, 0, window=self.nb_canvas_window)
# Put the whole notebook in the canvas window
self.nb = ttk.Notebook(self.nb_canvas_window)
self.root_f.grid()
self.nb_canvas.grid()
self.nb_canvas_window.grid()
self.nb.grid(row=0, column=0)
self.nb_scrollbar.grid(row=1, column=0, sticky='we')
self.nb.enable_traversal()
for count in range(20):
self.text = 'Lots of text for a wide Tab ' + str(count)
self.tab = tk.Frame(self.nb)
self.nb.add(self.tab, text=self.text)
# Create the canvas and scroll bar for the tab contents
self.tab_canvas = tk.Canvas(self.tab, width=self.width, height=self.height)
self.tab_scrollbar = tk.Scrollbar(self.tab, orient='vertical')
# Convigure the two together
self.tab_canvas.config(xscrollcommand=self.tab_scrollbar.set,
scrollregion=self.tab_canvas.bbox('all'))
self.tab_scrollbar.config(command=self.tab_canvas.yview)
# Create the frame for the canvas window
self.tab_canvas_window = tk.Frame(self.tab_canvas)
self.tab_canvas.create_window(0, 0, window=self.tab_canvas_window)
# Grid the content and scrollbar
self.tab_canvas.grid(row=1, column=0)
self.tab_canvas_window.grid()
self.tab_scrollbar.grid(row=1, column=1, sticky='ns')
# Put stuff in the tab
for count in range(20):
self.text = 'Line ' + str(count)
self.line = tk.Label(self.tab_canvas_window, text=self.text)
self.line.grid(row=count, column=0)
self.root.geometry('{}x{}+{}+{}'.format(self.width, self.height, 100, 100))
return
# MAIN (MAIN) =======================================================
def main():
""" Run the app
"""
# # Create the screen instance and name it
root = tk.Tk()
# # This wll control the running of the app.
app = Main(root)
# # Run the mainloop() method of the screen object root.
root.mainloop()
root.quit()
# MAIN (STARTUP) ====================================================
# This next line runs the app as a standalone app
if __name__ == '__main__':
# Run the function name main()
main()
OK, so I think I understand now. The tabs are inside the notebook, and inseperable from the notebook. As such, the notebook will always be as wide as the frames within it. To get the effect I wanted I would need put a canvas into the notebook, and then add the tabs the the canvas. And that is not allowed. So back to the drawing board!
If the tabs are of 'constant' width and you know how many will fit the desired (fixed?)size of the window, you could create a "scrolling tabs" widget by hiding the ones that don't fit your width. Create two buttons, left and right that for example hides the one to the right and shows the next hidden one to the left.
If there a way to figure out the width of a tab (fontsize in the label, padding etc?) it could be done more 'dynamic'.
I would recommend combining the solutions from here: Is there a way to add close buttons to tabs in tkinter.ttk.Notebook? (to be able to close a tab) and here: https://github.com/muhammeteminturgut/ttkScrollableNotebook to use buttons instead of a scroll-bar to handle the width issue.
Two changes to get it to work are to load the "notebookTab" variable as the CustomNotebook and to put the closing icon on the left side by switching the order of the innermost children of style.layout in the first answer. This produces a slidable and closeable custom notebook type.

Categories

Resources