Python Tkinter. How to store entry via button in label - python

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)

Related

Getting all user-selected values within the Combobox

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!

Calling a class method in another class - Tkinter

I am using Tkinter in Python 3 to create a converter between hex, binary and denary - while creating my own methods for conversions instead of using the built in methods.
I've completed the denary to binary conversion through the use of my 'highestPowerOf2' method in the DenToBin class. I want to use this method in my HexToBin class, but whenever I try to, an additional blank window pops up and I receive - AttributeError: 'NoneType' object has no attribute 'title' - error.
How would I run 'highestPowerOf2' in the HexToBin class?
(also sorry for any poor programming practise)
Code:
from tkinter import *
from functools import partial
class Menu(Frame):
def __init__(self, master= None): # initialise Menu
Frame.__init__(self, master) # initialise Frame
self.master = master # what does this do
self.createWindow()
def createWindow(self):
self.pack(fill = BOTH, expand = 1)
denToBin = Button(self, text = 'Denary to Binary', command = lambda: self.changeWindow(DenToBin))
denToBin.pack(fill = X, padx = 10, pady = 10)
hexToBin = Button(self, text = 'Hex to Binary', command = lambda: self.changeWindow(HexToBin))
hexToBin.pack(fill = X, padx = 10, pady = 10)
def changeWindow(self, object):
root.withdraw()
currentFrame = object(root)
class DenToBin(Toplevel):
def __init__(self, master = None):
Toplevel.__init__(self, master)
self.master = master
self.Window()
self.denary = 0
def Window(self):
# print(self.denary) -- Why is this not recognised?????????
self.master.title('Unit Converter: Denary to Binary')
instructionDen = Label(self, text = 'Denary Value: ')
instructionDen.grid(row = 1, column = 0, padx = 10)
instructionBin = Label(self, text = 'Binary Value: ')
instructionBin.grid(row = 2, column = 0, padx = 10)
self.denaryEntry = Entry(self)
self.denaryEntry.grid(row = 1, column = 2, padx = 10, pady = 5)
convertButton = Button(self, text = 'Convert!', command = lambda: self.highestPowerOf2(10)) # as an example
convertButton.grid(row = 3, column = 1)
# finds highest power of 2 that is less than denary number - helps self.convert()
def highestPowerOf2(self,number):
print('I find the highest power of 2 that fits into the number given.')
class HexToBin(Toplevel):
def __init__(self, master = None):
Toplevel.__init__(self, master)
self.master = master
self.Window()
self.denary = 0
def Window(self):
self.master.title('Unit Converter: Hexadecimal to Binary')
instructionHex = Label(self, text = 'Hexadecimal Value: ')
instructionHex.grid(row = 1, column = 0, padx = 10)
instructionBin = Label(self, text = 'Binary Value: ')
instructionBin.grid(row = 2, column = 0, padx = 10)
self.hexadecimalEntry = Entry(self)
self.hexadecimalEntry.grid(row = 1, column = 2, padx = 10, pady = 5)
convertButton = Button(self, text = 'Convert!', command = self.convert)
convertButton.grid(row = 3, column = 1)
def convert(self):
# I need to call the 'highestPowerOf2' method here.
pass
root = Tk()
unitConverter = Menu(root) # root is master
root.mainloop()

Python - Tkinter empty window

I'm doing an assignment for a course and I'm not really sure what's up with the code but it runs without error, only displaying an empty window. I used an example given as a start and basically modified it to get this code. If needed I can provide the example code to compare.
from Tkinter import *
class App(Tk):
def __init(self):
Tk.__init__(self)
self.height()
self.weigh()
self.calculate()
self.output()
def height(self):
Label(self, text = "Enter Height, feet").grid()
self.feet = Entry(self)
self.feet.grid(row = 0, column = 1)
self.feet.insert(0, "100")
lblinches = Label(self, text = "Enter Height, inches")
lblinches.grid(row = 1, column = 0)
self.inches = Entry(self)
self.inches.grid(row = 1, column = 1)
def weigh(self):
Label(self, text = "Enter Weight").grid(row =2, column = 0)
self.weight = Entry(self)
self.weight.grid(row = 2, column = 1)
def output(self):
self.calcBMI = Button(self, text = "Calculate BMI")
self.calcBMI.grid()
self.calcBMI["command"] = self.calculate
Label(self, text = "Body Mass Index").grid(row = 4)
Label(self, text = "Status").grid(row = 5)
def calculate(self):
feet1 = int(self.feet.get())
inches1 = int(self.inches.get())
height1 = feet1 *12 + inches1
weight1 = int(self.weight.get())
bmi = (weight1 * 703) / (height1 * 2)
self.lblbmi["text"] = ".2f" % bmi
def main():
app = App()
app.mainloop()
main()
__init should be __init__. Since __init__ was not defined, none of the configuration methods were called.

when I initiate second button click -> AttributeError: Application instance has no attribute 'readfile'

from Tkinter import *
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid() #put it on the grid
self.create_widgets()
#widget
def create_widgets(self):
self.button1 = Button(self)
self.button1.grid(row = 3, column = 5, columnspan = 2, stick = E)
self.button1["text"] = "Browse"
self.button1["command"] = self.update_count
self.pack()
#here
self.text1 = Text(self)
self.text1 = Text(self, width = 41, height = 1, wrap = WORD)
self.text1.grid(row = 3, column = 1, columnspan = 2, stick = W)
# here
self.button2 = Button(self)
self.button2.grid(row = 5, column = 5, columnspan = 2, stick = E)
self.pack()
self.button2["text"] = "OK"
self.button2["command"] = self.readfile
# select list box
self.listbox1 = Listbox(self, selectmode = EXTENDED)
self.listbox1 = Listbox(self, width = 51, height = 5)
self.listbox1.grid(row = 5, column = 1, columnspan = 2, stick = W)
self.label1 = Label(self)
self.label1.grid(row = 1, column = 1, columnspan = 2, sticky = W)
self.label1["text"] = "Choose the data log file folder ..."
self.label2 = Label(self)
self.label2.grid(row = 4, column = 1, columnspan = 2, sticky = W)
self.label2["text"] = "Choose the data log file ..."
#on click
def update_count(self):
path_string = tkFileDialog.askdirectory()
self.text1.delete(0.0, END)
self.text1.insert(0.0, path_string)
# read files
onlyfiles = [ f for f in listdir(path_string) if isfile(join(path_string,f)) ]
with open("output.txt", "w") as a:
for path, subdirs, files in os.walk(path_string):
for filename in files:
ffnames = os.path.join(filename)
self.listbox1.insert(END, ffnames)
a.write(str(ffnames) + os.linesep)
#on click
def readfile(self):
global listEx
items = map(int, listbox1.curselection())
result = listEx[items[0]]
print result
# main loop
root = Tk()
root.title("Test Window")
root.geometry("900x700")
app = Application(root)
root.mainloop() #main loop
Thanks in advance,
Can anyone help me to fix this error, I have got two button, first button works, now I need to read a file name from the listbox so have declared a constructor to do the work, eventually I got an AttributeError, don't know why?
I am new to Python, as a beginner struggling to fix, please help me.
Ramas
The error is telling you exactly the problem -- your class has no method named readfile. It looks like you have indented the method incorrectly, causing it to be private to the update_count method. The solution is to remove one level of indentation to the function definition, so that it is in the proper scope.

python: Taking an Tkinter entry value

I've been trying to get an entry value (the S1 in the code) to set itself as a value (STR in the _attributes dictionary), and I just can't get it to work. I want to make this an eventual toploop, but am going a step at a time on this, as I'm new to programming in general. Am I going about this the right way, or should I just have a button that, when pressed, does a lookup on the entry value at that time and goes with it, instead? I've gone through several tutorials and lessons I've found online for Tkinter, but still seem to be miles away from being able to make anything work the way I expect it to.
#! usr/bin/python27
from Tkinter import *
class Character:
def __init__(self, **kvargs):
self._attributes = kvargs
def set_attributes(self, key, value):
self._attributes[key] = value
return
def get_attributes(self, key):
return self._attributes.get(key, None)
def attrInput(stat, x, y):
"""Creates a label for entry box"""
L = Label(B,
width = 5,
relief = RIDGE,
anchor = E,
text = stat).grid(row = x,
column = y)
B = ""
def main():
Person = Character()
B = Tk()
S1 = Entry(B, width = 3)
S1.grid(row = 0, column = 1)
S1.bind("<Key>", Person.set_attributes('STR', S1.get()) )
attrInput("Str: ", 0, 0)
Button(B, text='Quit', command=B.destroy).grid(row=3, column=0, sticky=W, pady=4)
B.mainloop()
print Person.__dict__
if __name__ == '__main__': main()
new code (seems to be working, I'm getting what I want out of it, at least). I'll have to modify it slightly to make it a toploop, but here's the foundation
class Character:
def __init__(self, **kvargs):
self._attribute = kvargs
def set_attribute(self, key, value):
self._attribute[key] = value
return
def get_attribute(self, key):
return self._attribute.get(key, None)
class attrAsk:
def __init__(self, master, Char, attrName, Row, Column):
self.Char = Char
self.attrName = attrName
attrInput(attrName+":", Row, Column)
self.e = Entry(master, width = 3)
self.e.grid(row = Row, column = Column+1)
self.e.bind("<KeyRelease>", self.set_attr)
def set_attr(self, event):
self.Char.set_attribute(self.attrName, self.e.get())
def attrInput(stat, x, y):
"""Creates a label for entry box"""
L = Label(box,
width = 5,
relief = RIDGE,
anchor = E,
text = stat).grid(row = x,
column = y)
Person= Character()
box = Tk()
STRENT = attrAsk(box, Person, "STR", 0, 0)
DEXENT = attrAsk(box, Person, "DEX", 1, 0)
CONENT = attrAsk(box, Person, "CON", 2, 0)
INTENT = attrAsk(box, Person, "INT", 3, 0)
WISENT = attrAsk(box, Person, "WIS", 4, 0)
CHAENT = attrAsk(box, Person, "CHA", 5, 0)
Button(box,
text='Continue',
command=box.destroy).grid(columnspan = 2,
row=8,
column=0,
sticky=W,
pady=4)
box.mainloop()
print Person.__dict__
Change the line:
S1.bind("<Key>", Person.set_attributes('STR', S1.get()) )
to something like:
def key_pressed(event):
Person.set_attributes('STR', S1.get())
S1.bind("<KeyRelease>", key_pressed)
There are two reasons the original code doesn't work:
bind takes a function as its second argument- that function is then called when the event occurs. The expression Person.set_attributes('STR', S1.get()) as you use it, however, just happens immediately. You need to put that expression into a function so that it happens only when the key is pressed.
<Key> means the event occurs when the key is first pressed, but you would rather it happen when the key is released (and therefore the new character has been added). You thus want to use <KeyRelease>.
One other note: it would be a good idea to organize all your functionality, especially the callback methods, into a class. For example:
class Window(object):
def __init__(self):
self.person = Character()
self.B = Tk()
self.S1 = Entry(B, width = 3)
self.S1.grid(row = 0, column = 1)
self.S1.bind("<KeyRelease>", self.key_pressed)
attrInput("Str: ", 0, 0)
self.button = Button(B, text='Quit', command=self.B.destroy).grid(row=3, column=0, sticky=W, pady=4)
self.B.mainloop()
print self.person.__dict__
def key_pressed(self, event):
self.person.set_attributes('STR', self.S1.get())
def main():
w = Window()
if __name__ == '__main__': main()
The benefit of this organization might not be immediately apparent, but it becomes very useful once you have a large number of callback methods and are keeping track of a large number of widgets.
In response to your comment, you can create both the Entry and the Label objects in a for loop, each on its own row. The key_pressed method can then learn the field and the input text from the event object that gets passed to it, as seen here (try it):
class Window(object):
def __init__(self):
self.person = Character()
self.B = Tk()
self.fields = ["STR", "DEX", "CON", "INT", "WIS", "CHA"]
self.inputs = {}
for i, f in enumerate(self.fields):
self.inputs[f] = Entry(B, width = 3)
self.inputs[f].grid(row=i, column=1)
self.inputs[f].bind("<KeyRelease>", self.key_pressed)
attrInput(f + ":", i, 0)
self.button = Button(B, text='Quit', command=self.B.destroy).grid(row=7, column=0, sticky=W, pady=4)
self.B.mainloop()
print self.person.__dict__
def key_pressed(self, event):
field = self.fields[int(event.widget.grid_info()["row"])]
self.person.set_attributes(field, event.widget.get())

Categories