Open window and focus textbox tkinter - python

I have a (second) tkinter window, which, when opened, does not get the focus, but rather the first window remains focused (although the second window appears in front of the other).
It contains a textbox which I want to be able to type in, but I have to double-click it in order to type.
How do I focus the textbox when opening the window?
My tries:
textbox.focus_set(),
window.grab_set(),
window.focus_set()
None of them did what I wanted to do.
EDIT:
Instead, .focus_set() raises an error when (and only when) closing the main window: can't invoke "focus" command: application has been destroyed
This is my current code (tkWin is the main window, tkcWinis the second window):
def click(self, field):
import _tkinter
if field != None:
try:
self.tkcWin = Tk()#creating window
self.tkcWin.focus()
self.tkcWin.title(field)
self.tkcWin.geometry('300x100')
self.mainframe = Frame(master=self.tkcWin,background="#60BF98")
self.mainframe.place(x=0, y=0, width=300, height=300)
self.textb = Text(master=self.mainframe)
self.textb.place(x=0, y=50)
self.textb.bind("<Return>",lambda a: self.setM(field))
self.textb.bind("<Return>",lambda a: self.tkcWin.destroy(),True)
self.tkcWin.grab_set()
self.tkWin.wait_window(self.tkcWin)
self.textb.focus_set()
hwnd = self.tkcWin.winfo_id()
ctypes.windll.user32.SetFocus(hwnd)
self.tkcWin.mainloop()
except _tkinter.TclError:
self.tkcWin.destroy()

It turns out that you can simply call the secondary window's deiconify() method and then the widget's focus_set() method:
toplevel.deiconify()
text.focus_set()
Here's the original work-around for Windows (no longer recommended):
Start by adding import ctypes at the top.
Go ahead and focus your widget like you have with: text.focus_set()
Get the hwnd of the second window: top_hwnd = toplevel.winfo_id()
And finally activate the second window with: ctypes.windll.user32.SetFocus(top_hwnd)

Related

tkinter: Unbind not working, even workarounds don't work

I bring up here a problem, that's been there for ages, but is obviously still not solved and older workarounds don't work on my Python 3.7.2 (64-bit on Win10).
I have this code:
import tkinter as tk
import tkinter.simpledialog
# message box to enter a value where to set the scale to
class EnterValueBox(tk.simpledialog.Dialog):
def body(self, master):
self.e = tk.Entry(self, width=10)
self.e.pack(pady=5)
return self.e # initial focus
def apply(self):
print(self.e.get())
# callback to open message box
def enterValue(event):
EnterValueBox(root, title="Enter Value 0..100")
# create window with scale widget
root = tk.Tk()
scale = tk.Scale(root, orient=tk.HORIZONTAL, from_=0, to=100)
scale.pack()
# unbind any button-3 events
scale.unbind("<ButtonPress-3>")
scale.unbind("<ButtonRelease-3>")
scale.unbind("<Button-3>")
# bind button-3 press event to open message box
scale.bind("<ButtonPress-3>", enterValue)
tk.mainloop()
It creates a window with a single scale widget. I want to bind ButtonPress-3 to open a little dialog to directly enter a new value. The code only prints that value to the shell, but the example shows, that the unbind is not working, because after printing the value, the dialog box is closed (when the user clicks OK) and then the default binding is executed, which sets the slider, where the user clicked in the trough of the slider widget.
I tried the workaround from Deleting and changing a tkinter event binding with a PatchedScale widget (instead of the PatchedCanvas shown there), but that didn't make any difference.
Any help would be greatly appreciated!
The default bindings are not on the widget, they are on the widget class. Calling unbind on a widget for which there is no widget-specific binding won't have any effect.
If you don't want the default binding to run after your widget-specific binding, the normal technique is to have your bound function return the string break.
def enterValue(event):
EnterValueBox(root, title="Enter Value 0..100")
return "break"

Switching between windows in tkinter

First I have open main window then through that I have another window called order .Now I have to make a button that get to the main window again
def back():
wn.destroy()
import purchase
Button(text="Back",width='30',height='5',command=back,fg='black',bg='green',bd='5',font=(40),command=back).place(x='100',y='600')
You need to make the button start a function to open the window.
Ex.
def OpenNewWindow:
newWindow = TopLevel(master)
B1 = tk.Button(text="example", command = OpenNewWindow
B1.pack()
Treat it like a normal window: geometry, title, etc.
but remember one thing:
KEEP THE TOPLEVEL IN THE MAIN LOOP

Tkinter Focus lost after askstring

I am currently implementing a program that uses many tkinter frames and while subframe is being opened I want the superframe to be locked for the user (otherwise things will not work out). After some research I found the grab_set and grab_release method which worked quite fine.
However once the subframe (instanciated by Toplevel) calls the askstring the grab is "losed" and the user can interact with the superlevel window again. An example would be this (very simplified code):
import tkinter as tk
import tkinter.simpledialog
root = tk.Tk()
def open_sublevel():
tl = tk.Toplevel(root)
def ask():
print(tk.simpledialog.askstring("askstring", "askstring"))
tk.Button(tl, text="ask", command=ask).pack()
tl.grab_set()
root.wait_window(tl)
tl.grab_release()
print("release")
tk.Button(root, text="asdf", command=open_sublevel).pack()
tk.mainloop()
Once the user opens the subframe by clicking "asdf" the frame containing "asdf" will be locked for the duration while the subframe is opened. However once the user selects the "ask"-Button in the subframe this "lock" somehow disappears.
According to the notes in the tkinter library:
A grab directs all events to this and descendant widgets in the application.
I am not able so far to find any documentation that would explain why the grab_set() is falling off after you finish submitting your askstring but I would imaging it is because once the widget is gone the grab_set() falls off. Just like if you were to close out the Toplevel window.
In this case tl.grab_release() does not appear to be needed as grab releases once the window closes.
From what I have tested if you reset the grab_set() after the askstring is done then it will still work properly.
You need to simply add tl.grab_set() just below print(tk.simpledialog.askstring("askstring", "askstring")).
Modified code below:
import tkinter as tk
import tkinter.simpledialog
root = tk.Tk()
def open_sublevel():
tl = tk.Toplevel(root)
tl.grab_set()
def ask():
print(tk.simpledialog.askstring("askstring", "askstring"))
tl.grab_set()
tk.Button(tl, text="ask", command=ask).pack()
print("release")
tk.Button(root, text="asdf", command=open_sublevel).pack()
tk.mainloop()
setting the parent for simpledialog will make simpledialog take focus
x = simpledialog(parent = window_x, title = z etc.)
this will make sure x takes focus and not withdraw

Detect click on border of widget in tkinter 3

I am trying to make a kind of movable widget program in python with Tkinter, but I ran into a problem. I can't detect a click without interfering with the function of the widget that you click on. (example: Text or Button widget)
Here is an example:
import tkinter as tk
main = tk.Tk()
notes = tk.Text(main, height = 15, bd = 4)
notes.place(y = 10, x = 20)
notes.bind("<Button-1>", lambda event: print("hello"))
But if you try and click in the middle, it still works. Is there any way to make it only clickable on the border and not the widget itself?
You can prevent the default behavior by returning "break" from the event handler. For example:
def hello(event):
print("hello")
return "break"
...
notes.bind("<Button-1>", hello)
The same works for any widget. This prevents the default behavior (moving the cursor, clicking the button) from happening.
Another choice is to put each widget in a frame with a small border, and then put the binding on the frame.

Toplevel window in Python keeps appearing under the root window

I have a toplevel window that keeps appear underneath the root window, which is pretty annoying. Any idea how to make sure that the toplevel gets the focus when the function is called?
def setup(self):
self.setup_step = 1
setup_window = Toplevel(self)
setup_window.config(bg = "#000000", height = "600", width = "850")
setup_window.title("RoastMaster Setup")
setup_window.geometry("800x600")
You can try by making the toplevel window transient.
You might try raising it and giving it focus. It could be that you window manager wants the window with focus to always be on top.

Categories