I've built a small program with a Tkinter GUI to let me enter 2 lines of text and make it display it on the character screen. It all works pretty neatly until I press apply text, because then I just seem to get a weird value on both lines of the LCD.
e.g.
Wanted line 1: "Test"
Wanted line 2: "Please work"
Actual result
Line 1: .3047332040L.304
Line 2: 7332320L
This is my code:
__author__ = 'David'
from Tkinter import *
from Adafruit_CharLCD import Adafruit_CharLCD
from time import sleep
import psutil
chargui = Tk()
lcd = Adafruit_CharLCD()
lcd.begin(16, 1)
class FrameWork:
def __init__(self, master):
frame = Frame(master)
frame.pack()
# Creation
self.lbl_enter1 = Label(frame, text="Enter the first line:")
self.lbl_enter2 = Label(frame, text="Enter the second line:")
self.ent_line1 = Entry(frame)
self.ent_line2 = Entry(frame)
self.btn_apply = Button(frame, text="Apply Text", command=self.applymessage)
self.btn_cpum = Button(frame, text="CPUMem", command=self.CPUMem)
self.btn_quit = Button(frame, text="Quit", command=frame.master.destroy)
# Griding
self.lbl_enter1.grid(row=0, column=0, sticky=E, padx=2)
self.lbl_enter2.grid(row=1, column=0, sticky=E, padx=2)
self.ent_line1.grid(row=0, column=1, sticky=W)
self.ent_line2.grid(row=1, column=1, sticky=W)
self.btn_apply.grid(row=2, column=1, sticky=W, padx=24)
self.btn_cpum.grid(row=2, column=0, columnspan=2, sticky=W, padx=85)
self.btn_quit.grid(row=2, column=1, sticky=E)
def applymessage(self):
lcd.clear()
lcd.message(str(self.ent_line1))
lcd.message(str(self.ent_line2))
def CPUMem(self):
while 1:
lcd.clear()
lcd.message("CPU: " + str(psutil.cpu_percent()) + "%\n")
lcd.message("MEM: " + str(psutil.virtual_memory().percent) + "%")
sleep(1)
g = FrameWork(chargui)
chargui.mainloop()
Don't mind the CPUMem function. This function works nicely.
It's just applymessage(self): that gives me trouble. I get no error at all. If I remove srt() from the 2 lcd.message functions though, it says it can't concatenate a string with an int.
Any solutions?
Edit:
I tried to just print the value to the console instead of putting it onto the LCD, and it still gives me the weird values (are they memory locations? wild guess) for both lines
Line 1: .3047815368L.3047815608L
Line 2: .3047815368L.3047815648L
As you found, the LCD has nothing to do with it. The problem is trying to convert a Tkinter Entry object into a str:
str(self.ent_line1)
calls a special method, self.ent_line1.__str__() to get the string representation of the object (as does print). There is no expectation that __str__ is defined to do something useful.
Actually, investigating using the interactive shell, you can find that this special method is defined in a parent class, and it's docstring is "Return the window path name of this widget." That's what you're seeing.
What you actually want, the text typed into the widget as a string, is given by get():
print self.ent_line1.get()
EDIT: Hm, it appears that you don't need to couple widgets to variables at all, as per the other answer to this question. The few times I've used TK that's the way I've always done it, but calling get() directly on the widget is probably certainly easier in this instance. I'm leaving this answer up for additional background.
I'm far from an expert in tkinter but unless someone more knowledgeable replies, I'll do my best.
The problem is that you're attempting to print the widget objects themselves, but the API doesn't work quite like that. If you read through the documentation for the Entry widget, you'll see that you need to associate a StringVar instance with it. This page has some more details, and there's a section in the Python docs too.
So, you'll need to do something like this when building your Entry wigets:
self.ent_line1_text = StringVar()
self.ent_line2_text = StringVar()
self.ent_line1 = Entry(frame, textvariable=self.ent_line1_text)
self.ent_line2 = Entry(frame, textvariable=self.ent_line2_text)
And then your applymessage() would look something like:
def applymessage(self):
lcd.clear()
lcd.message(self.ent_line1.get())
lcd.message(self.ent_line2.get())
As well as the get() method to retrieve the current contents of the entry box, there's also set() if you need to change it programmatically (e.g. to initialise the text boxes with some default text).
Related
In my code I have two .get functions referring to filled tkinter cells. They are entirely identical as far as I can tell. However, new_comp_name.get() works perfectly while new_comp_email.get() returns an empty value. A couple hours I give up and I thought I could get some help regarding this here. The code below is simplified but running it, I still encounter the same bizzar issue. I even resorted to restarting my computer but still, no luck. Any help would be much appreciated.
def newc_popup():
compviewFresh()
newc_popup = Toplevel()
newc_popup.title("New Company")
#-----------fetch options list from types DB
connection = sqlite3.connect('companyDB99.db')
###### end of connection ####
query="SELECT type_name as class FROM types"
r_set=connection.execute(query);
my_list = [r for r, in r_set] # create a list
options = tk.StringVar(newc_popup)
comptypeSELECT =tk.OptionMenu(newc_popup, options, *my_list)
#om1.grid(row=2,column=5)
#-----------
comp_name_label = Label(newc_popup, text="Company Name")
comp_name_label.grid(row=1, column=0)
new_comp_name = Entry(newc_popup, width=50)
new_comp_name.grid(row=1, column=1)
comp_email_label = Label(newc_popup, text="Email Address")
comp_email_label.grid(row=2, column=0)
new_comp_email = Entry(newc_popup, width=50)
new_comp_email.grid(row=2, column=1)
comptypeSELECT_lable = Entry(newc_popup, width=50)
comptypeSELECT_lable.grid(row=2, column=1)
comptypeSELECT.grid(row=3, column=1,)
def addComp():
compviewFresh()
connection = sqlite3.connect('companyDB99.db')
cursor = connection.cursor()
print(new_comp_name.get())
print(new_comp_email.get())
addComp_btn = Button(newc_popup, text="Add Company", command=addComp)
addComp_btn.grid(row=4, column=0, columnspan=2)
Your call to .get is working fine. The problem is that you have two entries in the same place so you're not typing into the widget you think you're typing into.
Here's the problem:
new_comp_email.grid(row=2, column=1)
comptypeSELECT_lable.grid(row=2, column=1)
comptypeSELECT_label is an Entry, so when you think you are typing into new_comp_email you're actually typing into comptypeSELECT_label since it was added last and thus is on top of new_comp_email. Thus, new_comp_email is empty.
A have a Grid of 38 buttons (num0-num38).
I'm trying to pass the button attribute (text) to the print function.
Code:
def collect_num(num):
print(num)
num0 = tk.Button(buttons, text="0", padx=10, pady=5, fg="white", bg="green", command=collect_num(thisItem.text))
Is there something similar to thisItem.text in Python?
So basically, I would like to print the button name (text="0" - text="38") when the correspondent button is pressed.
Any help with that is appreciated.
Since you asked for an example, take a look here:
from tkinter import *
root = Tk()
MAX_BUTTON = 25 # Maximum number of buttons to be made
def collect_num(num):
print(num) # Print the number
btns = [] # A list to add all the buttons, to make them reusable later
for i in range(MAX_BUTTON): # Looping 25 times or maximum number of buttons, times
btns.append(Button(root,text=i,width=10)) # Creating a button and adding it to list
btns[-1]['command'] = lambda x=btns[-1].cget('text'): collect_num(x) # Changing the command of the button
btns[-1].pack() # Packing it on
root.mainloop()
Here cget() returns the current properties of the widget.
It is highly recommended to do this, instead of creating buttons one by one manually. There is a principle in software engineering called DRY which means Don't Repeat Yourself, which usually means if your repeating yourself, there will possibly be a way to not repeat your self and reduce the code written. And this looping and creating a widget and using lambda to make the command is quiet used frequently when needed to make more than one widget with a pattern.
you need pass the button text using partial(<function>, *args) function
from functools import partial
def collect_num(num):
print(num)
# create the button first and add some specs like `text`
button = tk.Button(
buttons, text="0", padx=10, pady=5, fg="white", bg="green"
)
# add command after creation with the above function and button's name
button.config(command=partial(collect_num, button["text"]))
with the help of this stack question:
text = button["text"]
this is the method with which you can get the text.
or
text = button.cget('text')
or (with the help of #TheLizzard)
text = button.config("text")[-1]
I am trying to give my code a gui but I don't know how to use inputs with tkinter text boxes.
The code I am trying to give a gui:
phrase = input("ENTER TEXT ")
print("YOU ENTERED " + phrase + " NUMBER OF LETTERS: ")
print(len(phrase))
print("CONVERTED TO LOWER CASE:")
print(phrase.lower())
print("CONVERTED TO UPPER CASE:")
print(phrase.upper())
def say_hi():
print("BYE USER")
input('Press ENTER to exit')
say_hi()
The question is a little confusing, but this will open a message with an entry box.
import tkSimpleDialog
answer = tkSimpleDialog.askstring(title, message)
The 'title' parameter is simply the title of the message box, the text at the top.
The 'message' parameter is the message inside of the message box, such as 'Input your name'.
This is easier than other methods, as you do not need to setup a box, and position all the elements. However, if you need anything but a dialog box with an entry, you may want to try one of the other answers. :)
You can then use 'answer' however you need, exactly how you would with input()
~ Hope this helped! :D
The following code perform exactly what you do using a tkinter GUI.
Here is what you need to understand :
Label is used to create a paragraph.
Entry is a text input widget.
Entry has a get method to extract the tiped text.
To overwrite existing text in an Entry, you use it's method delete. You have to specify a range to delete.
To insert text in an Entry, you use it's method insert. You have to specify the position where the text will be inserted.
Code
import tkinter as tk
class YourGUI(tk.Tk):
def __init__(self):
# inherit tkinter's window methods
tk.Tk.__init__(self)
tk.Label(self, text="ENTER TEXT:")\
.grid(row=0, column=0)
self.input = tk.Entry(self)
self.input.grid(row=0, column=1)
tk.Label(self, text="CONVERTED TO LOWER CASE:")\
.grid(row=1, column=0)
self.result_lower = tk.Entry(self)
self.result_lower.grid(row=1, column=1)
tk.Label(self, text="CONVERTED TO UPPER CASE:")\
.grid(row=2, column=0)
self.result_upper = tk.Entry(self)
self.result_upper.grid(row=2, column=1)
tk.Button(self, text="convert", command=self.do_conversion)\
.grid(row=3, column=0, columnspan=2)
def do_conversion(self):
self.result_lower.delete(0, tk.END)
self.result_upper.delete(0, tk.END)
phrase = self.input.get()
self.result_lower.insert(0, phrase.lower())
self.result_upper.insert(0, phrase.upper())
if __name__ == '__main__':
your_gui = YourGUI()
your_gui.mainloop()
You should take a look at some tkinter tutorial like the following
https://likegeeks.com/python-gui-examples-tkinter-tutorial/
You need to go through Tkinter manual to see what kind of layout would be suitable for you. For above mentioned requirements, try this:
First import the library: import tkinter as tk
Then outside your main function/method/class: root.mainloop()
You can use tkinter grid layout. Try some example here: https://www.python-course.eu/tkinter_layout_management.php
Then:
phraseLabel = Label(window, text = "ENTER TEXT").grid(row=1,column=1) #creates label
phraseEntry = Entry(window, textvariable=phraseValue).grid(row=2,column=1) #create input text field
To retrieve the value from the text field use: phraseValue.get()
Best wishes
I am trying to create a window with a line label, an entry field, a current value label, and an "Update Value" button.
Here is an example:
This is what I have so far. I can get the entered value to print to console, but I can't seem to work out how to get an entered value and change the currentValue Label to reflect that value by pressing the button:
from tkinter import*
main=Tk()
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
#This kills the program
def exitProgram():
exit()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry=Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue)
currentValue.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValues=Button(text='Set Values',width=30,command=setValues)
setValues.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton=Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
There are a couple of problems with your code.
Firstly, you are overwriting the setValues function with the setValues Button widget, and similarly, you are overwriting the currentValue StringVar with the currentValue Label.
To set a StringVar, you use its .set method.
Don't use plain exit in a script, that's only meant to be used in an interactive interpreter session, the proper exit function is sys.exit. However, in a Tkinter program you can just call the .destroy method of the root window.
Here's a repaired version of your code.
import tkinter as tk
main = tk.Tk()
#StringVar for currentValue in R0C2
currentValue = tk.StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
currentValue.set(content)
#This kills the program
def exitProgram():
main.destroy()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
tk.Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry = tk.Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValueLabel = tk.Label(textvariable=currentValue)
currentValueLabel.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValuesButton = tk.Button(text='Set Values',width=30,command=setValues)
setValuesButton.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton = tk.Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
BTW, it's a Good Idea to avoid "star" imports. Doing from tkinter import * dumps 130 names into your namespace, which is unnecessary and creates the possibility of name collisions, especially if you do star imports from several modules. It also makes the code less readable, since the reader has remember which names you defined and which ones came from the imported module(s).
In my opinion the easiest way to do this would be using an object orientated method. This way you could declare a button with a command that calls a def which runs self.label.configure(text=self.entry.get()).
This can be seen below:
import tkinter as tk
class App:
def __init__(self, master):
self.master = master
self.label = tk.Label(self.master)
self.entry = tk.Entry(self.master)
self.button = tk.Button(self.master, text="Ok", command=self.command)
self.label.pack()
self.entry.pack()
self.button.pack()
def command(self):
self.label.configure(text=self.entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
The above creates a label, entry and button. The button has a command which calls a def within the class App and updates the value of the label to be the text contained within the entry.
This all works very smoothly and cleanly and more importantly is drastically easier (in my opinion) to read and update in the future.
From your code you are setting the 'currentValue', which is a StringVar:
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
to an object Label further down in your code. You cannot do this!
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue) ** this line is wrong
currentValue.grid(row=0,column=2)
You should name the label something different like:
#Displays what the value is currently set to.
lblCurrentValue = Label(textvariable=currentValue)
lblCurrentValue.grid(row=0,column=2)
Then in your "setValues" method you should use 'StringVar.set(value) to update the label like so:
def setValues():
content = entry.get()
currentValue.set(entry.get())------------------Here I set the value to the entry box value
print(content)
I tend to avoid stringVar and just use:
Label.config(text='*label's text*')
If you need more help I can post you my solution but try and solve it first becasue its the best way to learn. My tip is to make sure you are using correct naming conventions. In tkinter I tend to use lbl..., entryBox... etc before widgets so I know what they are and not to confuse them with variables.
I would like to know how (If Possible) to listen to a certain phrase or word that is entered in a text box and the run a command.
For instance if i type the phrase "turn me red" i would like to know if it is possible to turn it red without pressing enter.
I just started and here is what i have:
from Tkinter import *
class mywidgets:
def __init__(self,root):
frame=Frame(root)
frame.pack()
self.txtfr(frame)
return
def txtfr(self,frame):
#define a new frame and put a text area in it
textfr=Frame(frame)
self.text=Text(textfr,height=10,width=50,background='white')
# put a scroll bar in the frame
scroll=Scrollbar(textfr)
self.text.configure(yscrollcommand=scroll.set)
#pack everything
self.text.pack(side=LEFT)
scroll.pack(side=RIGHT,fill=Y)
textfr.pack(side=TOP)
return
def main():
root = Tk()
s=mywidgets(root)
root.title('textarea')
root.mainloop()
main()
So i thought it would be a little cleaner if rather than edit your code, i just provided a fresh example of working code that exhibits the behavior you are interested in.
Here's what the code below does: when you run it, you get a little widget with an empty text box (technically, a Label in Tkinter) for the user to supply some value. When they enter a numeric value (integer or float) and then click the Calculate button then equivalent value in meters appears just below. If however, the user keys in 'red' then the word 'blue' appears as soon as it is entered--i.e., Blue will appear even though the Calculate button nor anything else was clicked.
As you can see in the penultimate line below, getting the behavior you want is just a matter of describing the behavior you want in the Tkinter event syntax.
from Tkinter import *
import ttk
root = Tk()
def calculate(*args) :
value = float(feet.get())
meters.set((0.305 * value * 10000. + .5)/10000.)
def callback_function(*args) :
meters.set('blue')
mf = ttk.Frame(root, padding="3 3 12 12")
mf.grid(column=0, row=0, sticky=(N, W, E, S))
mf.columnconfigure(0, weight=1)
mf.rowconfigure(0, weight=1)
feet = StringVar()
meters = StringVar()
feet_entry = ttk.Entry(mf, width=7, textvariable=feet)
feet_entry.grid(column=2, row=1, sticky=(W, E))
ttk.Label(mf, textvariable=meters, background='#E9D66B').grid(column=2,
row=2, sticky=(W, E))
ttk.Button(mf, text="Calculate", command=calculate).grid(column=2,row=3,
sticky=W)
ttk.Label(mf, text="feet").grid(column=3, row=1, sticky=W)
ttk.Label(mf, text="is equivalent to").grid(column=1, row=2, sticky=E)
ttk.Label(mf, text="meters").grid(column=3, row=2, sticky=W)
for child in mf.winfo_children():
child.grid_configure(padx=5, pady=5)
feet_entry.focus()
root.bind('<Return>', calculate)
# this is the key line
root.bind('red', callback_function)
root.mainloop()
What you want is certainly possible. The solution depends on what you really want.
Do you want to turn something red only if the user types "turn me red" precisely? Or, if the text is "turn me blue" and they change the word "blue" to "red", will that trigger the action?
If the former (must type exactly "turn me red") you can just bind to that exact sequence (eg: widget.bind("<t><u><r><n><space><m><e>....", doSomething)). It becomes impossible to manage, however, if you also want "Turn ME Red" to do the very same thing.
If the latter (whenever you type anything it looks to see if "turn it red" surrounds the insertion point), it's a tiny bit more work. You can bind on <KeyRelease> and then look at the characters prior to the insertion point for the magic phrase.
Bottom line is, you set up a binding either on something generic like <KeyRelease> then make the decision in the callback, or set up a highly specific binding for an exact phrase.