Tkinter focus_set() does not work on text widget - python

I am using Tkinter with python for a simple UI where I have a text widget and a button. When the button is pressed I want the cursor focus to be set on the text widget, using focus_set() method.
I don't understand why the focus_set() is not working in my code. I think it might be because my text widget is within a frame (frame1) and I cannot properly access this widget in my startPaus() method. Any ideas how I can fix the problem??
class TypingField(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.submit_tog = True
self.initUI()
def initUI(self):
self.parent.title("Text Field")
self.pack(fill = BOTH, expand=True)
frame1 = Frame(self, width = 50, height =25)
frame1.pack(fill = X, expand=True)
self.scroll = Scrollbar(frame1)
self.scroll.pack(side = "right", fill = Y)
self.text = Text(frame1)
self.text.pack(fill=Y)
self.scroll.config(command=self.text.yview)
self.text.config(yscrollcommand=self.scroll.set)
frame2 = Frame(self)
frame2.pack(fill=X, expand=True)
self.submit = Button(frame2,text="Start Test")
self.submit.bind("<Button-1>", self.startPause)
self.submit.pack(fill=X)
def startPause(self, event):
if self.submit_tog:
self.submit.configure(text = "Pause")
self.text.focus_set()
else:
self.submit.configure(text = "Start Test")
self.submit_tog = not self.submit_tog

The following works on my machine. Note that the focus is always on the Text widget for the code you posted, enter something to see this, because the focus is never set elsewhere, but in the code below it alternates between the Text widget and the Button to illustrate.
class TypingField():
def __init__(self, parent):
self.parent = parent
self.submit_tog = True
self.initUI()
def initUI(self):
self.parent.title("Text Field")
frame1 = Frame(self.parent, width = 50, height =25)
frame1.pack(fill = X, expand=True)
self.scroll = Scrollbar(frame1)
self.scroll.pack(side = "right", fill = Y)
self.text = Text(frame1)
self.text.pack(fill=Y)
self.scroll.config(command=self.text.yview)
self.text.config(yscrollcommand=self.scroll.set)
frame2 = Frame(self.parent)
frame2.pack(fill=X, expand=True)
self.submit = Button(frame2,text="Start Test")
self.submit.bind("<Button-1>", self.startPause)
self.submit.pack(fill=X)
def startPause(self, event):
if self.submit_tog:
self.submit.configure(text = "Text Focus")
self.text.focus_set()
else:
self.submit.configure(text = "Button Focus")
self.submit.focus_set()
self.submit_tog = not self.submit_tog
root=Tk()
TypingField(root)
root.mainloop()

Related

Frame switching does not work normally in Tkinter

Everyone
Startpage works fine.
However, I changed the frame to Pageone using change_img().
Pageone did not appear on the screen.
I enlarged the entire screen by clicking the square on the top right of the printed frame.
Pageone appears at the bottom.
I don't know why Pageone's text and buttons don't appear at the top of the frame
Please help.
import sys
import serial
import threading
import queue
import tkinter as tk
#import tkinter
import tkinter.ttk as ttk
import time
import tkinter.messagebox
from PIL import ImageTk, Image
class StartApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.switch_frame(None, StartPage)
self.geometry("1500x950+100+100")
self.resizable(width=True, height=True)
def switch_frame(self, Canvas, frame_class):
if Canvas is not None:
Canvas.delete("all")
new_frame = frame_class(self)
if self._frame is not None:
print( '{} destory'.format(self._frame))
self._frame.destroy()
self._frame = new_frame
print(new_frame)
self._frame.pack()
class StartPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.no= 10
self.is_charged= False
#self.img = ImageTk.PhotoImage(Image.open("images/10_image.png"))
#n can't be zero, recommend 0.25-4
n = 3
self.image = Image.open("images/10_image.png")
[imageSizeWidth, imageSizeHeight] = self.image.size
newImageSizeWidth = int(imageSizeWidth * n)
if 1:
newImageSizeHeight = int(imageSizeHeight*n)
else:
newImageSizeHeight = int(imageSizeHeight/n)
self.image = self.image.resize((newImageSizeWidth, newImageSizeHeight), Image.ANTIALIAS)
self.img = ImageTk.PhotoImage(self.image)
self.Canvas1 = tk.Canvas(width = 1500, height = 950)
#self.Canvas1.config(bg="blue")
self.Canvas1.pack()
self.Canvas1.create_image(1500/2,950/2,image = self.img)
# Canvas1.config(bg="blue",width = newImageSizeWidth, height = newImageSizeHeight)
#Canvas1.config(bg="blue")
#Canvas1.pack(side="left", anchor="ne")
# images
self.my_images = []
self.my_images.append(tk.PhotoImage(file = "images/10_image.png"))
self.my_images.append(tk.PhotoImage(file = "images/11_image.png"))
self.my_images.append(tk.PhotoImage(file = "images/12_image.png"))
self.my_image_number = 0
# set first image on canvas
self.image_on_canvas = self.Canvas1.create_image(0, 0, anchor = "nw", image = self.my_images[self.my_image_number])
self.after(500, self.change_img)
def change_img(self):
# next image
self.my_image_number += 1
# return to first image
if self.my_image_number == len(self.my_images):
self.my_image_number = 0
self.master.switch_frame(self.Canvas1, PageOne)
# change image
else:
self.Canvas1.itemconfig(self.image_on_canvas, image = self.my_images[self.my_image_number])
self.after(500, self.change_img)
class PageOne(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page one").pack(side="top")
tk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(None, StartPage)).pack()
class PageTwo(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
tk.Label(self, text="This is page two").pac`enter code here`k(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(None, StartPage)).pack()
if __name__ == "__main__":
root= StartApp()
root.mainloop()
It is because self.Canvas1 is not child of StartPage, it is child of root (StartApp) window. So even StartPage is destroyed, self.Canvas1 is still there.
Change the following line in StartPage.__init__():
self.Canvas1 = tk.Canvas(width = 1500, height = 950) # child of `root`
to
self.Canvas1 = tk.Canvas(self, width = 1500, height = 950) # child of `StartPage`

solution communication between two windows

I want to have two tkinter windows. A button should be in the first window, and a reaction text should be in the second window.
My questions:
Must the second window have no modal?
How do I make the second window movable?
How can I give information to second window via callback function?
Thanks in advance for answers and advice!
Here is some code that may help you:
from tkinter import *
class App:
def __init__(self):
self.window1 = Tk()
self.window2 = Toplevel()
self.button = Button(self.window1, bd = 5, text = "Click Me!", command = self.update)
self.button.pack()
self.label = Label(self.window2, bd = 5, text = "Button has not been clicked.")
self.label.pack()
def update(self):
self.label.config(text = "Button has been clicked!")
self.window2.update()
app = App()
Explanation:
The first line imports tkinter
In the next line, we create a class. At the bottom of the code, we create an object using that class. This is useful because when the object is created, the functions in the class are already defined, so the function definition can be after when it is called.
After we declare our class, in __init__, we write code that will run when an object is created from that class. The code creates two windows. One contains a button, and the other one contains a label. The button has a command parameter to run the class function, update.
In update, we change the label text and update the window.
I have not next questions. My problems solution is here:
import tkinter as tk
class ViewOnMoon(tk.Toplevel):
def __init__(self, parent = None, draw = None):
tk.Toplevel.__init__(self, parent)
self.transient(parent)
self.title('View')
self.minsize(height = 300, width = 300)
fr_canvas = tk.Frame(self)
fr_canvas.place(relx=0.23, rely=0.01, anchor="nw")
self.canv_w = 200
self.canv_h = 200
self.canvas = tk.Canvas(fr_canvas, bg='white', width = self.canv_w, height=self.canv_h)
self.canvas.grid(column = 0, row = 0)
return
class GuiMoonMove(tk.Frame):
def __init__(self, master):
mon_h = 600
mon_w = 1250
tk.Frame.__init__(self, master)
self.frame = tk.Frame(master, width=1000, height=200, bd=2)
self.master.title('Move')
self.master.minsize(height = mon_h, width = mon_w)
fr_canvas = tk.Frame(self.master)
fr_canvas.place(relx=0.23, rely=0.01, anchor="nw")
fr_button = tk.Frame(self.master)
fr_button.place(relx=0.02, rely=0.06, anchor="nw")
self.canv_h = 600
self.canv_w = 950
self.lbl_view = tk.BooleanVar()
chb_view_on_moon = tk.Checkbutton(fr_button, text="Pohled na Měsíc", variable = self.lbl_view, \
onvalue=True, offvalue=False,command = self.callback)
chb_view_on_moon.grid(column= 0, row= 4,pady = 10)
self.canvas = tk.Canvas(fr_canvas, bg='white', width = self.canv_w, height=self.canv_h)
self.canvas.grid(column = 0, row = 0)
def callback(self,*args):
if self.lbl_view.get()==True:
self.view_on_moon = ViewOnMoon(parent = self.master)
else:
self.vom.destroy()
if __name__=="__main__":
root = tk.Tk()
app = GuiMoonMove(master = root)
app.mainloop()

Python-Tkinter Place button on left of frame

How do I place the QUIT button in below code to the extreme right of the Frame?
I tried several things like:
padx
and
self.pack(side="top", anchor="e")
but after trying some 15 times both buttons are coming close to each other. Maybe Some help from anyone would be really appreciated. I need one button on extreme right and other on extreme left
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(anchor='e')
self.pack(side="top", anchor="w")
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
You have several problems here.
First, you're using the wrong geometry manager. The pack geometry manager, as the name implies, packs the widgets as close together as possible. That's not what you want. The grid geometry manager lets you put the widgets into a table-like layout with rows and columns. If you put the Browse button into the first column and the Quit button into the last column, you'll be a step closer.
Second, your Application window contains three child widgets and you're only putting two of them into a geometry manager. How that is going to mess you up I don't even want to think about. So I put the label into column 1, the Quit button into column 2, and the Browse button into column 0. The Quit button I gave a "sticky" value of "e" so it will be attached to the east (right) side of its allocated space.
Third, all the geometry managers try to compact the widgets as much as possible unless you specifically tell it to do otherwise. I told the grid manager to expand column 2 so that the extra space gets assigned to the cell that holds the Quit button.
Fourth, you need to tell the pack manager to expand the top widget so that it spans the entire window. The directive for that is fill="x".
Fifth, you have a redundant call to the pack manager at the end of your createWidgets function.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack(fill="x")
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.Label.grid(row=0, column=1)
self.Run_Main.grid(row=0, column=0, sticky="w")
self.QUIT.grid(row=0, column=2, sticky="e")
self.columnconfigure(2, weight=1)
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
These link, link helped. The other option would be to use tkinter's grid manager, it will be more intuitive and keep you more organized in the future.
import tkinter as tk
from tkinter.ttk import *
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse.."
# self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.Sayhello
self.Run_Main.pack(side='left')
self.Label = tk.Label(self)
self.Label["text"] = 'Processing...'
self.Label.pack(side='left')
self.progressbar = Progressbar(mode="indeterminate", maximum=20)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right')
self.pack(side="top", fill=tk.BOTH) # changes here
def Sayhello(self):
print("Hello")
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=18, width=60)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
return
root = tk.Tk()
root.resizable(width=False, height=False)
root.option_add('*font', ('verdana', 9, 'bold'))
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
There are two simple fixes you can make in order to get the behavior you want.
First, you need to pack Application so that it fills the window:
class Application(...):
def __init__(...):
...
self.pack(fill="x")
Next, simply pack the quick button on the right side of the window:
self.QUIT.pack(side="right", anchor='e')
Even though the above is all you need to do in this specific example, there are additional things you can do to make your job much easier.
I would recommend creating a frame specifically for the buttons. You can pack it at the top. Then, put the buttons inside this frame, and pack them either on the left or right. You'll get the same results, but you'll find it easier to add additional buttons later.
I also find that it makes the code much easier to read, write, maintain, and visualize when you separate widget creation from widget layout.
class Application(...):
...
def createWidgets(self):
toolbar = tk.Frame(self)
toolbar.pack(side="top", fill="x")
self.Run_Main = tk.Button(toolbar)
self.Label = tk.Label(toolbar)
self.QUIT = tk.Button(toolbar)
...
self.Run_Main.pack(side="left")
self.Label.pack(side="left", fill="x")
self.QUIT.pack(side="right")
...

How to make Tkinter button to be placed in particular position?

I am new to python so I was trying to make a GUI, in that I have to place a button in a particular position.
I tried using self.nxt_form.place(x=200,y=100) instead of self.nxt_form.pack().
But the button disappeared and only the frame appeared when it ran. Can you tell me how to place the button in a particular position?
Here is the code:
import tkinter as tk
class Main_form:
def __init__(self, root,title="Simulated MTBF"):
self.root = root
self.frame = tk.Frame(self.root)
"""Button nxt_form which moves to next form"""
self.nxt_form = tk.Button(self.frame, text = 'Next Form', width = 25,command = self.new_window)
self.nxt_form.pack()
self.frame.pack()
"""command to open new window by clicking Button """
def new_window(self):
self.newWindow = tk.Toplevel(self.root)
self.app = Demo2(self.newWindow)
class Demo2:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(self.root)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.root.destroy()
def main():
root = tk.Tk()
app = Main_form(root)
root.mainloop()
if __name__ == '__main__':
main()
when i am using tkinter i used column and row to position objects
self.btn = tk.Button(self, text = "button")
self.btn.grid(row = 1, column = 1)
EDIT - expanded on information in response to comment (below)
I would make an label and change its width and height to make the spacing you need (note im a beginer at python as well so this is probly a bad way but it works)
from tkinter import *
import tkinter as tk
from tkinter.ttk import Combobox,Treeview,Scrollbar
class MainMenu(Frame):
def __init__(self, master):
""" Initialize the frame. """
super(MainMenu, self).__init__(master)
self.grid()
self.create_GUI()
def create_GUI(self):
frame1 = tk.LabelFrame(self, text="frame1", width=300, height=130, bd=5)
frame1.grid(row=0, column=0, columnspan=3, padx=8)
#the frame is not needed but it is a good thing to use as can group
#parts of your interface together
self.text1 = Entry(frame1)
#note if you were not using frames would just put self here
self.text1.grid(row = 1, column = 0)
self.text2 = Label(frame1, text = "",height = 10)
self.text2.grid(row = 2 , column = 0)
self.text3 = Entry(frame1)
self.text3.grid(row = 3, column = 0)
root = Tk()
root.title("hi")
root.geometry("500x500")
root.configure(bg="white")
app = MainMenu(root)
root.mainloop()
Also note that you can not use pack and grid together what you could do is group your objects in different frames then use grid in one frame and pack in a different frame. I personally prefer to use grid to pack as it gives you more control over your object then pack does

How to change a entry widgets border color in Python Tkinter

I am working on a program that has a entry widget. And when the user clicks a button and that entry widget is empty then the program will change the border color if it to red. But when I try the border just stays the same color. Which is black.
Here is the code:
self.timeField = Entry(self.mfr, width=40, relief=SOLID, highlightbackground="red", highlightcolor="red")
self.timeField.grid(row=0, column=1, sticky=W)
Then in the if statement that checks if it is empty has this to change it to red but it does not seem to work:
self.timeField.config(highlightbackground="red")
self.timeField.config(highlightcolor="red")
Can someone explain to me why this is not working, what I am doing wrong, and a way to fix it? Thanks in advance.
Update:
Here is the rest of the code as requested:
def start(self):
waitTime = self.timeField.get()
password = self.passField.get()
cTime = str(self.tVers.get())
self.cTime = cTime
if waitTime.strip() != "":
if password.strip() != "":
if waitTime.isdigit():
if self.cTime == "Secs":
waitTime = int(waitTime)
elif self.timeVer == "Mins":
waitTime = int(waitTime) * 60
else:
waitTime = int(waitTime) * 3600
self.password = password
root.withdraw()
time.sleep(float(waitTime))
root.deiconify()
root.overrideredirect(True)
root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
self.tfr.destroy()
self.mfr.destroy()
self.bfr.destroy()
self.create_lockScreen()
else:
self.timeField.configure(highlightcolor="red")
else:
self.passFields.configure(highlightcolor="red")
else:
self.timeField.config(highlightbackground="red", highlightcolor="red")
Altough it is an old question i stumbled upon the same problem on windows 10 and there is meanwhile a fairly simple solution.
In addition to setting the highlightbackground and color you have to change the highlightthickness to something greater zero.
Bryan Oekley mentioned it in the heading of his answer but i couldnt find it in his code so here is a little code snippet.
self.entry = tk.Entry(self, highlightthickness=2)
self.entry.configure(highlightbackground="red", highlightcolor="red")
(this should maybe be a comment on Bryans Answer)
Addressing the issue with highlightthickness
The code you've given should work, though you may be bumping up against platform implementation (ie: windows may not treat the highlightthickness the same as other platforms)
Here's a program that should work, though I haven't tested it on windows 7:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Validate", command=self.validate)
self.entry.pack(side="top", fill="x")
self.button.pack(side="bottom")
self.validate() # initialize the border
def validate(self):
data = self.entry.get()
if len(data) == 0:
self.entry.configure(highlightbackground="red", highlightcolor="red")
else:
self.entry.configure(highlightbackground="blue", highlightcolor="blue")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Using a frame to simulate a border
Another solution is to create a border with a frame that is just slightly larger than the button. Here's a quick example. Its not really production ready, but it illustrates the point:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.entry = CustomEntry(self)
self.button = tk.Button(self, text="Validate", command=self.validate)
self.entry.pack(side="top", fill="x")
self.button.pack(side="bottom")
self.validate() # initialize the border
def validate(self):
data = self.entry.get()
if len(data) == 0:
self.entry.set_border_color("red")
else:
self.entry.set_border_color("blue")
class CustomEntry(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self, *args, **kwargs)
self.entry.pack(fill="both", expand=2, padx=2, pady=2)
self.get = self.entry.get
self.insert = self.entry.insert
def set_border_color(self, color):
self.configure(background=color)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

Categories