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()
Related
I have an idea to create a simple scoreboard app (sport) with two windows (creating in Tkinter). One is for controlling and another one for output information.
So the idea is that I press button "show scoreboard" in a tk.Toplevel window and it appears in the main app window. And as I press hide, it hides. I know that I can create it just writing script without Classes and just like hundreds def strings but I want to use OOP as I would like to start programming the right way.
My problem is that when I press "Hide the scoreboard" (I create Labels for scoreboard) label is not hiding. Any suggestions?
I know that commands and defs must be in the same "tree", but how to arrange it when using the OOP.
So here is my code
import tkinter as tk
def forget():
scoreboard.pack_forget()
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.title("Scorebug")
self.geometry("500x300")
self.configure(background="green")
scoreboard = tk.Label(self, text="This is like scoreboard")
scoreboard.pack()
class Control(tk.Toplevel):
def __init__(self):
super().__init__()
self.title("Controls")
self.geometry("100x300")
self.configure(background="red")
hidelabels = tk.Button(self, text="Hide the scoreboard", command=forget)
hidelabels.pack()
app = Main()
ctr = Control()
ctr.mainloop()
app.mainloop()
First you should use self.scoreboard to have access from other places.
self.scoreboard = tk.Label(self, text="This is like scoreboard")
self.scoreboard.pack()
and now you can remove it using
command=app.scoreboard.pack_forget
You can also send main window as argument to second window
ctr = Control(app)
class Control(tk.Toplevel):
def __init__(self, parent):
and then you can bind
command=parent.scoreboard.pack_forget
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.title("Scorebug")
self.geometry("500x300")
self.configure(background="green")
self.scoreboard = tk.Label(self, text="This is like scoreboard")
self.scoreboard.pack()
class Control(tk.Toplevel):
def __init__(self, parent):
super().__init__()
self.title("Controls")
self.geometry("100x300")
self.configure(background="red")
hidelabels = tk.Button(self, text="Hide the scoreboard", command=parent.scoreboard.pack_forget)
hidelabels.pack()
app = Main()
ctr = Control(app)
app.mainloop()
EDIT: You can also send only scireboard as argument to second window
ctr = Control(app.scoreboard)
and then you can bind
command=parent.pack_forget
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.
I would like to know how to create buttons widget in Tkinter with multiple labels as the following figure.
Buttons with sub-label.
As you can see, that in some buttons there is a sub-label, e.g., Button "X" has another small label of "A". I have tried to search for the solution, but found none.
Thank you very much in advance.
You can put your labels in a Frame, and have the Button be the parent of that frame. However, you'd need to be a little bit clever and overcome some issues, such as:
inability to click the button properly (you can only click on edges, because the frame containing labels is in the middle), which means you'd have to do some event-handling (clicking on the frame and the labels inside needs to trigger the same event as if the button was clicked)
unsynchronised colours when hovering over the button itself
and a few other minor details, like properly configuring the button's relief when it's clicked (don't forget, you may be clicking the frame or the labels!), etc.
Here is an MCVE:
import sys
import string
import random
try:
import tkinter as tk
from tkinter import ttk
except ImportError:
import Tkinter as tk
import ttk
CHARS = string.ascii_letters + string.digits
class CustomButton(tk.Button):
"""
CustomButton class inherits from tk.Button, which means it
behaves just like an ordinary tk.Button widget, but it also
has some extended functionality.
"""
def __init__(self, parent, *args, **kwargs):
super().__init__()
self.command = kwargs.get('command')
self.frame = tk.Frame(self)
self.frame.pack(fill='none', expand=False, pady=(3, 0))
self.upper_label = ttk.Label(self.frame, text=kwargs.get('upper_text'))
self.upper_label.grid(row=0, column=0)
self.bottom_label = ttk.Label(self.frame, text=kwargs.get('bottom_text'))
self.bottom_label.grid(row=1, column=1)
self.frame.pack_propagate(False)
self.configure(width=kwargs.get('width'), height=kwargs.get('height'))
self.pack_propagate(False)
self.clicked = tk.BooleanVar()
self.clicked.trace_add('write', self._button_cmd)
self.bind('<Enter>', self._on_enter)
self.bind('<Leave>', self._on_leave)
self.frame.bind('<Enter>', self._on_enter)
self.frame.bind('<Button-1>', self._on_click)
self.upper_label.bind('<Button-1>', self._on_click)
self.bottom_label.bind('<Button-1>', self._on_click)
def _button_cmd(self, *_):
"""
Callback helper method
"""
if self.clicked.get() and self.command is not None:
self.command()
def _on_enter(self, _):
"""
Callback helper method which is triggered
when the cursor enters the widget's 'territory'
"""
for widget in (self, self.frame, self.upper_label, self.bottom_label):
widget.configure(background=self.cget('activebackground'))
def _on_leave(self, _):
"""
Callback helper method which is triggered
when the cursor leaves the widget's 'territory'
"""
for widget in (self, self.frame, self.upper_label, self.bottom_label):
widget.configure(background=self.cget('highlightbackground'))
def _on_click(self, _):
"""
Callback helper method which is triggered
when the the widget is clicked
"""
self.clicked.set(True)
self.configure(relief='sunken')
self.after(100, lambda: [
self.configure(relief='raised'), self.clicked.set(False)
])
class KeyboardMCVE(tk.Tk):
"""
MCVE class for demonstration purposes
"""
def __init__(self):
super().__init__()
self.title('Keyboard')
self._widgets = []
self._create_widgets()
def _create_widgets(self):
"""
Instantiating all the "keys" (buttons) on the fly while both
configuring and laying them out properly at the same time.
"""
for row in range(5):
current_row = []
for column in range(15):
button = CustomButton(
self,
width=1, height=2,
upper_text=random.choice(CHARS),
bottom_text=random.choice(CHARS)
)
button.grid(row=row, column=column, sticky='nswe')
current_row.append(button)
self._widgets.append(current_row)
if __name__ == '__main__':
sys.exit(KeyboardMCVE().mainloop())
Alternatively, a simple workaround would be to use Unicode superscripts/subscripts.
In the following code, when I press the button to add some secondary windows, and then try to close a window by using "Command-w" it does not always close the active window. But if I disable the menu creation by commenting the line self.gerar_menu(), windows are opened and closed as expected (I mean, by clicking the red 'x' button or by pressing Command-W in OS X). Any idea about what is wrong here?
Here is my current test code:
#!/usr/bin/env python3.6
# encoding: utf-8
import tkinter as tk
import tkinter.font
from tkinter import ttk
class baseApp(ttk.Frame):
"""
Parent classe for main app window (will include some aditional methods and properties).
"""
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.master = master
self.mainframe = ttk.Frame(master)
self.mainframe.pack()
class App(baseApp):
""" Base class for the main application window """
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.master = master
#self.gerar_menu() # This line breaks "Command-w" functionality
self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window")
self.lbl_text.pack()
self.btn = ttk.Button(self.mainframe, text="Open Second window",
command=lambda: self.create_detail_window(self, number=0))
self.btn.pack()
self.newDetailsWindow = {}
self.windows_count=0
def gerar_menu(self):
""" generate the application menu """
self.menu = tk.Menu(root)
root.config(menu=self.menu)
self.fileMenu = tk.Menu(self.menu)
self.menu.add_cascade(label="File", menu=self.fileMenu)
self.fileMenu.add_command(label="New Document", command=None, accelerator="Command+n")
def create_detail_window(self, *event, number=None):
self.windows_count += 1
self.newDetailsWindow[self.windows_count]=tk.Toplevel()
self.newDetailsWindow[self.windows_count].geometry('900x600+80+130')
self.newDetailsWindow[self.windows_count].title(f'Detail: {self.windows_count}')
self.newDetailsWindow[self.windows_count].wm_protocol("WM_DELETE_WINDOW", self.newDetailsWindow[self.windows_count].destroy)
self.newDetailsWindow[self.windows_count].bind("Command-w", lambda event: self.newDetailsWindow[-1].destroy())
self.detail_window = detailWindow(self.newDetailsWindow[self.windows_count], self.windows_count)
self.newDetailsWindow[self.windows_count].focus()
print(self.newDetailsWindow)
class detailWindow(ttk.Frame):
""" Base class for secondary windows """
def __init__(self, master, rep_num, *args,**kwargs):
super().__init__(master,*args,**kwargs)
self.num_rep = rep_num
self.master.minsize(900, 600)
self.master.maxsize(900, 600)
print(f"Showing details about nr. {self.num_rep}")
self.mainframe = ttk.Frame(master)
self.mainframe.pack()
self.lbl_text = ttk.Label(self.mainframe,
text=f"Showing details about nr. {self.num_rep}")
self.lbl_text.pack()
if __name__ == "__main__":
root = tk.Tk()
janela_principal = App(root)
root.title('Main Window')
root.bind_all("<Mod2-q>", exit)
root.mainloop()
If you want to create a binding for closing a window, you need the function to act upon the actual window that received the event. Your code is always deleting the last window that was opened no matter which window received the event.
The first step is to bind to a function rather than using lambda. While lambda has its uses, binding to a named function is much easier to debug and maintain.
Once the function is called, the event object can tell you which window got the event via the widget attribute of the event object. Given this window, you can get the toplevel window that this window is in (or itself, if it's a toplevel window) via the winfo_toplevel command.
For example:
window = tk.Toplevel(...)
...
window.bind("<Command-w>", self.destroy_window)
...
def destroy_window(self, event):
window = event.widget.winfo_toplevel()
window.destroy()
I have a python-tkinter gui app that I've been trying to find some way to add in some functionality. I was hoping there would be a way to right-click on an item in the app's listbox area and bring up a context menu. Is tkinter able to accomplish this? Would I be better off looking into gtk or some other gui-toolkit?
You would create a Menu instance and write a function that calls
its post() or tk_popup() method.
The tkinter documentation doesn't currently have any information about tk_popup().
Read the Tk documentation for a description, or the source:
library/menu.tcl in the Tcl/Tk source:
::tk_popup --
This procedure pops up a menu and sets things up for traversing
the menu and its submenus.
Arguments:
menu - Name of the menu to be popped up.
x, y - Root coordinates at which to pop up the menu.
entry - Index of a menu entry to center over (x,y).
If omitted or specified as {}, then menu's
upper-left corner goes at (x,y).
tkinter/__init__.py in the Python source:
def tk_popup(self, x, y, entry=""):
"""Post the menu at position X,Y with entry ENTRY."""
self.tk.call('tk_popup', self._w, x, y, entry)
You associate your context menu invoking function with right-click via:
the_widget_clicked_on.bind("<Button-3>", your_function).
However, the number associated with right-click is not the same on every platform.
library/tk.tcl in the Tcl/Tk source:
On Darwin/Aqua, buttons from left to right are 1,3,2.
On Darwin/X11 with recent XQuartz as the X server, they are 1,2,3;
other X servers may differ.
Here is an example I wrote that adds a context menu to a Listbox:
import tkinter # Tkinter -> tkinter in Python 3
class FancyListbox(tkinter.Listbox):
def __init__(self, parent, *args, **kwargs):
tkinter.Listbox.__init__(self, parent, *args, **kwargs)
self.popup_menu = tkinter.Menu(self, tearoff=0)
self.popup_menu.add_command(label="Delete",
command=self.delete_selected)
self.popup_menu.add_command(label="Select All",
command=self.select_all)
self.bind("<Button-3>", self.popup) # Button-2 on Aqua
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def delete_selected(self):
for i in self.curselection()[::-1]:
self.delete(i)
def select_all(self):
self.selection_set(0, 'end')
root = tkinter.Tk()
flb = FancyListbox(root, selectmode='multiple')
for n in range(10):
flb.insert('end', n)
flb.pack()
root.mainloop()
The use of grab_release() was observed in an example on effbot.
Its effect might not be the same on all systems.
I made some changes to the conext menu code above in order to adjust my demand and I think it would be useful to share:
Version 1:
import tkinter as tk
from tkinter import ttk
class Main(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
master.geometry('500x350')
self.master = master
self.tree = ttk.Treeview(self.master, height=15)
self.tree.pack(fill='x')
self.btn = tk.Button(master, text='click', command=self.clickbtn)
self.btn.pack()
self.aMenu = tk.Menu(master, tearoff=0)
self.aMenu.add_command(label='Delete', command=self.delete)
self.aMenu.add_command(label='Say Hello', command=self.hello)
self.num = 0
# attach popup to treeview widget
self.tree.bind("<Button-3>", self.popup)
def clickbtn(self):
text = 'Hello ' + str(self.num)
self.tree.insert('', 'end', text=text)
self.num += 1
def delete(self):
print(self.tree.focus())
if self.iid:
self.tree.delete(self.iid)
def hello(self):
print ('hello!')
def popup(self, event):
self.iid = self.tree.identify_row(event.y)
if self.iid:
# mouse pointer over item
self.tree.selection_set(self.iid)
self.aMenu.post(event.x_root, event.y_root)
else:
pass
root = tk.Tk()
app=Main(root)
root.mainloop()
Version 2:
import tkinter as tk
from tkinter import ttk
class Main(tk.Frame):
def __init__(self, master):
master.geometry('500x350')
self.master = master
tk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self.master, height=15)
self.tree.pack(fill='x')
self.btn = tk.Button(master, text='click', command=self.clickbtn)
self.btn.pack()
self.rclick = RightClick(self.master)
self.num = 0
# attach popup to treeview widget
self.tree.bind('<Button-3>', self.rclick.popup)
def clickbtn(self):
text = 'Hello ' + str(self.num)
self.tree.insert('', 'end', text=text)
self.num += 1
class RightClick:
def __init__(self, master):
# create a popup menu
self.aMenu = tk.Menu(master, tearoff=0)
self.aMenu.add_command(label='Delete', command=self.delete)
self.aMenu.add_command(label='Say Hello', command=self.hello)
self.tree_item = ''
def delete(self):
if self.tree_item:
app.tree.delete(self.tree_item)
def hello(self):
print ('hello!')
def popup(self, event):
self.aMenu.post(event.x_root, event.y_root)
self.tree_item = app.tree.focus()
root = tk.Tk()
app=Main(root)
root.mainloop()
from tkinter import *
root=Tk()
root.geometry("500x400+200+100")
class Menu_Entry(Entry):
def __init__(self,perant,*args,**kwargs):
Entry.__init__(self,perant,*args,**kwargs)
self.popup_menu=Menu(self,tearoff=0,background='#1c1b1a',fg='white',
activebackground='#534c5c',
activeforeground='Yellow')
self.popup_menu.add_command(label="Cut ",command=self.Cut,
accelerator='Ctrl+V')
self.popup_menu.add_command(label="Copy ",command=self.Copy,compound=LEFT,
accelerator='Ctrl+C')
self.popup_menu.add_command(label="Paste ",command=self.Paste,accelerator='Ctrl+V')
self.popup_menu.add_separator()
self.popup_menu.add_command(label="Select all",command=self.select_all,accelerator="Ctrl+A")
self.popup_menu.add_command(label="Delete",command=self.delete_only,accelerator=" Delete")
self.popup_menu.add_command(label="Delete all",command=self.delete_selected,accelerator="Ctrl+D")
self.bind('<Button-3>',self.popup)
self.bind("<Control-d>",self.delete_selected_with_e1)
self.bind('<App>',self.popup)
self.context_menu = Menu(self, tearoff=0)
self.context_menu.add_command(label="Cut")
self.context_menu.add_command(label="Copy")
self.context_menu.add_command(label="Paste")
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def Copy(self):
self.event_generate('<<Copy>>')
def Paste(self):
self.event_generate('<<Paste>>')
def Cut(self):
self.event_generate('<<Cut>>')
def delete_selected_with_e1(self,event):
self.select_range(0, END)
self.focus()
self.event_generate("<Delete>")
def delete_selected(self):
self.select_range(0, END)
self.focus()
self.event_generate("<Delete>")
def delete_only(self):
self.event_generate("<BackSpace>")
def select_all(self):
self.select_range(0, END)
self.focus()
ent=Menu_Entry(root)
ent.pack()
root.mainloop()
Important Caveat:
(Assuming the event argument that contains the coordinates is called "event"): Nothing will happen or be visible when you call tk_popup(...) unless you use "event.x_root" and "event.y_root" as arguments. If you do the obvious of using "event.x" and "event.y", it won't work, even though the names of the coordinates are "x" and "y" and there is no mention of "x_root" and "y_root" anywhere within it.
As for the grab_release(..), it's not necessary, anywhere. "tearoff=0" also isn't necessary, setting it to 1 (which is default), simply adds a dotted line entry to the context menu. If you click on it, it detaches the context menu and makes it its own top-level window with window decorators. tearoff=0 will hide this entry. Moreover, it doesn't matter if you set the menu's master to any specific widget or root, or anything at all.