Can't get contents of an Entry widget using input() - python

I'm trying to write a simple python gui in windows 8.1 using python 3.4.2.
I try to make a program to calculate a concentration (molarity = moles / liter) but in the GUI I create No answer appears in the Text widget but numbers appear in the command shell.
The calculations also don't seem to work because when I left the entry empty something was calculated (which should be impossible, even if empty Entries would evaluate to 0 it shouldn't be able to divide by 0) and it gives me these numbers .56494480.56494448.
I think the problem is in this part
def mol(self):
moli = float(input(self.grammi)) / float(input(self.peso_molecolare))
self.text.delete(0.0, END)
self.text(0.0, moli)
def mola(self):
conc = float(float(input(self.grammi)/ float(input(self.peso_molecolare))) / float(input(self.litri))
self.text.delete(0.0, END)
self.text.insert(0.0, conc)
If you want the entire code here it is
from tkinter import *
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
self.instuction = Label(self, text="inserisci i seguenti dati")
self.instuction.grid(row=0, column=0, columnspan=2, sticky=W)
self.grammi = Entry(self)
self.grammi.label = Label(self, text="grammi")
self.grammi.grid(row=1, column=1, sticky=W)
self.grammi.label.grid(row=1, column=0, sticky=W)
self.peso_molecolare = Entry(self)
self.peso_molecolare.label = Label(self, text="peso molecolare")
self.peso_molecolare.grid(row=2, column=1, sticky=W)
self.peso_molecolare.label.grid(row=2, column=0, sticky=W)
self.litri = Entry(self, text="litri")
self.litri.label = Label(self, text="litri")
self.litri.grid(row=3, column=1, sticky=W)
self.litri.label.grid(row=3, column=0, sticky=W)
self.moli_button = Button(self, text="calcolo moli", command=self.mol)
self.moli_button.grid(row=2, column=2, sticky=W)
self.conc_button = Button(self, text="concentrazione", command=self.mola)
self.conc_button.grid(row=3, column=2, sticky=W)
self.exit_button = Button(self, text="Exit", command=self.close_window)
self.exit_button.grid(row=4, column=2, sticky=W)
self.text = Text(self, width=35, height=5, wrap=NONE)
self.text.grid(row=4, column=0, columnspan=2, sticky=W)
def mol(self):
moli = float(input(self.grammi)) / float(input(self.peso_molecolare))
self.text.delete('1.0', END)
self.text.insert('1.0', moli)
def mola(self):
conc = float(float(input(self.grammi)) / float(input(self.peso_molecolare))) / float(input(self.litri))
self.text.delete('1.0', END)
self.text.insert('1.0', conc)
def close_window(self):
root.destroy()
root = Tk()
root.title("chimica")
root.geometry("400x200")
app = Application(root)
root.mainloop()

With Tkinter, to get the value inserted in an Entry widget you shouldn't use input but you have to use the get method like:
moli = float(self.grammi.get()) / float(self.peso_molecolare.get())
same goes for conc:
conc = float(self.grammi.get()) / float(self.peso_molecolare.get()) / float(self.litri.get())
The problem you have is that input will prompt for user input in the command shell, after asking the question that is between the parentheses. However, you put a reference to an Entry widget there. So what is printed (.56494480 and .56494448) are internal references to these widgets, not results of any calculation.

I must assume that you are using Tkinter. It might be that line numbers start at 1 and you are inserting at line 0. Also, indexes are strings, not floats.
Try altering your code to:
def mol(self):
moli = float(input(self.grammi)) / float(input(self.peso_molecolare))
self.text.delete('1.0', END)
self.text.insert('1.0', moli)
def mola(self):
conc = float(float(input(self.grammi)) / float(input(self.peso_molecolare))) / float(input(self.litri))
self.text.delete('1.0', END)
self.text.insert('1.0', conc)
Or you could just use self.text.insert(INSERT, conc) which will insert at the current insertion point.

Related

Is it possible to resize an input box (entry) when clicked with tkinter?

Reading through other stackoverflow questions, and other sources I do see that bind can be used to call a function. Currently I'm working on a program that will communicate with a database (most likely mongodb), and so far I've set up a frame that has 2 inputs per row (key-value). I haven't completely decided whether I want one row per document, or one row per field. Right now, if a user has a lot to type then it wouldn't be ideal for them because you can't see everything you write. So what I was thinking is that, if the user clicks on the entry widget, then the box would become bigger and show them everything they have written. My current line of thinking is that maybe I could create another frame for it and somehow pass onto the information to that?
This is what it currently looks like
Then what I'd ideally want it to look like
Here's the code if interested how I made it (Images are from the "CreatePage" section):
from tkinter import *
import tkinter as tk
class Database_Project(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
stack_frame_container = tk.Frame(self)
stack_frame_container.grid_columnconfigure(0, weight=1)
stack_frame_container.grid_rowconfigure(0, weight=1)
stack_frame_container.pack(side="top", fill="both", expand=True)
self.frameslist = {}
for frame in (MainPage, CreatePage):
frame_occurrence = frame.__name__
active_frame = frame(parent=stack_frame_container, controller=self)
self.frameslist[frame_occurrence] = active_frame
active_frame.grid(row=0, column=0, sticky="snew")
self.current_frame("MainPage")
def current_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
active_frame.tkraise()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label_create = tk.Label(self, text="Create and insert data").grid(row=0, column=0, padx=50, pady=(50,0))
create_button = tk.Button(self, text="CREATE", command=lambda: controller.current_frame("CreatePage")).grid(row=1, column=0)
label_read = tk.Label(self, text="Query over data").grid(row=0, column=1, padx=50, pady=(50,0))
read_button = tk.Button(self, text="READ").grid(row=1, column=1)
label_update = tk.Label(self, text="Modify existing data").grid(row=2, column=0, padx=50, pady=(50,0))
update_button = tk.Button(self, text="UPDATE").grid(row=3, column=0, pady=(0,50))
label_delete = tk.Label(self, text="Remove data").grid(row=2, column=1, padx=50, pady=(50,0))
delete_button = tk.Button(self, text="DELETE").grid(row=3, column=1, pady=(0,50))
class CreatePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.inputlist = []
self.newinputlist = []
labels = [tk.Label(self, text="Enter unique field"), tk.Label(self, text="Enter corresponding the value/s")]
self.inputlist.append(labels[:])
for toplabels in range(1):
self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5)
self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5)
for entries in range(2):
for entrynum in range(0, 1):
print("column:", entries)
print("row", entrynum)
self.newinputlist.append(tk.Entry(self, borderwidth=5))
for x in range(len(self.newinputlist)):
self.newinputlist[x].grid(row=1, column=x, padx=10, pady=5)
self.inputlist.append(self.newinputlist[:])
button_input_1 = [tk.Button(self, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self, text="BACK", command=lambda: controller.current_frame("MainPage"))]
self.inputlist.append(button_input_1[:])
button_input_2 = [tk.Button(self, text="IMPORT FILE"), tk.Button(self, text="SUBMIT DATA")]
self.inputlist.append(button_input_2[:])
for button in range(len(self.inputlist) - 2, len(self.inputlist)):
self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5)
self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5)
def add_insert(self):
add_input = [tk.Entry(self, borderwidth=5), tk.Entry(self, borderwidth=5)]
self.inputlist.insert(-2, add_input)
self.newinputlist.append(add_input)
for widget in self.children.values():
widget.grid_forget()
for index, widgets in enumerate(self.inputlist):
widget_one = widgets[0]
widget_two = widgets[1]
print(str(index), widget_one, widget_two)
widget_one.grid(row=index, column=0, padx=10, pady=5)
widget_two.grid(row=index, column=1, padx=10)
if __name__ == "__main__":
NoSQL_Project = Database_Project()
NoSQL_Project.title("NoSQL Database Project")
NoSQL_Project.mainloop()
It's pointless to resize an Entry widget since they can only ever hold a single line. I'll give an example using the Text widget instead, though the technique works with any widget.
There's really no trick, just bind to <FocusIn> and <FocusOut>. In the following example I've created two Text widgets that have this resize behavior:
import tkinter as tk
def resizer(event):
if event.widget == event.widget.focus_get():
event.widget.configure(height=8)
else:
event.widget.configure(height=1)
root = tk.Tk()
root.geometry("400x200")
text1 = tk.Text(root, height=1, width=20)
text2 = tk.Text(root, height=1, width=20)
text1.pack(side="left")
text2.pack(side="right")
for widget in (text1, text2):
widget.bind("<FocusIn>", resizer)
widget.bind("<FocusOut>", resizer)
root.mainloop()
The actual behavior depends on how you've laid out your widget. This could cause widgets to jump around or the window resize, but every app will be different so it's hard to give a solution that works everywhere.

How can I close the current window and open a new window at the same time?

This code opens a menu, which links to another menu. The first button can't close itself and open a new one at the same time, How do I fix this?
import tkinter as tk
import tkinter.messagebox as box
class EnterRLE(tk.Tk):
def __init__(self):
super().__init__()
self.title('Enter RLE')
self.line_count_str = tk.StringVar()
self.compressed_data_str = tk.StringVar(self)
frame = tk.Frame(self)
tk.Label(self, text='Line Count:').pack(padx=15, pady=5)
tk.Entry(self, bd=5, textvariable=self.line_count_str).pack(padx=15, pady=5)
tk.Button(self, text="Next", width=5, command=self.line_count_func).pack(side='right', padx=5)
tk.Button(self, text='Exit', width=5, command=self.destroy).pack(side='right', padx=5)
frame.pack(padx=100, pady=19)
def line_count_func(self):
if self.line_count_str.get().isdigit():
if int(self.line_count_str.get()) < 3:
box.showinfo(title="Error", message="Enter a number over 3")
elif int(self.line_count_str.get()) > 1000000000:
box.showinfo(title="Error", message="Enter a number under 1,000,000,000")
else:
self.enter_rle_2()
def enter_rle_2(self):
top = tk.Toplevel(self)
top.title('Enter RLE')
frame = tk.Frame(top)
tk.Label(top, text='Compressed Data:').pack(padx=15, pady=5)
tk.Entry(top, bd=5, textvariable=self.compressed_data_str).pack(padx=15, pady=5)
tk.Button(top, text="Next").pack(side='right', padx=5)
frame.pack(padx=19)
EnterRle1().mainloop()
I have seen some people use a new "def" approach for this type of thing however i'm not sure how to adapt these to my code.
There are several problems here.
The 1st major problem is the use of Tk() twice. In this specific instance when the code is cleaned up it works fine but generally you never want to use more than one instance of Tk().
2nd major issue is how you are trying to destroy enter_rle in your linecount_button_clicked function. You cannot do this as the function does not know anything about enter_rle from a separate function. You need to pass it in your button command.
3rd major issue is int(linecount_STR.get()). This will error out if anything other than a number or an empty sting is grabbed by get. So your else clause will never happen as it will error before your if/else statement. So lets fix that with isdigit().
Next even though it does not hurt anything here you want to define your global's at the top of your function.
There are some PEP8 issues here that don't hurt the code but would make it easier to read if you clean them up.
Really this should probably be built in a class so we can use class attributes and methods to manage everything.
Here is your code cleaned up let me know if you have any questions.
from tkinter import *
import tkinter.messagebox as box
def enter_rle_1():
enter_rle = Tk()
linecount_STR = StringVar()
enter_rle.title('Enter RLE')
frame = Frame(enter_rle)
label_linecount = Label(enter_rle, text='Linecount:')
label_linecount.pack(padx=15, pady=5)
linecount = Entry(enter_rle, bd=5, textvariable=linecount_STR)
linecount.pack(padx=15, pady=5)
ok_button = Button(enter_rle, text="Next", width=5,
command=lambda lc=linecount_STR: linecount_button_clicked(enter_rle, lc))
ok_button.pack(side=RIGHT, padx=5)
stop = Button(enter_rle, text='Exit', width=5, command=enter_rle.destroy)
stop.pack(side=RIGHT, padx=5)
frame.pack(padx=100, pady=19)
enter_rle.mainloop()
def linecount_button_clicked(enter_rle, linecount_STR):
linecount = linecount_STR.get()
if linecount.isdigit(): # does nothing if value is not a digit.
if int(linecount) < 3:
box.showinfo(title="Error", message="Enter a number over 3")
elif int(linecount) > 1000000000:
box.showinfo(title="Error", message="Enter a number under 1,000,000,000")
else:
enter_rle_2(enter_rle)
def enter_rle_2(root):
enter_rle = Toplevel(root)
compressed_data_STR = StringVar(root)
enter_rle.title('Enter RLE')
frame = Frame(enter_rle)
label_compressed_data = Label(enter_rle, text='Compressed Data:')
label_compressed_data.pack(padx=15, pady=5)
compressed_data = Entry(enter_rle, bd=5, textvariable=compressed_data_STR)
compressed_data.pack(padx=15, pady=5)
ok_button = Button(enter_rle, text="Next")
ok_button.pack(side=RIGHT, padx=5)
frame.pack(padx=100, pady=19)
enter_rle_1()
Here is an OOP version of your code using class attributes and methods. This is much easier to manage this kind of interaction in Tkinter and no global's will ever be needed.
import tkinter as tk
import tkinter.messagebox as box
class EnterRle1(tk.Tk):
def __init__(self):
super().__init__()
self.title('Enter RLE')
self.line_count_str = tk.StringVar()
self.compressed_data_str = tk.StringVar(self)
frame = tk.Frame(self)
tk.Label(self, text='Line Count:').pack(padx=15, pady=5)
tk.Entry(self, bd=5, textvariable=self.line_count_str).pack(padx=15, pady=5)
tk.Button(self, text="Next", width=5, command=self.line_count_func).pack(side='right', padx=5)
tk.Button(self, text='Exit', width=5, command=self.destroy).pack(side='right', padx=5)
frame.pack(padx=100, pady=19)
def line_count_func(self):
if self.line_count_str.get().isdigit():
if int(self.line_count_str.get()) < 3:
box.showinfo(title="Error", message="Enter a number over 3")
elif int(self.line_count_str.get()) > 1000000000:
box.showinfo(title="Error", message="Enter a number under 1,000,000,000")
else:
self.enter_rle_2()
def enter_rle_2(self):
top = tk.Toplevel(self)
top.title('Enter RLE')
frame = tk.Frame(top)
tk.Label(top, text='Compressed Data:').pack(padx=15, pady=5)
tk.Entry(top, bd=5, textvariable=self.compressed_data_str).pack(padx=15, pady=5)
tk.Button(top, text="Next").pack(side='right', padx=5)
frame.pack(padx=100, pady=19)
EnterRle1().mainloop()

python tkinter entry input divided by label input from optionMenu

I need a bit off help..
In the example below I have two optionsMenus, two entries, and some labels.
What I'm trying to do, is to divide my input from the entry by the labels value, choosen from the optionsMenu, and then show the new value in the next column. But I'm a bit stuck now and can't get it to work.
from tkinter import *
class App(Frame):
def __init__(self, root=None):
Frame.__init__(self, root)
self.materialPrice = {'Brick': 70, 'Rockwool': 50, 'Concrete': 20}
materialvariable1 = StringVar(self, root)
materialvariable1.set("Choose material")
materialvariable2 = StringVar(self, root)
materialvariable2.set("Choose materiale")
self.w1 = OptionMenu(root, materialvariable1, *self.materialPrice, command=self.displayPrice).grid(row=2,
column=0,
columnspan=1,
sticky='WE')
self.w2 = OptionMenu(root, materialvariable2, *self.materialPrice, command=self.displayPrice2).grid(row=3,
column=0,
columnspan=1,
sticky='WE')
self.var = IntVar()
self.var.set(float(0.00))
self.var2 = IntVar()
self.var2.set(float(0.00))
self.entry1 = Entry(root, textvariable=self.var).grid(row=2, column=1)
self.entry2 = Entry(root, textvariable=self.var2).grid(row=3, column=1)
self.priceVarLabel1 = IntVar()
self.priceVarLabel1.set(float(0.00))
self.priceVarLabel2 = IntVar()
self.priceVarLabel2.set(float(0.00))
self.priceVarValue1 = Label(root, textvariable=self.priceVarLabel1, relief='sunken').grid(row=2,
column=2,
columnspan=1,
sticky='WE')
self.priceVarValue2 = Label(root, textvariable=self.priceVarLabel2, relief='sunken').grid(row=3,
column=2,
columnspan=1,
sticky='WE')
self.label1 = Label(root, textvariable=self.displayResult).grid(row=2, column=3)
self.label2 = Label(root, textvariable=self.displayResult2).grid(row=3, column=3)
def displayPrice(self, value):
self.priceVarLabel1.set(self.materialPrice[value])
def displayPrice2(self, value):
self.priceVarLabel2.set(self.materialPrice[value])
def displayResult(self):
self.label1.set(self.entry1 / self.priceVarValue1)
def displayResult2(self):
self.label1.set(self.entry1 / self.priceVarValue1)
root = Tk()
app = App(root)
root.title("help")
root.mainloop()
Just add the division to your function:
def displayPrice(self, value):
self.priceVarLabel1.set(self.materialPrice[value] / self.var.get())
You may want to change the starting value to 1 so that you don't get a ZeroDivisionError right off the bat.
BTW, initializing a widget and laying it out on the same line is a well known bug source. Always use 2 lines.
# very bad:
self.entry1 = Entry(root, textvariable=self.var).grid(row=2, column=1)
# good:
self.entry1 = Entry(root, textvariable=self.var)
self.entry1.grid(row=2, column=1)

Python/Tkinter: How to display a blank bottom frame when the application is run first?

When the application is run first, the last frame's widgets are displayed on the screen. What i wanted to do is, displaying the related frames when the user clicks their buttons. So, i want to display a blank frame with the top buttons. In order to do that, what should i do? (I removed the button functions, because they are not related to the question.) Thanks in advance.
import tkinter as tk
class TopFrame(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid(row=0, column=0, sticky="nsew")
self.BottomFrame = tk.Frame(master=master)
self.BottomFrame.grid(row=1, column=0, sticky="nsew")
self.f1 = tk.Frame(master=self.BottomFrame)
self.f2 = tk.Frame(master=self.BottomFrame)
self.f3 = tk.Frame(master=self.BottomFrame)
for f in (self.f1, self.f2, self.f3):
f.grid(row=0, column=0, sticky="nsew")
self.b1 = tk.Button(master=self, text="Add Words")
self.b2 = tk.Button(master=self, text="Add From File")
self.b3 = tk.Button(master=self, text="Change Words")
self.add_button = tk.Button(master=self.f1, text="Add")
self.open_button = tk.Button(master=self.f2, text="Open File")
self.change_button = tk.Button(master=self.f3, text="Change")
self.l1 = tk.Label(master=self.f1, text="English")
self.l2 = tk.Label(master=self.f1, text="Turkish")
self.l3 = tk.Label(master=self.f3, text="Old word")
self.l4 = tk.Label(master=self.f3, text="New word")
self.e1 = tk.Entry(master=self.f1)
self.e2 = tk.Entry(master=self.f1)
self.e3 = tk.Entry(master=self.f3)
self.e4 = tk.Entry(master=self.f3)
self.configure_buttons()
self.configure_labels()
self.configure_entries()
def configure_buttons(self):
self.b1.grid(row=0, column=0)
self.b1.configure(command=lambda: self.f1.tkraise())
self.b2.grid(row=0, column=1)
self.b2.configure(command=lambda: self.f2.tkraise())
self.b3.grid(row=0, column=2)
self.b3.configure(command=lambda: self.f3.tkraise())
self.add_button.grid(row=2, columnspan=2)
#self.add_button.configure(command=self.add_word)
self.open_button.pack(side="top")
#self.open_button.configure(command=self.add_from_file)
self.change_button.grid(row=2, columnspan=2)
def configure_labels(self):
self.l1.grid(row=0, column=0)
self.l2.grid(row=0, column=1)
self.l3.grid(row=0, column=0)
self.l4.grid(row=0, column=1)
def configure_entries(self):
self.e1.grid(row=1, column=0)
self.e2.grid(row=1, column=1)
self.e3.grid(row=1, column=0)
self.e4.grid(row=1, column=1)
if __name__ == "__main__":
root = tk.Tk()
example = TopFrame(master=root)
example.mainloop()
Instead of having 3 widgets in the same location, it's better to have only the one you need.
First, get rid of this code:
for f in (self.f1, self.f2, self.f3):
f.grid(row=0, column=0, sticky="nsew")
Now the frame will start in a blank state.
Then, instead of calling .tkraise() on the frames, we will remove the current frame (if any) and add another one in its place. So
self.b1.configure(command=lambda: self.f1.tkraise())
self.b2.configure(command=lambda: self.f2.tkraise())
self.b3.configure(command=lambda: self.f3.tkraise())
becomes:
self.b1.configure(command=lambda: self._activate(self.f1))
self.b2.configure(command=lambda: self._activate(self.f2))
self.b3.configure(command=lambda: self._activate(self.f3))
with
def _activate(self, frame):
# remove the current frame
for child in self.BottomFrame.winfo_children():
child.grid_forget()
# add the new frame in its place
frame.grid(row=0, column=0, sticky='nsew')

How to get input and give an output after calculation

I'm beginner for python and I tried to make a BMI calculator
but i have some problems with the input and output
I want to get input from self.Heighttypeand self.Weighttypeand give a output at self.BMI
And some tips to simply my coding?
from tkinter import *
import tkinter.messagebox
root = Tk()
root.resizable(0,0)
class win1:
def __init__(self, master):
self.master = master
master.title("BMI Calculator")
#
self.he = IntVar()
self.we = IntVar()
self.height = Label(master, text="ENTER Your Height(cm) Here:")
self.Heighttype = Entry(master, textvariable=self.he) #here
self.weight = Label(master,text="ENTER Your Weight(kg) Here:")
self.Weighttype = Entry(master, textvariable=self.we) #and here
#
self.ans = IntVar()
self.BMI = Label(master, textvariable=self.ans) #output here
self.credit = Button(master, text="Credit", command=self.credit_show)
self.result = Button(master, text="Result", command=self.calculation)
root.protocol('WM_DELETE_WINDOW', self.ask_quit)
self.close = Button(master, text="Close", command=self.ask_quit)
self.height.grid(sticky=E, padx=2, pady=4)
self.Heighttype.grid(row=0, column=1, columnspan=2, padx=2)
self.weight.grid(sticky=E, padx=2, pady=4)
self.Weighttype.grid(row=1, column=1, columnspan=2, padx=2)
self.BMI.grid(row=2, column=1, columnspan=2, padx=2)
self.credit.grid(row=3, sticky=W, padx=4 , pady=4)
self.result.grid(row=3, column=1, pady=4, sticky=W+E, padx=4)
self.close.grid(row=3, column=2, pady=4, sticky=W+E, padx=1)
def calculation(self):
# i want to get the user input from top and make calculation
# and make a output self.BMI = Label
def credit_show(self):
tkinter.messagebox.showinfo("Credit","Created by BlackLotus")
def ask_quit(self):
if tkinter.messagebox.askokcancel("Quit", "Do you want to Quit?"):
root.destroy()
apps = win1(root)
root.mainloop()
Someone help me please. Thanks a lot.
Use .get() and .set() on the IntVars to get the parameters and set the result:
def calculation(self):
m = self.we.get()
l = self.he.get()
bmi = # calculate the bmi here
self.ans.set(bmi)
Also, while it seems to work with an IntVar as well, it seems more reasonable to make ans a DoubleVar, i.e.:
self.ans = DoubleVar()

Categories