excuse the greeness. Trying to build GUI where option selected from Combobox populates text box. Nothing happening. First time programming so appreciate i have made a lot of errors here.
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
# function to display course selected
def courseDisplay():
box = course.get()
print(box)
# Create instance
win = tk.Tk()
win.resizable(130,130)
win.title("RaceCourse GUI")
# create combobox
course = tk.StringVar()
courseChosen = ttk.Combobox(win,width=60,textvariable=course,state='readonly')
courseChosen['values'] = ("Choose a course","Ascot", "Bath", "Chester")
courseChosen.grid(column=5, row=1,rowspan = 3, columnspan = 3,padx = 300, pady = 40)
courseChosen.current(0)
courseChosen.bind("<<ComboboxSelected>>", courseDisplay)
# create scrolled Text control
scrolW = 46
scrolH = 10
box = scrolledtext.ScrolledText(win, width=scrolW, height=scrolH, wrap=tk.WORD)
box.grid(column=5, row=8, columnspan=3,padx = 300,pady = 10)
# Start GUI
win.mainloop()
Since function courseDisplay is called when some event occurs on combobox (namely, when some option is selected), it should accept one variable (usually called event). So, your function should look like this:
def courseDisplay(event=None):
box = course.get()
print(box)
Of course, You should add another logic for showing test in textbox instead of print.
Related
Good days all, I am new here, and I have stuck the problem a few days a problem currently with Tkinter, I have done some research about how to close Tkinter window when the mouse has clicked away from it but there are not much information to do so.
So, my problem is how to close the Tkinter window when the mouse clicked outside the Tkinter? I have tried the method of FocusOut to my Tkinter. However, I have tried to bind with root, it will close the window even though I clicked inside the frame widget. Then, I bind with the frame, the Tkinter will close when I clicked outside the Tkinter. Therefore, I have proved that the idea to close the Tkinter is works so far.
Then a new problem has happened, when I clicked the Combobox widget in the window, the window will close also. Is there any better solution to prove this concept?
Here is the code to indicate my problem.
import tkinter as tk
from tkinter import StringVar, ttk,messagebox
root = tk.Tk()
root.title("Sample Window")
root.minsize(300,350)
info_frame = tk.LabelFrame(root, text = "Information")
info_frame.pack(padx = 5, pady = 5 , fill = "both",expand=True)
tabControl = ttk.Notebook(info_frame)
person1tab = ttk.Frame(tabControl)
tabControl.add(person1tab,text = "Person1")
tabControl.pack(expand=1,fill="both")
person2tab = ttk.Frame(tabControl)
tabControl.add(person2tab,text = "Person2")
tabControl.pack(expand=1,fill="both")
fname_var = tk.StringVar()
lname_var = tk.StringVar()
gender_var = tk.StringVar()
age_var = tk.IntVar()
fname_label = tk.Label(person1tab, text = "First name:").pack(padx=5,pady=3)
fname_entry = tk.Entry(person1tab, textvariable=fname_var).pack(padx=5,pady=3)
lname_label = tk.Label(person1tab, text = "Last name:").pack(padx=5,pady=3)
lname_entry = tk.Entry(person1tab, textvariable=lname_var).pack(padx=5,pady=3)
gender_label = tk.Label(person1tab, text = "Gender:").pack(padx=5,pady=3)
gender_combo = ttk.Combobox(person1tab, textvariable=gender_var,state='readonly')
gender_combo['values'] = ('Male','Female')
gender_combo.current(0)
gender_combo.pack(padx=5,pady=3)
age_label = tk.Label(person1tab, text = "Age:").pack(padx=5,pady=3)
age_label = tk.Entry(person1tab, textvariable=age_var).pack(padx=5,pady=3)
page2label = tk.Label(person2tab,text = "This is tab 2.").pack(padx=5,pady=3)
def lossfocus(event):
root.quit()
pass
tabControl.bind('<FocusOut>', lossfocus)
root.mainloop()
You can still bind <FocusOut> on root window, but you need to check:
whether the widget that trigger this event is root window
no other widget in this root window getting the focus:
def lossfocus(event):
if event.widget is root:
# check which widget getting the focus
w = root.tk.call('focus')
if not w:
# not widget in this window
root.destroy()
I have a small script which is organized in 3 frames:
1 in the first row
1 in the second row Left
1 in the second row right
I press the button in the first row frame and hand over the input value to the Label in the second row in the left.
Here my code:
import tkinter as tk
# Create Window
root = tk.Tk()
# Define String Variable
Name = tk.StringVar()
# Organize root window in 3 frames
EntryFrame = tk.Frame(root)
MainLeftFrame = tk.Frame(root)
MainRightFrame = tk.Frame(root)
# Create Buttons, Entry and Labels
NameLabel = tk.Label(MainLeftFrame, textvariable=Name)
InputName = tk.Entry(EntryFrame, width=20,bg='yellow')
SubmitButton = tk.Button(EntryFrame, text='Submit', command=lambda:action())
# Define what happens when press button reset
def reset():
MainLeftFrame.forget()
MainRightFrame.forget()
EntryFrame.pack()
# Define what happens when button is pressed
def action():
Name.set(InputName.get())
ResetButton = tk.Button(MainRightFrame, text='Reset', command=lambda: reset())
ResetButton.pack()
Placeholder = tk.Label(MainRightFrame, text="place holder")
Placeholder.pack(side="top")
EntryFrame.forget()
# Pack Widgets
EntryFrame.pack(side='top')
MainLeftFrame.pack(side='left')
MainRightFrame.pack(side='right')
InputName.pack()
SubmitButton.pack()
NameLabel.pack()
#mainloop
root.mainloop()
Now to my question:
When I press the "Submit" Button for the Second time (after pressing Reset Button) nothing is happening :(
Thanks in advance!
The reason of your program not working is that, after using forget on the MainLeftFrame and MainRightFrame you aren't packing them again when the action function is called. Adding these 2 lines of code in action function should make it work. BUT
MainLeftFrame.pack()
MainRightFrame.pack()
That's not the only issue, defining new widgets every time the function is called and packing them will additively increase the same widget set over and over again. To avoid this, you would have to predefine them and them perform forget and repacking. But a better thing to do would be to have a dedicated frame for them, so that it becomes easy for you to toggle. I have tried rewriting your script, let me know if this is what you wanted.
from tkinter import *
def reset():
entry_frame.pack()
main_frame.pack_forget()
def submit():
entry_frame.pack_forget()
main_frame.pack()
name.set(name_entry.get())
root=Tk()
entry_frame=Frame(root)
entry_frame.pack()
name_entry=Entry(entry_frame)
name_entry.pack(side='top')
submit_button=Button(entry_frame,text='Submit',command=submit)
submit_button.pack(side='top')
main_frame=Frame(root)
reset_button=Button(main_frame,text='Reset',command=reset)
reset_button.pack(side='top')
name=StringVar()
name_label=Label(main_frame,textvariable=name)
name_label.pack(side='left')
placeholer_label=Label(main_frame,text='placeholer')
placeholer_label.pack(side='right')
root.mainloop()
I have three widgets (shown below) - a label, an entry, and a button.
I'm hoping to accept user input of an integer when the button is clicked.
I get the following error: ERROR RESOLVED!
I was making this too complicated. Success is Mine!!!
I believe that my problem is related to this:
Stack Overflow Post on get()
I chopped it down to the minimum amount of code possible to achieve this before posting.
Any Ideas or obvious failures you see would be greatly appreciated.
Screenshot of screen #1 (should eventually progress to screen #2 after all the players are entered. Screen #2 is where the game will begin.):
Working Code:
from tkinter import *
import tkinter
from PIL import Image, ImageTk
# The next 2 imports override the basic Tk widgets
# widget options that use fg and bg will no longer work.
# Must use, instead, ttk.Style class
from tkinter import ttk
from tkinter.ttk import *
# See docs.python.org/8/library/tkinter.ttk.html
# Class Based Windows
# youtube.com/watch?v=RkaekNkIKNY
# Tkinter - GUI Eaxample Multiple Display Frames
# youtube.com/watch?v=KdoOm3xo8X0
def main():
root = tkinter.Tk()
window1 = Window(root, "Play_a_game_of_Pig", "969x690", "someMessage",
"pig.ico", 0)
return None
class Window:
number_of_players = 0
# int player_names[] = new player_names[number_of_players]
def __init__(self, root, title, geometry, message, iconbitmap,
playerNumber):
self.root = root # this is the instance variable for the entire window
self.root.title(title)
self.root.geometry(geometry)
self.root.iconbitmap(iconbitmap)
self.number_of_players = playerNumber
# Make the window show up in center of screen and not be ccovered up by anything else.
# self.root.eval('tk::PlaceWindow %s center' % self.root.wininfo_toplevel())
# this is an important line.
# self.root.mainloop()
def addPlayers():
"""Allows user to input the number of and names of all game players"""
# stackoverflow.com/questions/12169258/should-i-use-entrys-get-or-its-textvariables-for-tkinter-in-python
print("\n initial # of players = " + str(self.number_of_players))
# Collects user input from the entry and turns it into an int
# user_input_number_of_players.set(int(str(entry_player_number.get("1.0", 'end-1c'))))
try:
user_input_number_of_players = int(entry_player_number.get())
print("Inside try block, user_input = ")
print(user_input_number_of_players)
self.number_of_players = user_input_number_of_players
except ValueError:
tkinter.messagebox.showerror('Non-Integer Input', 'User MUST enter a player # greater than 1.', icon = 'error')
# tkinter.messagebox.deiconify()
# tkinter.messagebox.quit()
# tkinter.messagebox.destroy()
#user_input_number_of_players.set(int(str(entry_player_number.get("1.0", 'end-1c'))))
# Set class instance value to this input from the user
# self.number_of_players = user_input_number_of_players
print("# of players after click = " + str(self.number_of_players))
return self.number_of_players
print("# of players after click = " + str(self.number_of_players))
# Add a label
myLabel1 = tkinter.Label(self.root, text="Please Enter # of Players",
width=25)
myLabel1.config(font="Courier 14 bold")
myLabel1.grid(row=2, column=1)
# bind user input to a variable from the entry box.
# Specifies a name whose value is linked to the widget value.
user_input_number_of_players = tkinter.StringVar()
# add an entry box
entry_player_number = tkinter.Entry(self.root, width=5, borderwidth=5,
textvariable=user_input_number_of_players)
# number.set(int("0"))
entry_player_number.grid(row=2, column=3, rowspan=2)
# specify a default value inside the entry box
# entry_player_number.insert(0,int("2"))
# Add a button for adding players to the game
addPlayerButton = tkinter.ttk.Button(self.root,
text="Enter",
command=addPlayers)
addPlayerButton.grid(row=2, column=4)
self.root.mainloop()
pass
pass
main()
In Summary, I first made two screens. I'm hoping to make these into classes. The first screen (pictured) should get input from the user. Then, the first screen should progress to the second screen.
To do this, I have used the following resources:
Resources for merging this code together into Multiple class based windows and the backend:
Python: Tkinter Class Based Windows
Tkinter - GUI Example Multiple Display Frames
Here is the include file
import tkinter as tk
from tkinter import ttk
class CollapsiblePane(ttk.Frame):
"""
-----USAGE-----
collapsiblePane = CollapsiblePane(parent,
expanded_text =[string],
collapsed_text =[string])
collapsiblePane.pack()
button = Button(collapsiblePane.frame).pack()
"""
def __init__(self, parent, expanded_text ="Collapse <<",
collapsed_text ="Expand >>"):
ttk.Frame.__init__(self, parent)
# These are the class variable
# see a underscore in expanded_text and _collapsed_text
# this means these are private to class
self.parent = parent
self._expanded_text = expanded_text
self._collapsed_text = collapsed_text
# Here weight implies that it can grow it's
# size if extra space is available
# default weight is 0
self.columnconfigure(1, weight = 1)
# Tkinter variable storing integer value
self._variable = tk.IntVar()
# Checkbutton is created but will behave as Button
# cause in style, Button is passed
# main reason to do this is Button do not support
# variable option but checkbutton do
self._button.config( width=1, height=1, borderwidth = 0)
self._button.grid(row = 0, column = 0)
# This wil create a seperator
# A separator is a line, we can also set thickness
self._separator = ttk.Separator(self, orient ="horizontal")
self._separator.grid(row = 0, column = 1, sticky ="we")
self.frame = ttk.Frame(self)
# This will call activate function of class
self._activate()
def toggle(self):
"""Switches the label frame to the opposite state."""
self._variable = not self._variable
def _activate(self):
if not self._variable:
# As soon as button is pressed it removes this widget
# but is not destroyed means can be displayed again
self.frame.grid_forget()
# This will change the text of the checkbutton
self.toggle()
elif self._variable:
# increasing the frame area so new widgets
# could reside in this container
self.frame.grid(row = 1, column = 0, columnspan = 1)
self.toggle()
Here is the program that utilizes it.
# Importing tkinter and ttk modules
from tkinter import *
from tkinter.ttk import *
# Importing Collapsible Pane class that we have
# created in separate file
from collapsiblepane import CollapsiblePane as cp
# Making root window or parent window
root = Tk()
root.geometry('200x200')
# Creating Object of Collapsible Pane Container
# If we do not pass these strings in
# parameter the the defalt strings will appear
# on button that were, expand >>, collapse <<
cpane = cp(root, 'Expanded', 'Collapsed')
cpane.grid(row = 0, column = 0)
# Button and checkbutton, these will
# appear in collapsible pane container
b1 = Button(cpane.frame, text ="GFG").grid(
row = 1, column = 2, pady = 10)
cb1 = Checkbutton(cpane.frame, text ="GFG").grid(
row = 2, column = 3, pady = 10)
mainloop()
And here's my error.
TclError: unknown option "-borderwidth"
The error comes from this line
self._button.config( width=1, height=1, borderwidth = 0)
Which I've also tried as
self._button = ttk.Button(self,command = self._activate, width=1, height=1, borderwidth = 0)
With the same error.
I've removed the borderwidth, and then it throws the same error with width/height. I've tested the width/height/borderwidth in other instances and it works, I'm just not sure why it won't work here.
When using ttk widgets, you have to keep in mind that many of the parameters you would set separately for each tkinter widget are mostly managed by styles when it comes to ttk widgets.
If you look up the ttk.Button widget options (here), you can easily find out which parameters can and can not be set.
In your case, you'd have to create a style for the button.
s = ttk.Style()
s.configure("TButton", borderwidth=0)
If you use this code, you won't have to separately declare that your button is using that style, however every other (ttk) button will automatically use this style too. If you want to set a style for just one button, you'd have to do something like this:
s = ttk.Style()
s.configure("b1.TButton", borderwidth=0) #you can rename b1 to anything you want
and then
b1 = Button(cpane.frame, text ="GFG", style="b1.TButton").grid(
row = 1, column = 2, pady = 10)
You can also read up on styles by yourself here.
I got the assignment to build a program of a store.Now, the customers have to register to be able to buy, I made a main window that has buttons for each action I need to perform. When the user tries to register another window with the data needed to complete the registration appears. Now how do I store the data from the input-box into a list with a button?
Here's an example of how I'm setting each box that the user needs to fill:
var1 = StringVar()
var1.set("ID:")
label1 = Label(registerwindow,textvariable=var1,height = 2)
label1.grid(row=0,column=1)
ID=tkinter.StringVar()
box1=Entry(registerwindow,bd=4,textvariable=ID)
box.grid(row=0,column=2)
botonA= Button(registerwindow, text = "accept",command=get_data, width=5)
botonA.grid(row=6,column=2)
I tried setting the button to run a function that gets the input, but I't now working. Here's what I did
def get_data():
print (box1.get())
A few problems:
Unless you do import tkinter AND from tkinter import * - which you shouldn't; just choose one - your program will choke on either var1 = StringVar() or on ID=tkinter.StringVar().
Define the get_data() function before binding it to a Button.
You assigned box1 but then gridded box.
The following sample will get the box's contents, add it to a list, and print the list to the console every time you click "Accept." Replace the names of parent windows, grid locations of each widget, and so on to suit your program.
from tkinter import *
root = Tk()
root.wm_title("Your program")
mylist = []
def get_data(l):
l.append(box1.get())
print(l)
var1 = StringVar()
var1.set("ID:")
label1 = Label(root,textvariable=var1,height = 2)
label1.grid(row=0,column=0)
ID=StringVar()
box1=Entry(root,bd=4,textvariable=ID)
box1.grid(row=0,column=1)
botonA= Button(root, text = "accept",command=lambda: get_data(mylist), width=5)
botonA.grid(row=0,column=2)
root.mainloop()
to retrieve the value, you need to access the variable it is attached to, not the entry field on the screen:
def get_data():
print (ID.get())