How to change focus of simpledialog in Tkinter? - python

I have several simpledialog popup windows. The first one that shows is in focus and after it closes then every single one after that is not in focus. The simpledialog code is this:
from tkinter import *
from tkinter import messagebox
import os
import tkinter as tk
from tkinter import simpledialog
def doing_stocks():
name = myaskstring("Input data", "Enter box number:", parent = root)
name2 = myaskstring("Input data2", "Enter box number2:", parent = root)
name3 = myaskstring("Input data3", "Enter box number3:", parent = root)
class My_QueryString(tk.simpledialog._QueryString):
def body(self, master):
self.bind('<KP_Enter>', self.ok)
self.bind('<Return>', self.ok)
w = Label(master, text=self.prompt, justify=LEFT)
w.grid(row=0, padx=5, sticky=W)
self.entry = Entry(master, name="entry")
self.entry.grid(row=1, padx=5, sticky=W+E)
if self.initialvalue is not None:
self.entry.insert(0, self.initialvalue)
self.entry.select_range(0, END)
root.update_idletasks()
self.entry.focus_force()
return self.entry
def myaskstring(title, prompt, **kw):
d = My_QueryString(title, prompt, **kw)
root.update_idletasks()
answer = d.result
d.destroy()
return answer
root = Tk()
root.geometry("700x761")
label1 = Label(root, text="")
label1.place(x = 0, y = 0)
button2 = Button(root, text = "Doing Stocks", command=doing_stocks).place(x = 300, y = 340)
root.mainloop()
This is a simplified version of the code. I call my simpledialog popups like this:
myaskstring("Title", "Prompt", parent = root)
In the doing_stocks() method the first time I call myaskstring the window and the entry field will be in focus then all times after that it won't be. How can I make every simpledialog be in focus when it appears on the screen?

I agree is seems odd that the focus isn't set to each My_QueryString instance when it's initialized. Regardless of the reason why, a workaround for it not happening is to bind a <Map> event handler function to the dialog's Entry widget to shift keyboard focus to itself whenever it's made visible.
The code below shows an implementation doing that. It's based on your code with the important changes indicated with # ALL CAP comments to make them stand out.
import tkinter as tk
from tkinter import simpledialog
from tkinter.constants import *
def doing_stocks():
name = myaskstring("Input data", "Enter box number:", parent=root)
name2 = myaskstring("Input data2", "Enter box number2:", parent=root)
name3 = myaskstring("Input data3", "Enter box number3:", parent=root)
class My_QueryString(tk.simpledialog._QueryString):
def body(self, master):
self.bind('<KP_Enter>', self.ok)
self.bind('<Return>', self.ok)
w = tk.Label(master, text=self.prompt, justify=LEFT)
w.grid(row=0, padx=5, sticky=W)
self.entry = tk.Entry(master, name="entry")
self.entry.grid(row=1, padx=5, sticky=W+E)
self.entry.bind('<Map>', self.on_map) # <--- ADDED.
if self.initialvalue is not None:
self.entry.insert(0, self.initialvalue)
self.entry.select_range(0, END)
root.update_idletasks()
# self.entry.focus_force() # <--- NOT NEEDED.
return self.entry
# ADDED METHOD.
def on_map(self, event):
self.entry.focus_force()
def myaskstring(title, prompt, **kw):
d = My_QueryString(title, prompt, **kw)
root.update_idletasks()
answer = d.result
# d.destroy() # <--- NOT NEEDED.
return answer
root = tk.Tk()
root.geometry("700x761")
label1 = tk.Label(root, text="")
label1.place(x = 0, y = 0)
button2 = tk.Button(root, text = "Doing Stocks", command=doing_stocks)
button2.place(x = 300, y = 340)
root.mainloop()

Related

tkinter wait_variable doesn't return

i've a tkinter toplevel widget were you can type some text, and then press ok. it is created in a function: if you call that funtion the program should wait for a click on the button and than the function should return that text, so that my function have the same use a the input function in python 3. now my problem is: how can i pause my script until the user clicked on a button, so that all other files in my project pause (there happens nothing if the user clicks on a button in another tkinter window) are paused? i tried to use button.wait_variable(chosen) and when the user clicks on the button the variable "chosen" will change.
def choosefile():
root = tk.Tk()
root.geometry("275x50")
chosen = tk.BooleanVar()
chosen.set(False)
label = tk.Label(root, text="enter name of file (without extention)")
label.pack(fill="both")
entry = tk.Entry(root)
entry.pack(side="left", expand=True, fill="x")
button = tk.Button(root, text="Ok", command=lambda: [chosen.set(True), output(chosen.get())])
button.pack(side="right")
output("waiting")
button.wait_variable(chosen)
output("not waiting")
result = entry.get() + ".py"
root.quit()
return result
this works ok if you only run this file, but when i this run with main.py(below) the window don't destroy and also not return.
main.py
from tkinter import Tk, Button, Entry, StringVar
import tkinter.font as tkfont
from customtext import CustomText
from execute import Execute
from coloring import color
from files import create, openfile, choosefile
from alert import alert
import keyboard
class App(Tk):
def __init__(self):
super(App, self).__init__()
self.width = self.winfo_screenwidth() - 1
self.height = self.winfo_screenheight() - 1
self.attributes('-fullscreen', True)
self.state = StringVar()
self.state.set("saved")
self.input = CustomText(self)
self.input.place(x=10, y=10, width=self.width - 20, height=self.height - 20)
self.input.bind("<<TextModified>>", lambda a: [color(self.input), self.state.set("unsaved")])
self.run = Button(self, text="run", command=lambda: [create(self.name.get("1.0", "end-1c"), ".py", self.input.get("1.0", "end-1c")), Execute(self.name.get("1.0", "end-1c")), self.state.set("saved")])
self.run.place(x=self.width - 70, y=20, width=50, height=30)
self.exit = Button(self, text="exit", command=lambda: self.destroy())
self.exit.place(x=self.width - 130, y=20, width=50, height=30)
self.save = Button(self, text="save", command=lambda: [create(self.name.get("1.0", "end-1c"), ".py", self.input.get("1.0", "end-1c")), self.state.set("saved")])
self.save.place(x=self.width - 190, y=20, width=50, height=30)
self.open = Button(self, text="open", command=lambda: self.openfile())
self.open.place(x=self.width - 250, y=20, width=50, height=30)
self.name = CustomText(self, border=3, font=('Arial', 13))
self.name.place(x=self.width - 360, y=21, width=100, height=30)
def openfile(self):
if self.state.get() == "unsaved":
choise = alert("your code is unsaved, save now?")
if choise == "Ok":
create(self.name.get("1.0", "end-1c"), ".py", self.input.get("1.0", "end-1c"))
self.state.set("saved")
self.input.delete("1.0", "end")
self.input.insert("end-1c", openfile(choosefile())) # here choosefile is called.
if __name__ == "__main__":
app = App()
keyboard.add_hotkey("ctrl+enter", lambda: [keyboard.write("\b"), Execute(app.input.get("1.0", "end"))])
app.mainloop()
can someone help? thanks in advance!
EDIT:
i tried to implemant what acw1668 commented and it works! what i've done: in all my functions, i added a parameter master, so that i at any place in my project can use tk.Toplevel(master). now, since i only one instance of tk.Tk() have, it is working correctly.

How can i make a text entry submission be used to make another function?

So basically i want to make this GUI where you can insert text and then hit the go button ad it would do the feature. (example: "Stopwatch" and then after you hit enter it would start a stopwatch).
import tkinter as tk
import math
import time
root = tk.Tk()
root.geometry()
root.attributes("-fullscreen", True)
exit_button = tk.Button(root, text = "Exit", command = root.destroy)
exit_button.place(x=1506, y=0)
main_entry = root.Entry(root, )
root.mainloop()
So basically I want the answer in main entry to be taken and do the action asked in the text insertion.
Here i wrote an example on how to use the Entry and the Button widgets in Tkitner:
from tkinter import *
from tkinter.messagebox import showerror
import random
class App(Tk):
def __init__(self):
super(App, self).__init__()
self.title('Get a random integer')
self.geometry('400x250')
Label(self, text='Get a random number from A to B').pack(pady=20)
self.a_value = Entry(self, width=10)
self.a_value.insert(END, 'A')
self.a_value.pack(pady=5)
self.b_value = Entry(self, width=10)
self.b_value.insert(END, 'B')
self.b_value.pack(pady=5)
self.btn = Button(self, text='Get Random', command=self.get_rand)
self.btn.pack(pady=5)
self.out_label = Label(self)
self.out_label.pack(pady=10)
self.bind('<Return>', self.get_rand)
def get_rand(self, event=None):
a = self.a_value.get()
b = self.b_value.get()
try:
a = int(a)
b = int(b)
self.out_label.configure(text=f'{random.randint(a, b)}')
self.update()
except Exception as error:
showerror(title='ERROR', message=f'{error}')
if __name__ == '__main__':
myapp = App()
myapp.mainloop()

How do I use the The Tkinter Menu Widget?

I don't get how to add the The Tkinter Menu Widget with my code I tried the code on a website but it won't work. It think im just being silly but please help. Just for the record I have looked at other stack overflow solutions but they did't work.
import pyautogui
import time
import tkinter as tk
class Coordinates():
replayBtn = (100,350)
class YourGUI(tk.Tk):
def __init__(self):
# inherit tkinter's window methods
tk.Tk.__init__(self)
#Enter X field and label ⬇
tk.Label(self, text="ENTER X:").grid(row=0, column=3)
self.inputX = tk.Entry(self)
self.inputX.grid(row=0, column=1)
#Enter Y field and label ⬇
tk.Label(self, text="ENTER Y:").grid(row=0, column=0)
self.inputY = tk.Entry(self)
self.inputY.grid(row=0, column=4)
# Start Button ⬇
tk.Button(self, text="start", command=self.do_conversion).grid(row=3, column=0, columnspan=2)
# close button ⬇
tk.Button(self, text="exit!", command=self.EXITME).grid(row=4, column=0, columnspan=2)
def EXITME(self):
exit(0) # crashed prog so it closes
# strtoint("crashmE!")
def do_conversion(self):
y = self.inputY.get()
x = self.inputX.get()
running = True
try:
x = int(x)
y = int(y)
except:
print("Invalid point")
exit(0)
# strtoint("crashmE!")
while running:
pyautogui.click(x, y)
if __name__ == '__main__':
your_gui = YourGUI()
your_gui.title('Macro Clicker') # Set title
your_gui.iconbitmap('favicon.ico') # Set icon
your_gui.resizable(False, False)
your_gui.mainloop()
time.sleep(0)
Sorry to say that but if you aren't lazy u will find over thousend examples about tkinter menu.
Even your code has nothing to do with tkinter.menu!?
Here's an example of mine. The "Info" menu is empty just to show you how create additional menus.
from tkinter import Tk, Frame, Menu
class Gui(Frame):
def __init__(self, master):
self.master = master
Frame.__init__(self, self.master) #main container
self.grid()
self.create_menu()
def create_menu(self):
self.menubar = Menu(self.master)
self.theme = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="View", menu=self.theme)
self.menubar.add_command(label="Info", command=self.passing)
self.views = Menu(self.theme, tearoff=0)
self.theme.add_cascade(label="Themes", menu=self.views)
self.views.add_command(label="Default", command=self.passing)
self.views.add_command(label="Red", command=self.passing)
self.views.add_command(label="Blue", command=self.passing)
self.views.add_command(label="Random", command=self.passing)
self.master.configure(menu=self.menubar)
def passing(self):
print("Use your brain before asking questions on stackoverflow or use google if your brain is slow")
if __name__ == "__main__":
root = Tk()
root.geometry("300x200")
my_gui = Gui(root)
root.mainloop()

Increase tkSimpleDialog window size

How do i increase or define window size of tkSimpleDialog box ?
import Tkinter, tkSimpleDialog
root = Tkinter.Tk()
root.withdraw()
test = tkSimpleDialog.askstring("testing", "Enter your search string text")
print test
Make the second parameter as big as you like:
import Tkinter, tkSimpleDialog
root = Tkinter.Tk()
root.withdraw()
test = tkSimpleDialog.askstring("testing", "Enter your search string text in the space provided")
print test
Starting from: http://effbot.org/tkinterbook/tkinter-dialog-windows.htm
I did the following:
import Tkinter as tk, tkSimpleDialog
class MyDialog(tkSimpleDialog.Dialog):
def body(self, master):
self.geometry("800x600")
tk.Label(master, text="Enter your search string text:").grid(row=0)
self.e1 = tk.Entry(master)
self.e1.grid(row=0, column=1)
return self.e1 # initial focus
def apply(self):
first = self.e1.get()
self.result = first
root = tk.Tk()
root.withdraw()
test = MyDialog(root, "testing")
print test.result
If you want geometry and the label's text to be customizable, you will probably need to override __init__ (the version given in the link should be a good starting point).
If you want to wider box you can make buttons wider from the source code or override it inside your code:
def buttonbox(self):
'''add standard button box.
override if you do not want the standard buttons
'''
box = Frame(self)
w = Button(box, text="OK", width=100, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=100, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
tkSimpleDialog doesn't allows you to change it's geometry
In place use Tk() only
Here you have an exmple where you'll see the difference between two windows
import Tkinter, tkSimpleDialog
root = Tkinter.Tk()
root.geometry('240x850+200+100')
#root.withdraw()
test = tkSimpleDialog.askstring("testing", "Enter your search string text")
root.mainloop()
print test

Lift and raise a Canvas over a Canvas in tkinter

I'm creating a game, and I am using tkinter to build the GUI.
In this game, I want the user to start with a window, filled by a Canvas, with an image as background, and some buttons in some sort of item windows. And this Canvas is the home Canvas of the game.
The problem is that I want the user to click on a Button to land on other Canvas, who will host other things like the map or others buttons for others actions. Actually, I would like to superimpose several canvas (as in the Z-index method), and put a canvas on the top of the list, when I want it (if I click on a button for example).
I already searched and I had to change my mind several times, and now I really don't know how to do it.
I find the following code here on Stack Overflow, but it is coded for Python 2 (I think), and I'm starting coding in Python 3, so I am not able to translate it to Python 3 and solve my problem.
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.frame = tk.Frame(self)
self.frame.pack(side="top", fill="both", expand=True)
self.label = tk.Label(self, text="Hello, world")
button1 = tk.Button(self, text="Click to hide label",
command=self.hide_label)
button2 = tk.Button(self, text="Click to show label",
command=self.show_label)
self.label.pack(in_=self.frame)
button1.pack(in_=self.frame)
button2.pack(in_=self.frame)
def show_label(self, event=None):
self.label.lift(self.frame)
def hide_label(self, event=None):
self.label.lower(self.frame)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
My code using grid :
from tkinter import *
fenetre = Tk()
fenetre.title(my game)
# acceuil
# variable acceuil
largeur = 700
hauteur = 430
BG_acceuil = PhotoImage(file="BG_acceuil.gif")
acceuil = Canvas(fenetre, width=largeur, height=hauteur)
acceuil.create_image(0, 0, anchor=NW, image=BG_acceuil)
acceuil.grid(row=0)
acceuil.pack()
# fond
fond = PhotoImage(file="BG_acceuil.gif")
acceuil2 = Canvas(fenetre, width=largeur, height=hauteur)
acceuil2.create_image(0, 0, anchor=NW, image=fond)
acceuil2.pack()
# variable bt_jouer
x0 = 80
y0 = 230
class hide_me():
def hide_me(event, widget, pos):
widget.grid_forget()
def show_me(event, widget, pos):
widget.grid(row=pos)
# Boutton jouer
BT_jouer = Button(acceuil, text="Jouer", command=hide_me())
BT_jouer.configure(width=10, activebackground="#33B5E5", relief=GROOVE)
BT_jouer_window = acceuil.create_window(x0, y0, window=BT_jouer,)
BT_jouer.bind('<Button-1>', lambda event: hide_me(event, BT_jouer, 1))
BT_jouer.grid(row=1)
# Bouton règle
BT_regle = Button(acceuil2, text="Règles", command=fenetre.destroy)
BT_regle.configure(width=10, activebackground="#33B5E5", relief=FLAT, bd=0)
BT_regle_window = acceuil2.create_window(x0, y0 + 50, window=BT_regle)
# Boutton quitter
BT_quit = Button(acceuil, text="Quitter", command=fenetre.destroy)
BT_quit.configure(width=10, activebackground="#33B5E5", relief=FLAT)
BT_quit_window = acceuil.create_window(x0, y0 + 100, window=BT_quit)
fenetre.mainloop()
The answer is very easy: To convert to Python3, change Tkinter to tkinter, and it works!
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.frame = tk.Frame(self)
self.frame.pack(side="top", fill="both", expand=True)
self.label = tk.Label(self, text="Hello, world")
button1 = tk.Button(self, text="Click to hide label",
command=self.hide_label)
button2 = tk.Button(self, text="Click to show label",
command=self.show_label)
self.label.pack(in_=self.frame)
button1.pack(in_=self.frame)
button2.pack(in_=self.frame)
def show_label(self, event=None):
self.label.lift(self.frame)
def hide_label(self, event=None):
self.label.lower(self.frame)
def main():
app = SampleApp()
app.mainloop()
return 0
if __name__ == '__main__':
main()
Note: You are not really hiding the label - it still occupies space on the canvas. The following code, from this entry, really removes the item. It can then be recalled with a pack() call:
from Tkinter import *
def hide_me(event):
event.widget.pack_forget()
root = Tk()
btn=Button(root, text="Click")
btn.bind('<Button-1>', hide_me)
btn.pack()
btn2=Button(root, text="Click too")
btn2.bind('<Button-1>', hide_me)
btn2.pack()
root.mainloop()
I did some testing, and made an equivalent program to yours... The only problem is that the unhidden widget is always packed at the end:
from tkinter import *
def hide_me(event, widget):
widget.pack_forget()
def show_me(event, widget):
widget.pack()
root = Tk()
lbl = Label(root, text="Victim")
btn = Button(root, text="Hide the victim")
btn.bind('<Button-1>', lambda event: hide_me(event, lbl))
btn.pack()
btn2 = Button(root, text="Show the victim")
btn2.bind('<Button-1>', lambda event: show_me(event, lbl))
btn2.pack()
lbl.pack()
root.mainloop()
A better version uses the grid() packer. Here you can actually restore the 'forgotten' widget to its original position. Only slightly more complicated :)
from tkinter import *
def hide_me(event, widget, pos):
widget.grid_forget()
def show_me(event, widget, pos):
widget.grid(row = pos)
root = Tk()
lbl = Label(root, text="Victim")
lbl.grid(row = 0)
btn = Button(root, text="Hide the victim")
btn.bind('<Button-1>', lambda event: hide_me(event, lbl, 0))
btn.grid(row = 1)
btn2 = Button(root, text="Show the victim")
btn2.bind('<Button-1>', lambda event: show_me(event, lbl, 0))
btn2.grid(row = 2)
root.mainloop()
EDIT: Another observation from the comments: Bryan Oakley commented that if you use .grid_remove() instead of .grid_forget(), then the coordinates will not be lost, and a simple .grid() will restore the widget at its location.

Categories