Binding a key to a function in Tkinter - python

I have a problem with binding a key (Ctrl-A) to a function (select all) in menu_bar function. Whatever I do, this key combination is moving me to beginning of the line.
My main program:
from tkinter import *
import tkinter as tk
import func
from tkinter import font
class Editor():
def __init__(self, window):
# Window + Title
window.geometry('1200x800')
window.title('SuperEditor')
# Scrool bar
scrollbar = Scrollbar(window)
scrollbar.pack(side=RIGHT,fill=Y)
# Text area
user_font=font.Font(size=20)
editor = Text(window,width=400,height=450,yscrollcommand=scrollbar.set, undo = True,
font=user_font)
editor.pack(fill=BOTH)
scrollbar.config(command = editor.yview)
# Selfs
self.window = window
self.editor = editor
self.user_font = user_font
editor.bind("<Control-A>", func.select_all(editor))
def menu_bar(self):
menubar = Menu(self.window)
# Edit menu
editmenu = Menu(menubar, tearoff=0)
editmenu.add_command(label="Select All", command=lambda: func.select_all(self.editor), accelerator='Ctrl-A')
menubar.add_cascade(label="Edit", menu=editmenu)
# Add the menu bar
self.window.config(menu=menubar)
def main():
# Create a SuperEditor window
se_window = Tk()
# Create a text editor
editor = Editor(se_window)
# Create menubar
editor.menu_bar()
# Rune the SuperEditor
se_window.mainloop()
if __name__ == "__main__":
main()
Functions file:
import tkinter as tk
import tkinter.filedialog as fd
from tkinter import font
def select_all(text, event=None):
'''Select all text'''
if event:
print('Up')
text.tag_add("sel", "1.0","end")
text.tag_config("sel",background="gray",foreground="white")
return 'break'
else:
text.tag_add("sel", "1.0","end")
text.tag_config("sel",background="gray",foreground="white")
When I put this in __init__, it selects everything at start, but doesn't work later:
self.editor.bind("<Control-A>", functions.select_all(editor))
I tried to do it with lambda, then i get:
TypeError: Editor.menu_bar.<locals>.<lambda>() takes 0 positional arguments but 1 was given

Change
editor.bind("<Control-A>", func.select_all(editor))
to
editor.bind("<Control-A>", lambda event: func.select_all(editor, event=event))
This answer was posted as an edit to the question Binding a key to a function in Tkinter by the OP koleks92 under CC BY-SA 4.0.

Related

how to open only 1 new window in tkinter after button click?

I have referred to this post Python tkinter: How can I ensure only ONE child window is created onclick and not a new window every time the button is clicked? but i couldn't get my code to work. Could anyone please help me?
from tkinter import *
import os
import sys
import _thread
from s2 import work
from s1 import work2
root = Tk()
root.resizable(width=False, height=False)
root.title("Sample tkinter")
root.geometry(r"550x550")
def open_entry():
os.system("data1.py")
def open_aisle():
os.system("data2.py")
def close_entry():
os.system("data10.py")
def close_aisle():
os.system("data20.py")
def run():
_thread.start_new_thread(work,())
_thread.start_new_thread(work2,())
def openNewWindow():
global createdb_btn
newWindow = Toplevel(root)
newWindow.resizable(width=False,height=False)
newWindow.title("New Window")
newWindow.geometry("300x300")
create_front = Button(newWindow,text="Entry DB",relief=FLAT,bg="black",fg="white",command=open_entry)
create_front.place(x=50,y=80)
create_aisle = Button(newWindow,text="Aisle DB",relief=FLAT,bg="black",fg="white",command=open_aisle)
create_aisle.place(x=145,y=80)
def openAnotherWindow():
global removedb_btn
otherWindow = Toplevel(root)
otherWindow.resizable(width=False,height=False)
otherWindow.title("New Window")
otherWindow.geometry("300x300")
remove_front = Button(otherWindow,text="Entry DB",relief=FLAT,bg="black",fg="white",command=close_entry)
remove_front.place(x=50,y=80)
remove_aisle = Button(otherWindow,text="Aisle DB",relief=FLAT,bg="black",fg="white",command=close_aisle)
remove_aisle.place(x=145,y=80)
removedb_btn = Button(root,text="Remove databases",relief=FLAT,bg="black",fg="white",state=DISABLED)
removedb_btn.place(x=380,y=120)
camera_btn = Button(root,text="Open Cams",relief=FLAT,bg="black",fg="white",command=run)
camera_btn.place(x=50,y=120)
createdb_btn = Button(root,text="Create databases",relief=FLAT,bg="black",fg="white",command=openNewWindow)
createdb_btn.place(x=205,y=120)
removedb_btn = Button(root,text="Remove databases",relief=FLAT,bg="black",fg="white",command=openAnotherWindow)
removedb_btn.place(x=380,y=120)
root.mainloop()
I am trying to achieve this in both the openNewWindow function and openAnotherWindow function.
This is what I did. Changed the lines:
createdb_btn = Button(root,text="Create databases",relief=FLAT,bg="black",fg="white")
createdb_btn.config(command=lambda b=createdb_btn: (openNewWindow(b), b.config(state='disabled')))
and added
newWindow.protocol("WM_DELETE_WINDOW", lambda:(button.config(state='active'), newWindow.destroy()))
Inside newWindow function so that when you pass in the button that created the window as a variable and use that on the overwrite of opennewWindows function to reactivate it just before newWindow.destroy()

Tkinter combobox not working when focus_force is called

Am trying to select item in my combobox but it does not work after i call focus_force() on the toplevel window other widget in the window works but the combobox selection is not working.
When i remove focus_force() from my code it works alright but want to keep it for main code.
#!python3
import tkinter as tk
import tkinter.ttk as ttk
def toplevel_window():
global top, cb
top = tk.Toplevel(root)
top.title("TOP")
top.geometry("300x300")
cb = ttk.Combobox(top, values=["djdjd", "fjfjf"])
cb.pack()
BT = tk.Button(top, text="print", command=combo_select)
BT.pack(side=tk.BOTTOM)
top.bind("<FocusOut>", focusout_func)
top.bind("<<ComboboxSelected>>", combo_select)
def focusout_func(event):
print(cb.get())
top.focus_force()
top.bell()
def combo_select(event=None):
print(cb.get())
root = tk.Tk()
root.geometry("600x600")
root.title("root")
b = tk.Button(text="OPEN TOPLEVEL", command=toplevel_window)
b.pack()
root.mainloop()
The reason the Combobox selection is not working is because you're causing the funcion focusout_func() to be called when an attempt is made to select it in the window containing it and that shifts the focus back to the toplevel window itself, which prevents the selection from happening.
One solution is simply to not use a <FocusOut> callback function at all. Instead, first add a top.focus_force() call to the end of toplevel_window() function to switch the focus to to it immediately after it's created.
Secondly, to keep the focus on the windows with the Combobox and make a bell alert sound when attempts are made to switch focus away from the it, you can bind a <FocusIn> callback to the root window and check the status of the other window in it (and prevent the focus from being changed if it exists and is being displayed).
Here's what I'm suggesting:
import tkinter as tk
import tkinter.ttk as ttk
from _tkinter import TclError
def toplevel_window():
global top, cb
top = tk.Toplevel(root)
top.title("TOP")
top.geometry("300x300")
cb = ttk.Combobox(top, values=["djdjd", "fjfjf"])
cb.pack()
btn = tk.Button(top, text="print", command=combo_select)
btn.pack(side=tk.BOTTOM)
top.bind("<<ComboboxSelected>>", combo_select)
top.focus_force()
def combo_select(event=None):
print('cb.get():', cb.get())
def root_focusin_callback(event):
try:
top_state = top.state() # Check status of toplevel widget.
except (NameError, TclError) as exc:
pass # Ignore - widget doesn't exist or was closed.
else:
top.bell()
top.focus_force()
root = tk.Tk()
root.geometry("600x600")
root.title("root")
root.bind("<FocusIn>", root_focusin_callback)
b = tk.Button(text="OPEN TOPLEVEL", command=toplevel_window)
b.pack()
root.mainloop()

Tkinter: How can I provide text status updates during execution that is in response to a single event?

I have primary.py, the primary file of a Tkinter app that has a user select a file, and then calls a variety of functions contained in other.py.
The functions in other.py take a while to run, so I want to provide users with updates after each one is complete.
My issue is that all of these messages appear all at once, after the completion of step 3. I want them to appear after each step, as I tried to write it. How can I ensure 'Step 1 in Progress' appears before dummy2 is called, and not at the end of all the functions being performed?
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter import ttk
from multiprocessing import Process, Pipe
import other.py
class GUI:
def __init__(self, master):
self.master = master
def tkinter_display(the_message):
ttk.Label(frame, text=the_message, style='my.TLabel').pack()
def select():
path = askopenfilename()
tkinter_display('The script is running...')
if __name__ == '__main__':
side1, side2 = Pipe()
tkinter_functions_conn.send(path)
p_import = Process(target=other.dummy, args=(side2,))
p_import.start()
tkinter_display('Step 1 in progress')
tkinter_display(side1.recv())
p_import2 = Process(target=other.dummy2, args=(side2,))
p_import2.start()
tkinter_display('Step 2 in progress')
p_import3 = Process(target=other.dummy3, args=(side2,))
p_import3.start()
tkinter_display('Step 3 in progress')
if __name__ == '__main__':
root = Tk()
my_gui = GUI(root)
# Styles
root.style = ttk.Style()
root.style.configure('my.TButton')
root.style.configure('my.TLabel')
# Display
frame = Frame()
frame.pack()
ttk.Button(frame, text="Select", command=select, style='my.TButton').pack()
root.mainloop()
You can force the GUI to update by changing your tkinter_display method to this:
def tkinter_display(the_message):
ttk.Label(frame, text=the_message, style='my.TLabel').pack()
frame.update()

How do you close a tkinter window in another function?

I want a button in my window to open a new window and close the previous one. Is it possible to have one button do both of these? I've tried in the following code, but it hasn't worked, just told me that window is not defined:
import tkinter
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = window2).pack()
window.mainloop()
def window2():
window.destroy() #This is where the error is
menu = tkinter.Tk()
etc, etc, etc
window1()
First, you need to return the window object from the first function:
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = lambda: window2(window)).pack()
window.mainloop()
return window
Then, you need to pass the window as an argument to your function:
def window2(window):
window.destroy()
menu = tkinter.Tk()
And then call window1 with:
window = window1()
and click the button to destroy it and do the rest
This is an example using Toplevels, which is usually a better choice than creating, destroying, re-creating Tk() instances. The unique Toplevel ID is passed to the close_it function using partial(). You would, of course, combine them or have the close function call the open function.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
class OpenToplevels():
""" open and close additional Toplevels with a button
"""
def __init__(self):
self.root = tk.Tk()
self.button_ctr=0
but=tk.Button(self.root, text="Open a Toplevel",
command=self.open_another)
but.grid(row=0, column=0)
tk.Button(self.root, text="Exit Tkinter", bg="red",
command=self.root.quit).grid(row=1, column=0, sticky="we")
self.root.mainloop()
def close_it(self, id):
id.destroy()
def open_another(self):
self.button_ctr += 1
id = tk.Toplevel(self.root)
id.title("Toplevel #%d" % (self.button_ctr))
tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
command=partial(self.close_it, id),
bg="orange", width=20).grid(row=1, column=0)
Ot=OpenToplevels()
Yes. Is possible. But you'll need to def that:
def window1:
blablabla
blablabla
def window2:
window2.destroy() <-- Here where the error was
How you noticed, put your name of window what you want Destroy and it will work!
using Python3
You could use a "global" such as:
root = Tk()
root.title('This is the root window')
def window_create():
global window_one
window_one = Tk()
window_one.title('This is window 1')
Then, from any function (or elsewhere) when you want to destroy window_one, do:
def window_destroyer():
window_one.destroy()
You could call your window_destroyer function from a button anywhere such as root which the example shows:
kill_window_btn = Button(root, text="Destroy", command=window_destroyer).pack()
Of course, follow your own naming conventions. :)
It seems to me, just 'global window_one' would solve it.

Multiple windows open at once in python

I've searched and found a few things on parent windows in python but that is not what I was looking for. I am trying make a simple program that opens a window and another window after that when the previous one is closed. I was also trying to implement some kind of loop or sleep time to destroy the window by default if the user does not. This is what I have (I'm new please don't laugh)
from tkinter import *
import time
root = Tk()
i = 0
if i < 1:
root.title("title")
logo = PhotoImage(file="burger.gif")
w1 = Label(root, image=logo).pack()
time.sleep(3)
root.destroy()
i = i + 1
if i == 1:
root.title("title")
photoTwo = PhotoImage(file="freedom.gif")
labelTwo = Label(root, image=photoTwo).pack()
time.sleep(3)
root.destroy()
i = i + 1
mainloop.()
Perhaps you're looking for something like this:
from tkinter import *
import time
def openNewWindow():
firstWindow.destroy()
secondWindow = Tk()
secondWindow.title("Second Window")
photoTwo = PhotoImage(file="freedom.gif")
labelTwo = Label(secondWindow, image=photoTwo).pack()
secondWindow.mainloop()
firstWindow = Tk()
firstWindow.title("First Window")
logo = PhotoImage(file="burger.gif")
w1 = Label(firstWindow, image=logo).pack()
closeBttn = Button(firstWindow, text="Close!", command=openNewWindow)
closeBttn.pack()
firstWindow.mainloop()
This creates a button in the first window, which the user clicks. This then calls the openNewWindow function, which destroys that window, and opens the second window. I'm not sure there's a way to do this using the window exit button.
To get create a more sustainable window creation, use this:
from tkinter import *
import time
def openThirdWindow(previouswindow):
previouswindow.destroy()
thirdWindow = Tk()
thirdWindow.title("Third Window")
photoTwo = PhotoImage(file="freedom.gif")
labelTwo = Label(thirdWindow, image=photoTwo).pack()
thirdWindow.mainloop()
def openSecondWindow(previouswindow):
previouswindow.destroy()
secondWindow = Tk()
secondWindow.title("Second Window")
photoTwo = PhotoImage(file="freedom.gif")
labelTwo = Label(secondWindow, image=photoTwo).pack()
closeBttn = Button(secondWindow, text="Close!", command= lambda: openThirdWindow(secondWindow))
closeBttn.pack()
secondWindow.mainloop()
def openFirstWindow():
firstWindow = Tk()
firstWindow.title("First Window")
logo = PhotoImage(file="burger.gif")
w1 = Label(firstWindow, image=logo).pack()
closeBttn = Button(firstWindow, text="Close!", command= lambda: openSecondWindow(firstWindow))
closeBttn.pack()
firstWindow.mainloop()
openFirstWindow()
This places the opening of each window in a seperate function, and passes the name of the window through the button presses into the next function. Another method would be setting the window names as global, but this is messy.
The function "lambda:" calls the function, in tkinter you must type this if you want to pass something through a command.
We initiate the whole process first first called "openFirstWindow()"

Categories