Adding button (with variables) by pressing button - tkinter - python

I'm making a point of sale system and trying to implement a button, that when pressed a new button appears but also, a window which asks the user to input an Item
def newButton ():
w = Toplevel()
w.title("New Item") #creates new window when button is pressed
w.geometry("200x200")
itemNameLabel = Label(w, font=("arial", 15), text="What is the item called?")
itemNameLabel.grid(row=0, padx=5)
itemName = Entry(w, width=18, borderwidth=5)
itemName.grid(row=1, padx=5)
newItemName = itemName.get
itemPriceLabel = Label(w, font=("arial", 15), text="What is the item's price?")
itemPriceLabel.grid(row=4, padx=5)
itemPrice = Entry(w, width=18, borderwidth=5)
itemPrice.grid(row=5, padx=5)
def item6_Button():
global item6_qty
item6_price = itemPrice.get
item6_text = newItemName
item6_qty += 1
item6_text = (item6_text + " "+str(item6_price) +" "+ str(item6_qty)) #concatonates text & variable
item6.config(text=item6_text) #updates label text - doesn't add multiple
item6.pack()
item6_Button = Button(itemFrame, text=newItemName, width=10, height=5, command=item6_Button)
item6_Button.grid(row=7, column=1, padx=5)
item6 = Label(receiptFrame)
w.mainloop()
newButton= Button(itemFrame, text="Add New Button", width=20, height=5, command=newButton) #creates button for new window
newButton.place(x=480, y=600)
newButton = Label(itemFrame)
*item6_qty and item6_price are declared near the beginning of the program
This is what I have so far and although the window appears, I don't think the variables are actually set, on top of the new button appearing in the item frame. I'm not entirely sure how to go about this - do I need to use .insert for the variables?
This is the standard code I have which creates the normal button
#Item1 Button + Function
def item1_Button():
global item1_qty #making qty variable global so it can used
item1_text = ("Chips")
item1_qty += 1 #increments qty variable by one everytime button is clicked
item1_text = (item1_text + " "+str(item1_price) +" "+ str(item1_qty)) #concatonates text & variable
item1.config(text=item1_text) #updates label text - doesn't add multiple
item1.pack() #places label within the frame
item1_Button = Button(itemFrame, text="Chips", width=10, height=5, command=item1_Button)
#creates button + links to function
item1_Button.grid(row=4, column=1, padx=5) #positions button
item1 = Label(receiptFrame)#creates label for button
I'm not sure if I've provided enough code of what I've done to give a better picture of what I'm trying to achieve but I know large chunks of code aren't very favoured

here is an example of what You could do (does this help?):
from tkinter import Tk, Button, Entry, Toplevel
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.geometry('100x150')
self.btn = Button(self, text='Create New!', command=self.ask)
self.btn.pack()
def ask(self):
ask_window = InputWindow(self)
ask_window.focus_force()
def create(self, text):
button = Button(self, text=text)
button.pack()
class InputWindow(Toplevel):
def __init__(self, parent):
Toplevel.__init__(self, parent)
self.parent = parent
self.bind('<FocusOut>', self.destroy_)
self.user_input = Entry(self)
self.user_input.pack()
self.submit_btn = Button(self, text='Submit!', command=self.retrieve)
self.submit_btn.pack()
def retrieve(self):
text = self.user_input.get()
self.parent.create(text)
self.destroy()
def destroy_(self, event):
if isinstance(event.widget, Toplevel):
self.destroy()
root = MainWindow()
root.mainloop()

Related

Close Command and Update Command Don't Work With One Button

So for this larger program I'm making I want it so when a user presses a button it closes the dialog windows and updates all the values the user input. Therefore, I have one button do these two things: update the values and close the program. However, trying to combine these two functions doesn't work as when I use both of them only the update() command is called, not the close command. Either works separately btw. Any way to fix this?
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import *
from tkinter.ttk import *
propDiameterInch = 10.5
propDiameterMetric = propDiameterInch*0.0254
class Counter_program():
def __init__(self):
self.window = tk.Tk()
self.window.title("Test")
style = ttk.Style()
style.configure("BW.TLabel", foreground="black", background="white")
#default unit color
unitColor = "slategrey"
boxWidth = 5
# Create some room around all the internal frames
self.window['padx'] = 5
self.window['pady'] = 5
propeller_frame = ttk.LabelFrame(self.window, text="Propeller", relief=tk.RIDGE)
propeller_frame.grid(row=1, column=1, sticky=tk.E + tk.W + tk.N + tk.S)
#propeller diameter
propellerDiameter_label = ttk.Label(propeller_frame, text="Propeller Diameter")
propellerDiameter_label.grid(row=1, column=1, sticky=tk.W + tk.N +tk.S)
propellerDiameter_Units = ttk.Label(propeller_frame, text="inches",foreground=unitColor)
propellerDiameter_Units.grid(row=1, column=3, sticky=tk.W)
propellerDiameter_entry = ttk.Entry(propeller_frame, width=boxWidth)
propellerDiameter_entry.grid(row=1, column=2, sticky=tk.W, pady=3)
propellerDiameter_entry.insert(tk.END, "10")
#now set all global variables from entries - update function
def update():
global propDiameter
propDiameter = propellerDiameter_entry.get()
# Finish button in the lower right corner
#finish_button = ttk.Button(self.window, text = "Submit Input", command = self.window.destroy)
finish_button = ttk.Button(self.window, text = "Submit Input", command=lambda:[update(),self.window.destroy])
finish_button.grid(row=2, column=2)
# Create the entire GUI program
program = Counter_program()
# Start the GUI event loop
program.window.mainloop()
propDiameter
Since your using lambda, its safe to use () with the functions, so just change finish_button to:
finish_button = ttk.Button(self.window, text = "Submit Input", command=lambda:[update(),self.window.destroy()])
Or you could make a new function that does both of this for you, like:
def both():
update()
self.window.destroy()
finish_button = ttk.Button(self.window, text = "Submit Input", command=both)
TIP:
Also its not recommended to use global with OOP, so I recommend you change your code and use proper "methods" and self with OOP for a better experience.
Here is how I think your class should like:
class Counter_program():
def __init__(self):
self.window = tk.Tk()
self.window.title("Test")
style = ttk.Style()
style.configure("BW.TLabel", foreground="black", background="white")
#default unit color
unitColor = "slategrey"
boxWidth = 5
# Create some room around all the internal frames
self.window['padx'] = 5
self.window['pady'] = 5
self.propeller_frame = ttk.LabelFrame(self.window, text="Propeller", relief=tk.RIDGE)
self.propeller_frame.grid(row=1, column=1, sticky=tk.E + tk.W + tk.N + tk.S)
#propeller diameter
self.propellerDiameter_label = ttk.Label(self.propeller_frame, text="Propeller Diameter")
self.propellerDiameter_label.grid(row=1, column=1, sticky=tk.W + tk.N +tk.S)
self.propellerDiameter_Units = ttk.Label(self.propeller_frame, text="inches",foreground=unitColor)
self.propellerDiameter_Units.grid(row=1, column=3, sticky=tk.W)
self.propellerDiameter_entry = ttk.Entry(self.propeller_frame, width=boxWidth)
self.propellerDiameter_entry.grid(row=1, column=2, sticky=tk.W, pady=3)
self.propellerDiameter_entry.insert(tk.END, "10")
# Finish button in the lower right corner
#finish_button = ttk.Button(self.window, text = "Submit Input", command = self.window.destroy)
self.finish_button = ttk.Button(self.window, text = "Submit Input", command=self.both)
self.finish_button.grid(row=2, column=2)
def update(self):
self.propDiameter = self.propellerDiameter_entry.get()
def both(self):
self.update()
self.window.destroy()
Hope this solved the issue, do let me know if any errors or doubts.
Cheers

How do I use a Tkinter Button for two states?

I have the following code, which causes a color/text change when a Tkinter button is clicked. I would like to revert to the original color/text when the button is clicked a second time.
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
I want something that behaves a little more like a toggle, but I would like the look of a button.
Help gratefully received.
You will need an if block to choose what to do. You can make another flag variable to keep track of the state, or just use the current Label or Button text:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
if btn_rf1['text'] == "Start":
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
else:
btn_rf1.configure(text="Start")
lbl_rf1.configure(text=" OFF ", bg="red")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
This would be an ideal place to make a custom Button subclass, so you could have many of these in your program:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
class Christina(Frame):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
self.btn = Button(self, text="Start", command=self.clicked)
self.btn.grid(column=0, row=0)
self.lbl = Label(self, text=" OFF ", bg="red")
self.lbl.grid(column=1, row=0)
def clicked(self):
if self.btn['text'] == "Start":
self.btn.configure(text="Stop")
self.lbl.configure(text=" ON ", bg="green")
else:
self.btn.configure(text="Start")
self.lbl.configure(text=" OFF ", bg="red")
btn1 = Christina(window)
btn1.grid()
btn2 = Christina(window)
btn2.grid()
btn3 = Christina(window)
btn3.grid()
window.mainloop()
If you want a toggle, you can use the checkbutton without an indicator. It has options for the color in the selected and deselected state, and you can tie the value and the label together so that the label changes when you toggle the button.
Like any button, you can tie a command to it. The command can check the value of the variable to determine whether it should do the "on" function or the "off" function.
Here's a simple example:
import Tkinter as tk
def toggle():
if var.get() == "ON":
print("turning on...")
else:
print("turning off...")
root = tk.Tk()
var = tk.StringVar()
toggle = tk.Checkbutton(root, onvalue="ON", offvalue="OFF", width=4,
indicatoron=False,
variable=var, textvariable=var,
selectcolor="green", background="red",
command=toggle)
var.set("OFF")
toggle.pack()
root.mainloop()
Another approach might be to put the "pile of code" to run into different function, collect those in an iterator, and then get the next function from that iterator and execute it:
def bunchofcode():
print("foo")
def somethingelse():
print("bar")
whattodo = iter([bunchofcode, somethingelse])
def clicked_rf1():
try:
next(whattodo)()
except StopIteration:
print("nothing to do")
Or for cyclic behaviour:
from itertools import cycle
whattodo = cycle([bunchofcode, somethingelse])
For a two-state toggle button, you could also use a dict to map the current state to the next. You could also use the button's relief to mark the state.
def clicked_rf1():
transition = {"raised": "sunken", "sunken": "raised"}
btn_rf1["relief"] = transition[btn_rf1["relief"]]

Close and Open Another Window

I am trying to write a script that will ask a word in English and then show its meaning. I am able to ask the question but the answer window doesn't show up. Code I wrote so far as below. In second window, it starts like a new page. How can I modify it? Now, it shows the label but buttons doesn't seem.
from tkinter import *
class Application(Frame):
def __init__(self, master):
"""Initialize the Frame"""
Frame.__init__(self, master)
self.grid()
self.button_clicks = 0 # count the number of button clicks
self.create_widgets()
def root_close(self):
global root
root.destroy()
self.button_clicky()
def create_widgets(self):
"""Button displays number of clicks"""
if clicker % 2 == 0:
self.soru = Label(self, text="Kelime: " + kelime)
self.soru.grid(row=0, column=0, columnspan=2, sticky=W)
self.btn_submit = Button(self, text="Submit", command=self.root_close)
self.btn_submit.grid(row=3, column=1, sticky=W)
else:
self.cevap = Label(self, text="Kelimenin türkçe anlamları:\n" + anlam)
self.cevap.grid(row=0, column=0, columnspan=2, sticky=W)
self.btn_okay = Button(self, text="Bildim", command=self.dogru)
self.btn_submit.grid(row=3, column=0, sticky=W)
self.btn_okay = Button(self, text="Bilemedim", command=self.yanlis)
self.btn_submit.grid(row=3, column=2, sticky=W)
def button_clicky(self):
global clicker
clicker += 1
def dogru(self):
#will do stuff
self.root_close()
def yanlis(self):
self.root_close()
clicker = 0
kelime = "apple"
anlam = "elma"
root = Tk()
root.title("Ask word")
root.geometry("200x85")
app = Application(root)
root.mainloop()
So what I get from your question is that you have the first window with the Kelime question, and you want to open up another window with the else clause in your create_widgets() function once you click the submit button. The problem here is that when you are running root_close(), you're essentially terminating the whole program (the program runs because the root is on a loop created by root.mainLoop()). If you want to open one window when you close the other, check out Closing current window when opening another window
.

Trying to create and configure multiple buttons from the same function in tkinter

so i'm trying to create a code which when each button is clicked it will make that button fit a certain style but with changes to the name, however my current code will replace the button you click and them when you click on another it disables the last one you clicked permanently is there anyway to make it so that the buttons all use the same function but dont get disabled when you activate another button?
from tkinter import *
MainWindow = Tk()
def SquadInfoBttnFull():
global SquadFullBttn
SquadFullBttn = Toplevel(MainWindow)
SquadFullBttn.geometry("658x600")
# -- unit buttons -- #
for i in range(10):
Unit_Button = Button(SquadFullBttn, text="EMPTY", width=8, height=6)
Unit_Button.grid(row = 0, column = i)
Unit_Button.bind("<Button-1>", UnitFill)
def SquadInfoBttn():
InfoBttn = Button(MainWindow, wraplength=50, justify=LEFT, text="SquadInfo Here", width=100, command=SquadInfoBttnFull)
InfoBttn.grid(row=0, column=0, sticky=W)
def UnitInfoBttn():
UnitInfo = Toplevel(SquadFullBttn)
UnitInfo.geometry("300x200")
def UnitFill(event):
global photo
photo = PhotoImage(file="csmSingle.png")
btn = event.widget
grid_info = event.widget.grid_info()
btn = Button(SquadFullBttn, text="UNIT", image=photo, width=58, height=93, command=UnitInfoBttn, compound = TOP)
btn.grid(row=grid_info["row"], column=grid_info["column"], sticky=E)
SquadInfoBttn()
MainWindow.mainloop()
You are trying to make changes to the existing button and not create a new one. Also, you don't need to create a new instance of PhotoImage every time. The following code works for me:
from tkinter import *
MainWindow = Tk()
photo = PhotoImage(file="csmSingle.png")
def SquadInfoBttnFull():
global SquadFullBttn
SquadFullBttn = Toplevel(MainWindow)
SquadFullBttn.geometry("658x600")
# -- unit buttons -- #
for i in range(10):
Unit_Button = Button(SquadFullBttn, text="EMPTY", width=8, height=6)
Unit_Button.grid(row = 0, column = i)
Unit_Button.bind("<Button-1>", UnitFill)
def SquadInfoBttn():
InfoBttn = Button(MainWindow, wraplength=50, justify=LEFT, text="SquadInfo Here", width=100, command=SquadInfoBttnFull)
InfoBttn.grid(row=0, column=0, sticky=W)
def UnitInfoBttn():
UnitInfo = Toplevel(SquadFullBttn)
UnitInfo.geometry("300x200")
def UnitFill(event):
btn = event.widget
grid_info = event.widget.grid_info()
btn.config(text="UNIT", image=photo, width=58, height=93, command=UnitInfoBttn, compound = TOP)
btn.grid(row=grid_info["row"], column=grid_info["column"], sticky=E)
SquadInfoBttn()
MainWindow.mainloop()

How can I hide the main tkinter program window whilst logging in using another Toplevel window?

I am practicing with a simple login system. I have a main program window which I wish to hide whilst a login window appears. When the correct password is entered I want the login window to destroy and the main window to reappear (deiconify).
The main program code (LibrarySQL.py):
from tkinter import *
import libraryentrySQL, librarydatabase, login
import sqlite3
import os
def click():
entered_text = entry.get() #collect text from text entry box
output.delete(0.0,END) #clears text box - start clearing from 0.0 (from line 0) to END (after last character)
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute("SELECT definition FROM Dictionary WHERE word = ?", (entered_text,))
for row in c:
definition = row[0]
break
else:
definition = "No word found in dictionary, try again!"
output.insert(END, definition) #this inserts the contents of variable 'definition' at the beginning (END) - because it was cleared before, END is the at the start
def clickentry(): #this function is run when the 2nd button (entry is pressed)
def definition_submitted(word, definition):
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute("INSERT INTO Dictionary VALUES (?, ?)", (word, definition))
new_db.commit()
new_db.close()
definition_window = libraryentrySQL.DefinitionWindow(window, definition_submitted) #this creates the object 'definition window' and passes to it 'the window variable'
#so that it can have a canvas
def clickdataview():
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute("SELECT * FROM Dictionary")
data = c.fetchall()
count = len(data)
database_window = librarydatabase.DatabaseWindow(window, count, data)
new_db.close()
def login_window(window):
log = login.Login(window)
window = Tk()
login_window(window)
window.withdraw()
window.deiconify()
window.title("My Little Dictionary")
#Create the Label
Label(window, text="Enter the word you want defining:").grid(row=0, column=0, sticky=W)
#create entry box
entry=Entry(window, width=20, bg="light green")
entry.grid(row=1, column=0, sticky=W)
#create submit button
Button(window, text="Submit", width=5, command=click).grid(row=2, column=0, sticky=W)
#create second label
Label(window, text="\nDefinition").grid(row=3, column=0, sticky=W)
#create text box
output=Text(window, width=75, height=6, wrap=WORD, background="light green")
output.grid(row=4, column=0, sticky=W)
#create submit button to open enter new definition window
Button(window, text="Enter a New Definition", width=20, command=clickentry).grid(row=5, column=0, sticky=W)
#create open database button to open database view window
Button(window, text="View Dictionary", width=20, command=clickdataview).grid(row=6, column=0, sticky=W)
#Create the Dictionary.db if not already present
if not os.path.isfile("dictionary.db"):
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute('''CREATE TABLE Dictionary
(word text,
definition text)''')
c.execute('''INSERT INTO Dictionary VALUES
('Algorithm', 'Step by step instructions to complete a task')''')
new_db.commit()
new_db.close()
window.mainloop()
The Login class code (login.py):
from tkinter import *
class Login(Toplevel):
def __init__(self, window):
Toplevel.__init__(self, window)
self.title("Current Library")
Label(self, text="Log in to use this program:").grid(row=0, column=0, sticky=W)
self.userbox=Entry(self, width=20, bg="light green")
self.userbox.grid(row=1, column=0, sticky=W)
self.passbox=Entry(self, width=20, bg="light green")
self.passbox.grid(row=2, column=0, sticky=W)
Button(self, text="Submit", width=5, command=self.clicked).grid(row=3, column=0, sticky=W)
def clicked(self):
username = self.userbox.get()
password = self.passbox.get()
if password == "password":
self.correct = True
self.destroy()
else:
pass
I know exactly where the problem is: the main window hides then reappears instantly due to the order of instructions in the mainloop.
How therefore, can I get the main to hide and the login window to appear UNTIL the password is entered correctly? After which I wish the login window to destroy and the main to reappear?
Any help would be gratefully received.
You can modify the clicked() method of your Toplevel instance to call deiconify, that way it's only called when the Toplevel is destroyed.
class Login(Toplevel):
def __init__(self, window):
Toplevel.__init__(self, window)
# this is the parent/root window
self.window = window
Button(self, text='show main', command=self.click).pack()
def click(self):
self.destroy() # destroy the toplevel
self.window.deiconify() # and restore the root window
And in your main module:
root = Tk()
# make the login instance
Login(root)
# withdraw the main window
root.withdraw()
# set up widgets in main window
...
mainloop()

Categories

Resources