i want to add multiple values that the user select from different comboboxes. I then want to add together the values and display the total value next to the boxes. The problem seems to be that the "values" from the comboboxes are strings, and can therefore not be added together.
If you wanna run the program, just comment out the variable "totalvalues before the last for loop. (i know it looks wierd, i just tried to make a new program to show my problem)
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
win=tk.Tk() #window
win.title("Moskalkylator")
antalcomboboxes=4
mighty=ttk.LabelFrame(win,text='Welcome')
mighty.grid(column=0,row=0,padx=8 ,pady=4)
Items=['Wood','Iron','Plastic','Glass']
def click():
calculate = ttk.Label(win, text="Your total number of items is : " + totalvalues.get()) # See row 27
calculate.grid(column=1, row=0)
buttons_frame=ttk.LabelFrame(mighty)
buttons_frame.grid(column=1,row=8, padx=3, pady=3,sticky=tk.W)
calculate=ttk.Button(buttons_frame,text='Count', command=click).grid(column=1,row=8)
#Creates combobox 1
list1=list(range(11))
number_chosen1= ttk.Combobox(mighty,values=list1, state="readonly") #The number you pick in the combobox
number_chosen1.grid(column=1,row=1,sticky=tk.W)
#Creates combobox 2
list2=list(range(11))
number_chosen2= ttk.Combobox(mighty,values=list2, state="readonly")
number_chosen2.grid(column=1,row=3,sticky=tk.W)
###############
totalvalues=number_chosen1+number_chosen2 #This does not work! COMMENT THIS
###############
#Loop to get the names of the items above combobox
Items_order=0
row_move_loop2=0
for element in Items:
article = ttk.Label(mighty, text=Items[Items_order])
article.grid(column=1, row=row_move_loop2)
Items_order=Items_order+1
row_move_loop2=row_move_loop2+2
win.mainloop()
You have to use the get() method to get the values from combobox and convert them to integers. Also, don't keep creating new label everytime you want to display the count instead create the label once and use configure method to change the text.
Here is your corrected code:
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
win=tk.Tk() #window
win.title("Moskalkylator")
antalcomboboxes=4
mighty=ttk.LabelFrame(win,text='Welcome')
mighty.grid(column=0,row=0,padx=8 ,pady=4)
Items=['Wood','Iron','Plastic','Glass']
def click():
global totalvalues
if number_chosen1.get() != '' and number_chosen2.get() != '':
totalvalues= int(number_chosen1.get()) + int(number_chosen2.get())
calculate.configure(text="Your total number of items is : " + str(totalvalues))
buttons_frame=ttk.LabelFrame(mighty)
buttons_frame.grid(column=1,row=8, padx=3, pady=3,sticky=tk.W)
calculate=ttk.Button(buttons_frame,text='Count', command=click).grid(column=1,row=8)
#Creates combobox 1
list1=list(range(11))
number_chosen1= ttk.Combobox(mighty,values=list1, state="readonly") #The number you pick in the combobox
number_chosen1.grid(column=1,row=1,sticky=tk.W)
#Creates combobox 2
list2=list(range(11))
number_chosen2= ttk.Combobox(mighty,values=list2, state="readonly")
number_chosen2.grid(column=1,row=3,sticky=tk.W)
###############
print(number_chosen1.get())
totalvalues = 'None'
calculate = ttk.Label(win) # See row 27
calculate.grid(column=1, row=0)
###############
#Loop to get the names of the items above combobox
Items_order=0
row_move_loop2=0
for element in Items:
article = ttk.Label(mighty, text=Items[Items_order])
article.grid(column=1, row=row_move_loop2)
Items_order=Items_order+1
row_move_loop2=row_move_loop2+2
win.mainloop()
when u retrieve values as a string u need to convert them.
totalvalues=int(number_chosen1)+int(number_chosen2)
or
totalvalues=float(number_chosen1)+float(number_chosen2)
Related
I am trying to create a pop-up box to select multiple years. I have the box created but I cannot figure out how to make a button to actually select multiple years. The goal is to take that selection and store it in a list.
from tkinter import *
import pandas as pd
import tkinter as tk
test_years = ["2016", "2017", "2018", "2019"]
root = tk.Tk()
root.title("Test Year Selection")
lb = Listbox(root, selectmode=MULTIPLE, height = len(test_years), width = 50) # create Listbox
for x in test_years: lb.insert(END, x)
lb.pack() # put listbox on window
root.mainloop()
To clarify I am looking to select lets say 2017 and 2018 and have that selection stored in a list using tkinter listbox.
Any assistance would be greatly appreciated.
A example to get the value you select when you press the Start button:
from tkinter import *
# import pandas as pd
import tkinter as tk
def printIt():
SelectList = lb.curselection()
print([lb.get(i) for i in SelectList]) # this will print the value you select
test_years = ["2016", "2017", "2018", "2019"]
root = tk.Tk()
root.title("Test Year Selection")
lb = Listbox(root, selectmode=MULTIPLE, height = len(test_years), width = 50) # create Listbox
for x in test_years: lb.insert(END, x)
lb.pack() # put listbox on window
tk.Button(root,text="Start",command=printIt).pack()
root.mainloop()
Basically you want to add the value of selected item of listbox to a list. you need to call bind() method on the listbox widget. here is the code from this amazing tutorial on tkinter listbox
def get_value(event):
# Function to be called on item click
# Get the index of selected item using the 'curseselection' method.
selected = l.curselection()
if selected: # If item is selected
print("Selected Item : ",l.get(selected[0])) # print the selected item
# Create a listbox widget
l = Listbox(window)
l.bind('<>',get_value)
l.pack()
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 months ago.
I am trying to program a minesweeper game on python using tkinter. I started off by creating a grid of buttons using a two dimensional list, and the generation of the button and everything works. The only issue I have is that I don't know how to determine which button in my grid is clicked. My goal is to be able to click on a button and through that I know the coordinates of that in my grid [row][col].
This is the code I have so far.
from tkinter import *
from functools import partial
from itertools import product
# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
# Define settings upon initialization. Here you can specify
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
#reference to the master widget, which is the tk window
self.master = master
#with that, we want to then run init_window, which doesn't yet exist
numRows = int(input("# of Rows: "))
numCols = int(input("# of Cols: "))
self.init_window(numRows, numCols)
#Creation of init_window
def init_window(self, rowNum, colNum):
# print(x, y)
# changing the title of our master widget
self.master.title("GUI")
# allowing the widget to take the full space of the root window
self.pack(fill=BOTH, expand=1)
# creating a button instance
#quitButton = Button(self, text="Exit",command=self.client_exit)
# placing the button on my window
#quitButton.place(x=0, y=0)
but = []
for row in range(0, rowNum):
curRow = []
for col in range(0, colNum):
curRow.append(Button(self, bg="gray", width=2,height=1, command=lambda: self.open_button(row, col)))
curRow[col].grid(row=row,column=col)
but.append(curRow)
#but[1][1].config(state="disabled")
#but[1][1]["text"] = "3"
#but[1][1]["bg"] = "white"
def open_button(self, r, c):
print(r, " : ", c)
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("600x600")
#creation of an instance
app = Window(root)
#mainloop
root.mainloop()
Whenever I click on the grid, it gives me the very last button...
For example, a 9x9 grid always gives me "9 : 9" whenever I click any button.
Solutions welcomed! I want an easy way to get the coordinates without changing too much of the code (if possible).
Thanks!
The row and col variables are assigned each value in the ranges. At the end of the loop that generates the buttons, the values for those variables are left at the last values in the ranges, e.g. "9 : 9".
Try replacing the line
curRow.append(Button(self, bg="gray", width=2,height=1, command=lambda: self.open_button(row, col)))
with
curRow.append(Button(self, bg="gray", width=2,height=1, command=lambda rw=row, cl=col: self.open_button(rw, cl)))
This assigns the values of row and col at the time the button is created to the variables rw and cl, which remain the same for each button as the for-loop iterates.
See this link:
Tkinter assign button command in loop with lambda
I made this program where I am putting labels on a grid without saving them in a variable. I do this because then I can for loop through a list of classes and get the data from each class in and add them to a row. This is a small piece of it:
self.collum = 0
for i in self.gui_resource_list:
Label(text=i.get_name(), relief="groove", width=15).grid(column=self.column, row=0)
Label(text=i.get_buyPrice(), relief="groove", width=15).grid(column=self.column, row=1)
Label(text=i.get_salePrice(), relief="groove", width=15).grid(column=self.column, row=2)
Label(text=i.arrow, relief="groove", width=15).grid(column=self.column,row=3)
self.column += 1
So this will generate a table-like layout. Then there is a button that updates all the values runs that for loop again. So it basically draws the new labels on top of the older ones. This is not good because when you are on the turn 300 there are 300 labels times the all the resource instances in gui_resource list. A way to fix this is to delete the old labels.
Is there a way to delete an unsaved label? Something like:
delete_grid(column=2,row=3)
And that would delete all of the things in the grid at position 2,3?
You can ask grid to give a list of widgets that it manages. You could then iterate over that list to find out which widget is in each row and column.
For example, if you wanted to be able to modify the text in the widget at a specific row or column, you could do something like this:
def set_item_text(master, row, column, text):
for child in master.grid_slaves():
grid_info = child.grid_info()
if grid_info['row'] == row and grid_info['column'] == column:
child.configure(text=text)
Here's an example that will change the text in row 2 column 2 to "hello, world":
import Tkinter as tk
def set_item_text(master, row, column, text):
for child in master.grid_slaves():
grid_info = child.grid_info()
if grid_info['row'] == row and grid_info['column'] == column:
child.configure(text=text)
root = tk.Tk()
for row in range(4):
for col in range(5):
tk.Label(
root, text="row %s col %s" % (row, col)
).grid(row=row, column=col, padx=8)
set_item_text(root, 2,2, "hello, world")
root.mainloop()
You could just as easily delete the widget, though if you're just wanting to refresh a "table-like thing" it's more efficient just to change the data than to delete and recreate all of the widgets.
from pprint import pprint
from tkinter import Tk, Label
root = Tk()
Label(root, text='MyLabel').pack()
Label(root, text='MyLabel').pack()
Label(root, text='MyLabel').pack()
# as you did not kept references to the labels
# you have to look into the childrens of root
pprint(root.children) # show root children names
print()
root.children['!label2'].destroy() # do what you asked on the second Label
pprint(root.children) # check that it's gone
Let's say I have a Tkinter app with 2 rows displaying 2 widgets:
from tkinter import *
from tkinter.ttk import *
root = Tk()
Label(root, text="Some Data").grid(row=0)
Label(root, text="Some Data").grid(row=1)
root.mainloop()
Now this will display two widgets on row0 and row1.
Now if I want to insert another (one or more) widget between these two rows at a later stage (say as a response to a button click event), what would be the best way to do that.
Current output:
Some Data
Some Data
Expected output:
Some Data
<<New data>>
Some Data
<<New Data>> will be inserted at a later stage as a response to a button click.
<<New Data>> may be one or more rows.
I do have a simple solution for you.
If you are expecting to insert a widget later and you know you will be then you can simply place your 2nd label on grid row 2 and then place your new widget on grid row 1 later. If you need to have more than one row you could place your 2nd label even further down the line.
from tkinter import *
from tkinter.ttk import *
root = Tk()
def add_new_data():
Label(root, text="<<New Data>>").grid(row=1)
Label(root, text="Some Data").grid(row=0)
Label(root, text="Some Data").grid(row=2)
Button(root, text="Add New Data", command=add_new_data).grid(row=3)
root.mainloop()
Results:
The reason this works is because Tkinter's geometry manager will collapse rows and columns to nothing if there is nothing in them so you can use this behavior to your advantage when working with something like this.
Now if you wanted something that could work with any number of label then we can use a list to help us accomplish that.
My next example with be written in class and will show the use of a list to do what we want.
We can store widgets in a list and because we can do this we are also able to decide where in that list to put stuff and use the lists index to our advantage when setting up the grid.
First thing is to create our Some Data labels and append them to a list. The next is the add the button to that list at the end of the list. This button we will used to call a class method that will insert() a new label into our list.
Next that same method will forget the grid for all widgets inside of our label frame and then it will perform a for loop over the list and re add all the old widgets and the new one in the correct order.
Take a look at the below example.
import tkinter as tk
class App(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.label_frame = tk.Frame(self.master)
self.label_frame.grid()
self.label_list = []
for i in range(2):
self.label_list.append(tk.Label(self.label_frame, text="Some Data"))
self.label_list[i].grid(row=i)
self.label_list.append(tk.Button(self.label_frame, text="Add new data", command=self.add_new_data))
self.label_list[2].grid(row=2)
def add_new_data(self):
self.label_list.insert(1, tk.Label(self.label_frame, text="<<New Data>>"))
for widget in self.label_frame.children.values():
widget.grid_forget()
for ndex, i in enumerate(self.label_list):
i.grid(row=ndex)
if __name__ == "__main__":
root = tk.Tk()
my_app = App(root)
root.mainloop()
Results:
We can add as many new labels as we like.
I'm tring to put some drop down list on a graphic interface I'm building.
I've found the following code for a drop down list, but I'm not able to adapt it to my code.
from Tkinter import *
def print_it(event):
print var.get()
root = Tk()
var = StringVar()
var.set("a")
OptionMenu(root, var, "a","b","c", command=print_it).pack()
root.mainloop()
This is my code, it's quite simple what I've done so far. A menu shows up, it asks for how many (n) components does the users want to enter, and it shows n options to entry. The code above shows 'blank' entrys after you put the desired number of components. I want to replace those three blank entrys with three drop down list.
It's marked when I want to put those dropdown lists.
from Tkinter import *
import Image
import ImageTk
import tkFileDialog
class Planificador:
def __init__(self,master):
master.title("Planificador")
self.frameOne = Frame(master)
self.frameOne.grid(row=0,column=0)
# Logo
self.imgLabel = Label(self.frameOne, image = None)
self.imgLabel.grid(row=0,column=0)
self.img = ImageTk.PhotoImage(file = "logo.png")
self.imgLabel["image"] = self.img
self.botones()
def botones(self):
self.piezastext = Label(self.frameOne, text = " number of components ", justify="center")
self.piezastext.grid(row=1, column=0)
self.entrypiezas = Entry(self.frameOne,width=5)
self.entrypiezas.grid(row=2, column=0)
self.aceptarnumpiezas = Button(self.frameOne,text="Aceptar", command=self.aceptar_piezas,width=8)
self.aceptarnumpiezas.grid(row=6, column=0)
def aceptar_piezas(self):
num_piezas = self.entrypiezas.get()
print num_piezas
self.piezastext.grid_remove()
self.entrypiezas.grid_remove()
self.aceptarnumpiezas.grid_remove()
n = 1;
while n <= int(num_piezas):
self.textopieza = Label(self.frameOne, text = "Pieza", justify="left")
self.textopieza.grid(row=n, column=0)
// INSTEAD THESE 'n' BLANK ENTRYS, I WANT TO PUT 'n' DROP DOWN LISTS
self.entrypiezas = Entry(self.frameOne,width=5)
self.entrypiezas.grid(row=n, column=1)
self.aceptarpiezas = Button(self.frameOne,text="Aceptar",width=8)
self.aceptarpiezas.grid(row=int(num_piezas)+1, column=0)
n += 1
# Main
if __name__ == "__main__":
# create interfacE
root = Tk()
movieApp = Planificador(root)
root.mainloop()
So I want to know how can I put that drop down list on a given frame, frameOnein my case, instead of a full window. Thanks in advance.
I modified your aceptar_piezas function to do what I think you want:
def aceptar_piezas(self):
num_piezas = self.entrypiezas.get()
print num_piezas
self.piezastext.grid_remove()
self.entrypiezas.grid_remove()
self.aceptarnumpiezas.grid_remove()
# Create a list of tuples to hold the dynamically created Optionmenus
# The first item in the tuple is the menu, the second is its variable
self.optionmenus = list()
n = 1
while n <= int(num_piezas):
self.textopieza = Label(self.frameOne, text = "Pieza", justify="left")
self.textopieza.grid(row=n, column=0)
# Variable for the Optionmenu
var = StringVar()
# The menu
menu = OptionMenu(self.frameOne, var, "a","b","c")
menu.grid(row=n, column=1)
# Set the variable to "a" as default
var.set("a")
# Add the menu to the list of Optionmenus
self.optionmenus.append((menu, var))
n += 1
def clicked():
"""This function was made just to demonstrate. It is hooked up to the button"""
for optionmenu in self.optionmenus:
print optionmenu[1].get()
print self.optionmenus
# This button doesn't need to be in the while loop
self.aceptarpiezas = Button(self.frameOne, text="Aceptar", command=clicked, width=8)
self.aceptarpiezas.grid(row=int(num_piezas)+1, column=0)
The tuples in the list are in the order that the Optionmenus were created. So, the first tuple contains the data for the first Optionmenu, the second for the second, and so forth.