I have created a simple loop within a combobox which creates a dynamic number of entries - this in fact depends on the number of filters the user wants. Ideally I'd like to store all of the user-made choices through the 'Submit' Button but I can't seem to be able to pass the variables into the 'callback' function within the class module. As a result, I am only able to store the last combobox. I have created a 'n' variable which would allow for easy retrieval of each combobox. Essentially I then want to be storing all those selections in the variable 'user_selections'. Ideally this code would then be re-used as template when facing user-selection choices.
For future reference I'd also like to explore whether there is the possibility of having multiple user selections within each of the comboboxes, rather than one single dropdown selection. I am rather new to python coding so struggling to put things together.
Any help is massively appreciated! Code below:
import tkinter as tk
from tkinter import *
from tkinter import ttk
class User_ComboBox(Tk):
def __init__(self, s, options):
Tk.__init__(self)
self.title("User Selections: ")
x = s*100
y = s*50
self.geometry(str(x) + "x" + str(y) + '+350+350')
self.labelTop = tk.Label(self,text = "Data Slicing Options: ")
self.labelTop.grid(row = 0, column = 0, sticky = W, pady = 2)
for i in range(1, s+1):
n = "combobox_" + str(i)
self.label = Label(self,text="Select Criteria " + str(i))
self.label.grid(row = i, column = 0, sticky = W, pady = 2)
self.n = ttk.Combobox(self,values=options[i - 1])
self.n.grid(row = i, column = 1, sticky = W, pady = 2)
self.okButton = tk.Button(self, text='Submit',command = self.callback)
self.okButton.grid(row = i + 1, column = 0, sticky = W, pady = 2)
def callback(self):
""" Get the contents of the Entry and exit """
self.comboBox_contents = {'a':self.n.get()}
self.destroy()
def ComboboxSelection():
options = [['Layer 1','Layer 2','Layer 3'],['Americas','APAC','EMEA'],['Bank','Institution','Fund']]
n_comboboxes = 3
selection = User_ComboBox(n_comboboxes, options)
selection.mainloop()
return selection.comboBox_contents
user_selections = ComboboxSelection()
I've edited your code below, this should work:
(note that I've removed a few things and cleaned it a bit). Basically the problem was that you were trying to reassign to n every time, whereas you needed a list container to which to append each n.
from tkinter import *
from tkinter import ttk
class User_ComboBox(Tk):
def __init__(self, s, options):
Tk.__init__(self)
self.title("User Selections: ")
self.comboBox_contents = []
self.comboBoxes = [[] for n in range(s)]
x = (s+1)*100
y = (s+1)*50
self.geometry(str(x) + "x" + str(y) + '+350+350')
self.labelTop = Label(self,text = "Data Slicing Options: ")
self.labelTop.grid(row = 0, column = 0, sticky = W, pady = 2)
for i in range(s):
self.label = Label(self,text="Select Criteria " + str(i))
self.label.grid(row = i+1, column = 0, sticky = W, pady = 2)
self.comboBoxes[i] = ttk.Combobox(self, values = options[i])
self.comboBoxes[i].grid(row = i+1, column = 1, sticky = W, pady = 2)
self.okButton = Button(self, text='Submit',command = self.callback)
self.okButton.grid(row = i + 2, column = 0, sticky = W, pady = 2)
def callback(self):
""" Get the contents of the Entry and exit """
self.comboBox_contents = [x.get() for x in self.comboBoxes]
self.destroy()
def ComboboxSelection():
options = [['Layer 1','Layer 2','Layer 3'],['Americas','APAC','EMEA'],['Bank','Institution','Fund']]
n_comboboxes = 3
selection = User_ComboBox(n_comboboxes, options)
selection.mainloop()
return selection.comboBox_contents
user_selections = ComboboxSelection()
print(user_selections)
also, if I understand your second question correctly, I think what you are looking for is a ListBox?
As a general comment, try keeping text and code to a minimum, the shorter the question the more people are gonna read it!
Related
I am new in creating GUI. I am doing it in Python with Tkinter. In my program I calculate following characteristics
def my_myfunction():
my code ...
print("Centroid:", centroid_x, centroid_y)
print("Area:", area)
print("Angle:", angle)
I would like to ask for any help/tips how to display those values in GUI window or how to save them in .txt file so that I can call them in my GUI
Thanks in advance
Tkinter is easy and an easy way to do a GUI, but sometimes it can be frustrating. But you should have read the docs before.
However, you can do in this way.
from tkinter import *
yourData = "My text here"
root = Tk()
frame = Frame(root, width=100, height=100)
frame.pack()
lab = Label(frame,text=yourData)
lab.pack()
root.mainloop()
There are several ways to display the results of any operation in tkiner.
You can use Label, Entry, Text, or even pop up messages boxes. There are some other options but these will probably be what you are looking for.
Take a look at the below example.
I have a simple adding program that will take 2 numbers and add them together. It will display the results in each kind of field you can use as an output in tkinter.
import tkinter as tk
from tkinter import messagebox
class App(tk.Frame):
def __init__(self, master):
self.master = master
lbl1 = tk.Label(self.master, text = "Enter 2 numbers to be added \ntogether and click submit")
lbl1.grid(row = 0, column = 0, columnspan = 3)
self.entry1 = tk.Entry(self.master, width = 5)
self.entry1.grid(row = 1, column = 0)
self.lbl2 = tk.Label(self.master, text = "+")
self.lbl2.grid(row = 1, column = 1)
self.entry2 = tk.Entry(self.master, width = 5)
self.entry2.grid(row = 1, column = 2)
btn1 = tk.Button(self.master, text = "Submit", command = self.add_numbers)
btn1.grid(row = 2, column = 1)
self.lbl3 = tk.Label(self.master, text = "Sum = ")
self.lbl3.grid(row = 3, column = 1)
self.entry3 = tk.Entry(self.master, width = 10)
self.entry3.grid(row = 4, column = 1)
self.text1 = tk.Text(self.master, height = 1, width = 10)
self.text1.grid(row = 5, column = 1)
def add_numbers(self):
x = self.entry1.get()
y = self.entry2.get()
if x != "" and y != "":
sumxy = int(x) + int(y)
self.lbl3.config(text = "Sum = {}".format(sumxy))
self.entry3.delete(0, "end")
self.entry3.insert(0, sumxy)
self.text1.delete(1.0, "end")
self.text1.insert(1.0, sumxy)
messagebox.showinfo("Sum of {} and {}".format(x,y),
"Sum of {} and {} = {}".format(x, y, sumxy))
if __name__ == "__main__":
root = tk.Tk()
myapp = App(root)
root.mainloop()
I want to have the text input of this entry box save to a list and then be printed. I cannot have it save the input upon the button press and it just prints the placeholder variable.
names = []
from tkinter import*
class Trip:
def __init__(self, parent):
E_name = "q"
self.En_name = "g"
self.En_name = str(self.En_name)
self.go = Frame(parent, width = 500, height = 450, bg = "snow", pady = 30, padx = 10)
self.go.grid(row = 1, column = 0)
self.go.grid_propagate(0) # to reserve space required for frame
self.tet = Frame(parent, width = 500, height = 450, bg = "snow")
name = Label(self.go, text = "Name:", bg = "snow")
name.grid(row = 1, column = 0, sticky = E)
self.E_name = Entry(self.go, width = 40, textvariable = self.En_name)
self.E_name.grid(row = 1, column = 1, sticky = W, pady = 4)
menuButton = Button(self.go, text = "Continue", command = self.breakeverything)
menuButton.grid(row = 8, column = 1, pady = 4)
def breakeverything(self):
names.append(self.En_name)
print (names[0])
self.E_name.delete(0, "end")
#main routine
if __name__ == "__main__":
root = Tk()
root.title("Traveller Details")
play = Trip(root)
root.geometry("500x450+0+0")
root.mainloop()
A textvariable is supposed to be a tkinter.StringVar(), not a primitive string. Your application looks simple enough that it shouldn't even need it. Take out self.En_name, take out the textvariable, and just retrieve the current value of the Entry widget in breakeverything() (which should no longer be an appropriate name):
def breakeverything(self):
names.append(self.E_name.get())
print(names[-1]) # printing the last name in the list seems more useful
self.E_name.delete(0, "end")
I would also recommend moving names into Trip.__init__ and making it an instance variable like everything else, with self.names = []. It'll make it easier to keep track of scopes.
You're using textvariable incorrectly (you must use one of the special Tkinter variables such as StringVar), but you don't need to use it at all. Simply save a reference to the widget, then call the get method when you want the value:
self.E_name = Entry(self.go, width = 40)
...
print("you entered: " + self.E_name.get())
If you insist o using textvariable, use a StringVar and then call the get method on that instead:
self.En_name = StringVar()
self.E_name = Entry(..., textvariable=self.En_name)
...
print("you entered: " + self.En_name.get())
I want the whole thing to expand, as the user expands it, without widgets, like the listbox, reshaping. I want it to be exactly like expanding a still image which maintains its aspect ratio. How do I do this?
from tkinter import *
import random
from random import *
import threading
selected = 0
contained = {}
contained[0] = []
name = {}
maxEventItems = 100
i = 0
while i < 10 ** 3:
contained[0].append(i)
name[i] = 'Event {}'.format(i)
i += 1
# Beginning of thread part, in the future.
def eventViewerListboxItems():
i = 0
while i < len(contained[selected]):
eventNumber = contained[selected][i]
eventViewerListbox.insert(END,'{}'.format(name[eventNumber]))
i += 1
master = Tk()
masterCanvas = Canvas(master)
masterCanvas.grid(row = 0,column = 0,sticky = N + S + E + W)
masterFrame = Frame(masterCanvas)
masterFrame.grid(row = 0,column = 0)
main = Frame(masterFrame)
main.grid(row = 0,column = 0)
topButtons = Frame(main)
topButtons.grid(row = 0, column = 0)
saveButton = Button(topButtons,text = 'Save')
saveButton.grid(row = 0,column = 0,sticky = W)
loadButton = Button(topButtons,text = 'Load')
loadButton.grid(row = 0,column = 1,sticky = W)
createEventButton = Button(topButtons,text = 'Create event')
createEventButton.grid(row = 0,column = 2,sticky = W)
eventViewer = Frame(main)
eventViewer.grid(row = 1, column = 0)
eventViewerListboxScrollbar = Scrollbar(eventViewer)
eventViewerListboxScrollbar.grid(row = 1,column = 1, sticky = W + N + S)
eventViewerListbox = Listbox(eventViewer)
eventViewerListbox.grid(row = 1,column = 0,sticky = W)
eventViewerListbox.config(yscrollcommand = eventViewerListboxScrollbar.set)
eventViewerListboxScrollbar.config(command = eventViewerListbox.yview)
bottomButtons = Frame(main)
bottomButtons.grid(row = 2, column = 0,sticky = E)
simulateButton = Button(bottomButtons,text = 'Simulate')
simulateButton.grid(row = 0,column = 0,sticky = E)
callEventViewerListboxItems = threading.Thread(target = eventViewerListboxItems)
callEventViewerListboxItems.start()
partial_contained = {}
partial_contained[selected] = []
i = 0
while i < maxEventItems and i < len(contained[selected]):
partial_contained[selected].append(contained[selected][i])
i += 1
print('I started putting the items from contained[{}] into the listbox in the event viewer.'.format(contained[selected][0]))
print()
print('Below, I will show the first {} items that are in contained:'.format(i,contained[selected]))
print(partial_contained[selected])
print()
master.mainloop()
The problem is that you are using grid, but you haven't given a weight to any rows or columns. The weight determines which rows and columns are given extra space such as when a user resizes the window. By default the weight of every row and column is zero, meaning they get no extra weight.
As a rule of thumb, you need to call grid_rowconfigure and grid_columnconfigure for at least one row and one column, and give that row or column a positive weight. You will need to do this for every widget that is using grid to manage its children.
You also aren't using the sticky attribute consistently, so even if you get the columns to grow and shrink, your widgets won't expand to fill the area they've been given.
You need to be methodical about creating a complex layout. My advice is to start over, and just get masterCanvas to grow and shrink like you expect. Give it a bold, distinctive cover so you can see it (ie: if it has the same color as the background, it's impossible to know if it's growing or shrinking to fill the area)
Once you get masterCanvas where it is growing and shrinking properly, move on to masterFrame, and then main, and then eventViewer, and finally the listbox. At each step be sure to give the widget a distinctive color so you can visualize it.
You might also want to consider removing so many layers of nested frames. The more you have, the more difficult it becomes to get them to all work together. However, multiple areas are easy if you take a methodical approach, and don't try to get half a dozen different frames working together all at once. Get one working, then focus on its immediate children. When they work, focus on their children, and so on.
I really need help with some code. I don't expect you to write it for me, since it is a school project, but I am just really lost and need help.
The code I am writing is some sort of production system.
It doesn't need to actually be able to send a task anywhere, since this is just an imagined scenario.
The code has to consist of three files: data.py, model.py and gui.py.
Gui can access the two other files
Data can only access model
Model can't access either of the other two.
My teacher had written some of the code witch I have continued on. Some of the text is in danish, but most comments are in English.
The code is as follows.
data.py
from model import *
class Data(object):
def __init__(self):
self.units = []
self.finished_tasks = []
def __str__(self):
result = "These tasks have been finished: "
for i in self.finished_tasks:
result += str(i)
return result
def task_done(self, unit):
done_task = unit.task_done()
if done_task != None:
#TODO: add to list of finished tasks
pass
def add_task(self, name, amount, unit):
s = Springroll_task(name, amount)
unit.add_to_queue(s)
def read_from_database(self):#doesn't actually read from db..
self.units.append(Production_unit("maskine1"))
self.units.append(Production_unit("maskine2"))
self.add_task("Miniruller", 100, self.units[0])
self.add_task("Maxiruller", 200, self.units[0])
self.add_task("HowIRoll", 3000, self.units[0])
self.add_task("RulleMarie", 40, self.units[1])
self.add_task("Rullesten", 500, self.units[1])
self.add_task("Toiletpapirsruller", 6000, self.units[1])
model.py
class Springroll_task(object):
def __init__(self, name, amount):
self.name = name
self.amount = amount
def __str__(self):
return self.name + " " + str(self.amount)
class Production_unit(object):
def __init__(self, amount={}, name={},):
#name of the production unit
self.name = name
self.amount = amount
#the current task
self.current_task = None
#the tasks in the queue
self.springroll_queue = []
#the size of the queue
self.queue_size = 0
def __str__(self):
#TODO
return self.name + " " + str(self.amount)
def add_to_queue(self, task={}):
if self.current_task == None:
self.current_task = task
else:
self.springroll_queue.append(task)
self.queue_size += 1
#remember to update queue_size
pass
def task_done(self):
#TODO: remember the old current task.
#Set the current task to be the first in the queue (and remove from queue)
# - if there is a task in the queue.
#return the old current task.
#remember to update queue_size
self.queue_size -= 1
pass
gui.py
from tkinter import *
from model import *
from data import Data
class Application(Frame):
def __init__(self, master, unit):
self.mod = Production_unit()
super(Application, self).__init__(master)
self.grid()
self.unit = unit
self.create_widgets()
def create_widgets(self):
self.unit_name_lbl = Label(self, text = self.unit.name)
self.unit_name_lbl.grid(row = 0, column = 0, columnspan = 2, sticky = W)
self.cur_prod_lbl = Label(self, text = "produktion nu: ")
self.cur_prod_lbl.grid(row = 1, column = 0, columnspan = 2, sticky = W)
self.prod_lbl = Label(self, text = "produkt")
self.prod_lbl.grid(row = 2, column = 0, sticky = W)
self.amount_lbl = Label(self, text = "antal")
self.amount_lbl.grid(row = 2, column = 1, sticky = W)
#Label for production now
self.amount1_lbl = Label(self, text = " ", bg ="red")
self.amount1_lbl.grid(row = 3, column = 0, sticky = W)
self.amount2_lbl = Label(self, text = " ", bg ="red")
self.amount2_lbl.grid(row = 3, column = 1, sticky = W)
#Button for task finished
self.finished_but = Button(self, text = "Opgave afsluttet", bg ="pink", command=self.mod.task_done)
self.finished_but.grid(row = 3, column = 2, sticky = W)
#Label for queue
self.queue_lbl = Label(self, text = "Kø")
self.queue_lbl.grid(row = 4, column = 0, sticky = W)
#Label for production queue
for i in range(0,3):
self.name_lbl =Label(self, text = self.mod.springroll_queue, bg="red", width= 6)
self.name_lbl.grid(row = 5+i, sticky = W)
for j in range(0,3):
self.qt_lbl =Label(self, text = self.mod.springroll_queue, bg="red", width= 4)
self.qt_lbl.grid(row = 5+j, column = 1)
self.new_lbl = Label(self, text = "Ny")
self.new_lbl.grid(row = 10, column = 0, sticky = W)
#Entry for entries
self.eq1_ent = Entry(self, text = "", width=6)
self.entry_name = self.eq1_ent.get()
self.eq1_ent.grid(row = 11, sticky = W)
self.ea1_ent = Entry(self, text = "", width=4)
self.ea1_ent.grid(row = 11, column = 1, sticky = W)
#Button for add to queue
self.add_but = Button(self, text = "Tilføj til kø", bg ="pink", command=self.mod.add_to_queue(self.ea1_ent.get()))
self.add_but.grid(row = 11, column = 2, sticky = W)
def done(self):
d.task_done(self.unit)
self.redraw()
def add(self):
n = "Nyt navn" #read from gui
a = "Nyt antal" #read from gui
d.add_task(n, a, unit)
self.redraw()
def redraw(self):
#TODO
pass
# main
root = Tk()
root.title("Daloon")
root.geometry("300x300")
d = Data()
d.read_from_database()
p = d.units[0]
app = Application(root, p)
root.mainloop()
So it currently looks like this:
What I need to be able to do is to take an input in the bottom two entry widgets and put them in one of the 4 label widgets above, beginning from the top and then in the queue afterwards, this should happen when I press the button add_but, which seems to be gone currently.
After that I need the task stored in the data file when the "Opgave afsluttet" button is pressed.
I really hope someone is able to help me!
I edited it with some suggestions, and am calling the right self.eq1_ent.get() now, I think. I dont get any error any longer, now I just don't really know how to make it do what I want.
Edit 2: I am slowly getting some stuff, so i have made changes in the model.py and gui.py...
It looks like this now:
self.eq1 is not defined. you have self.q1_lbl and self.eq1_ent.
To access the label use self.q1_lbl.
To be able to set text to your label create them as following:
self.var = StringVar()
self.unit_name_lbl = Label(self, textvariable=self.var)
For example, from redraw() you can set 'text' to self.unit_name_lbl like this : self.var.set('text').
Check if you missed self in d.add_task(n, a, unit)
When you do command=mod.add_to_queue(self.ea1_ent.get()) the mod.add_to_queue function will be called directly, if you want to pass argument to this function when user press the button, you can use lambda:
command=lambda: mod.add_to_queue(self.ea1_ent.get)
I wrote a simple program importing Tkinter just to play with Radio Buttons. I find that I'm getting errors in very, very weird places.
from Tkinter import *
class Application (Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
Label(self, text = "Select the last book you read.").grid (row = 0, column = 0, sticky = W)
self.choice = StringVar()
Radiobutton (self,text = "Nausea by Jean-Paul Sartre",variable = self.choice,
value = "Wake up. This is a dream. This is all only a test of the emergency broadcasting system.",
command = self.update_text).grid (row = 2, column = 1, sticky = W)
Radiobutton (self,
text = "Infinite Jest by David Foster Wallace",
variable = self.choice,
value = "Because an adult borne without the volition to choose the thoughts that he thinks, is going to get hosed ;)",
command = self.update_text).grid (row = 3, column = 1, sticky = W)
Radiobutton (self,
text = "Cat's Cradle by Kurt Vonnegut",
variable = self.choice,
value = " \"Here we are, trapped in the amber of the moment. There is no why!\" ",
command = self.update_text.grid (row = 4, column = 1, sticky = W)
self.txt_display = Text (self, width = 40, height = 5, wrap = WORD)
self.txt_display.grid (row = 6, column = 0, sticky = W)
#There is only one choice value - self.choice. That can be "printed."
def update_text(self):
message = self.choice.get()
self.txt_display.delete (0.0, END)
self.txt_display.insert (0.0, message)
# The Main
root = Tk()
root.title ("The Book Critic One")
root.geometry ("400x400")
app = Application (root)
root.mainloop()
I seem to be getting errors in very odd places. One came in the "=" sign in the Label attribution and when I changed it to == when i was playing around, the next one came in the variable part of the RadioButton attributes.
Any help would be greatly appreciated. Won't be able to respond immediately as I have to leave to work in a bit, but if you do spot where the bugs are, please let me know.
There are a lot of things going on here. I'll just point out the few that I've found quickly looking at this.
For your Label you shouldn't have = before your parameters...
Label = (self, text = "Select the last book you read.").grid (row = 0, column = 0, sticky = W)
to:
Label(self, text = "Select the last book you read.").grid (row = 0, column = 0, sticky = W)
Change all instances of RadioButton to Radiobutton as that is the actual name of the class in Tkinter.
choice1, choice2, and choice3 do not exist in Application.
More Stuff:
def create_widgets() is missing the self parameter: def create_widgets(self)
Your update_text() function isn't working because you're referencing self.text_display, I believe you want this to be self.txt_display since that is how you defined it previously.