In this simple example, I would like the label to be displayed as "1 3":
import tkinter
window = tkinter.Tk()
A = [1," ", 3]
label = tkinter.Label(window, text = A[0:])
label.pack()
window.mainloop()
However, python displays "1 { } 3" instead.
Where is the problem?
As jonathan and Ron Norris already said, you need to pass a string to the text argument of the Label constructor.
I would suggest to transform your heterogeneous list to a string thusly:
A = [1," ", 3]
labeltext = ''.join(map(str, A))
label = tkinter.Label(window, text = labeltext)
Documentation link for map: map documentation for python 3
Documentation for str: str documentation for python 3
Documentation for the join method of string: string join method, python 3
Edit: Since you now ask for a way to do this in many code locations, with minimal code changes, I would suggest this wrapper around tkinter.Label:
def createlabel(*args, **kwargs):
if 'text' in kwargs and isinstance(kwargs['text'], list):
kwargs['text'] = ''.join(map(str, kwargs['text']))
return tkinter.Label(*args, **kwargs)
This function will pass all of its arguments unchanged to tkinter.Label, but taking care to transform a text keyword argument to a string, if it is a list.
When you have defined this, use a "Find & Replace" function in your editor to replace calls to tkinter.Label with createlabel.
Your original could would be transformed like this, for example:
import tkinter
def createlabel(*args, **kwargs):
if 'text' in kwargs and isinstance(kwargs['text'], list):
kwargs['text'] = ''.join(map(str, kwargs['text']))
return tkinter.Label(*args, **kwargs)
window = tkinter.Tk()
A = [1," ", 3]
label = createlabel(window, text = A[0:])
label.pack()
window.mainloop()
A[0:] is a heterogeneous list, tkinter.Label's documentation suggests that its text parameter should receive a string (Which the word "text" also quite strongly suggests)
https://docs.python.org/3/library/tkinter.ttk.html#label-options
If you want "1 3", try:
A = [1," ", 3]
s = ''
for c in A:
s += str(c)
label = tkinter.Label(window, text = s)
Related
I got a problem with my code, here it is : I want to return the closest left and right child of a node, but whenever I try to, it returns me ALL the children of this node.
I separated the code in two files, one contains the binary tree class (not BST, just a simple BT), and the other contains tkinter things, that suppose to show the node and his children. See below for the code, and sorry for some of the parts in french, if u need words to be translated i'm up.
binary tree file :
class ArbreBinaire:
def __init__(self, valeur):
self.valeur = valeur
self.enfant_gauche = None
self.enfant_droit = None
def __str__(self):
return f"{self.valeur}, ({self.enfant_gauche}, {self.enfant_droit})"
[enter image description here](https://i.stack.imgur.com/KJ3Gr.png)
def insert_gauche(self, valeur):
if self.enfant_gauche == None:
self.enfant_gauche = ArbreBinaire(valeur)
else:
new_node = ArbreBinaire(valeur)
new_node.enfant_gauche = self.enfant_gauche
self.enfant_gauche = new_node
def insert_droit(self, valeur):
if self.enfant_droit == None:
self.enfant_droit = ArbreBinaire(valeur)
else:
new_node = ArbreBinaire(valeur)
new_node.enfant_droit = self.enfant_droit
self.enfant_droit = new_node
def get_valeur(self):
return self.valeur
def get_gauche(self):
return self.enfant_gauche
def get_droit(self):
return self.enfant_droit
tkinter and main parts file :
from arb import ArbreBinaire
from tkinter import *
def classe_arb():
global racine, b_node, f_node, c_node, g_node, h_node
arb = ArbreBinaire
racine = ArbreBinaire('A')
racine.insert_gauche('B')
racine.insert_droit('F')
b_node = racine.get_gauche()
b_node.insert_gauche('C')
b_node.insert_droit('D')
f_node = racine.get_droit()
f_node.insert_gauche('G')
f_node.insert_droit('H')
c_node = b_node.get_gauche()
c_node.insert_droit('E')
g_node = f_node.get_gauche()
g_node.insert_gauche('I')
h_node = f_node.get_droit()
h_node.insert_droit('J')
return arb
def accueil():
global fenetre
fenetre = Tk()
fenetre.title("Bienvenue dans l'énigme du manoir")
fenetre.geometry("1000x500")
bt_jouer= Button(fenetre, text="Jouer", fg="green", command=lambda: valeur_bouton(1))
bt_jouer.pack()
bt_quitter = Button(fenetre, text="Quitter", fg="red", command=quit)
bt_quitter.pack()
fenetre.mainloop()
def jeu(phrase, rep1, rep2):
global run
run = True
while run == True:
global wd_jeu
wd_jeu = Tk()
wd_jeu.title("L'énigme du manoir")
wd_jeu.geometry("1000x500")
label = Label(wd_jeu, text=phrase, bg = "blue", fg = "white", font = "Castellar")
label.pack()
option1 = Button(wd_jeu, text=rep1, fg="black")
option1.pack()
option2 = Button(wd_jeu, text=rep2, fg="black")
option2.pack()
wd_jeu.mainloop()
def valeur_bouton(nb):
if nb == 0:
None
if nb == 1:
fenetre.destroy()
jeu('Hello', racine.valeur, racine.enfant_gauche)
classe_arb()
accueil()
Okay, so basically the problem is in the last function : "racine.valeur" and "racine.enfant_gauche". The first one works very well, it returns me the node with no problem. The second one "racine.enfant_gauche" is supposed to return the closest left child of the A node (B in this case), but it prints me all the children : "B, (C, (None, E, (None, None)), D, (None, None))".
I tried many things like this, like getting the value with the method "get_gauche" but it doesnt work as well. Thanks in advance for your help.
The __str__ method is called recursively because the format string f"{self.valeur}, ({self.enfant_gauche}, {self.enfant_droit})" will use the __str__ method of each child to resolve the embedded text.
To avoid the recursion, you could define method to get values of the left and right (e.g. valeur_gauche(), valeur_droite()) and use those instead in the format string. You could also get the values within your __str__ function and assemble the resulting string accordingly.
The expression racine.enfant_gauche evaluates to an instance of ArbreBinaire, and so when you pass it as argument to jeu and pass it to the Button constructor, the node's __str__ method will be called to retrieve the string that must be printed. This __str__ method will create a string representation that includes all descendants.
The expression racine.valeur on the other hand is a string (a single letter in your example), and so when you pass that to jeu, which passes it to the Button constructor, it will render as that string (as expected).
If you want the same behaviour for the left child, you should also take the .valeur attribute, like so:
jeu('Hello', racine.valeur, racine.enfant_gauche.valeur)
the code below is part of a class.
def expected_callback(self,expected_val_var,index,mode):
dictionary[self.row][3] = expected_val_var
def dataType callback(self,event):
expected_val_var = StringVar()
expected_val_var.trace("w",self.expected_callback)
expected_val = Entry(self.root, width=20, textvariable= expected_val_var)
expected_val.insert(0,"Expected value")
expected_val.grid(row=self.row,column=self.col+2)
Im trying to get a text from a tkinter entry and put it inside a dictionary but I get PY_VAR(some number) instead.
I also tried dictionary[self.row][3] = expected_val_var.widget.get() but it said that str has no get(). What can I do to get the users input from expected_val entry into the dictionary?
It is because the first argument of the callback for .trace() is the internal name (string) of the tkinter variable.
If you want to pass expected_val_var (instance of StringVar) to expected_callback(), use lambda on .trace() instead:
def expected_callback(self, expected_val_var):
dictionary[self.row][3] = expected_val_var.get()
def dataType_callback(self, event):
...
expected_val_var.trace("w", lambda *args: self.expected_callback(expected_val_var)
...
not sure, shouldn't it be
dictionary[self.row][3] = expected_val.get()
but I would rename
expected_val
to something like
entry_field
for to be clearer that it is the input
element which value is to be red
and are you sure about
deff
, I know it as
def
I'm trying to reference a specific tkinter Label in a function based on an argument input. I've tried many things, and found some topics on exec and eval and variable variables, but I want to steer away from bad practices (I don't know how to achieve it through those methods anyway). I feel like I'm missing something extremely basic, but I can't wrap my head around it.
Below is a simplified code of my function:
def myFunction(input_num):
while ArbitraryVariableName == False:
# Do stuff in while loop
if input_num== "1":
self.lbl1["text"] = "New Value 1"
elif input_num == "2":
self.lbl2["text"] = "New Value 2"
elif input_num== "3":
self.lbl3["text"] = "New Value 3"
elif input_num== "4":
self.lbl4["text"] = "New Value 4"
elif input_num== "5":
self.lbl5["text"] = "New Value 5"
# And so forth for 20+ more elif statements
You will notice that the input_num directly relates to the specific tkinter Label name of "lbl + input_num". If it helps, below is the code for one of two of the labels (they all follow a similar pattern):
self.lbl1 = Label(topframe, text="Old Value Test 1")
self.lbl1 .grid(column=1, row=1)
self.lbl2 = Label(topframe, text="Old Value Test 2")
self.lbl2 .grid(column=1, row=2)
# And so forth
Is there a cleaner and less-repetitive way to do this?
You say that you don't want to have to use the eval function, so you could instead use a label list, which makes your code rather a lot shorter:
import tkinter as tk
class example:
def __init__(self, master):
self.master = master
self.lbl1 = tk.Label(self.master, text="Old Value Test 1")
self.lbl1.grid(column=0, row=0)
self.lbl2 = tk.Label(self.master, text="Old Value Test 2")
self.lbl2.grid(column=0, row=1)
self.lbls = [self.lbl1, self.lbl2]
self.myfunction(1)
self.myfunction(2)
def myfunction(self, input_num):
self.lbls[input_num - 1]["text"] = f"New Value {input_num}"
def main():
root = tk.Tk()
example_win = example(root)
root.mainloop()
if __name__ == '__main__':
main()
With this code I did assume you had an integer from the input_num variable, instead of the string you showed in your example.
If you aren't using Python 3 you can't take advantage of the f-string.
Hope this helps,
James
I've been having a lot of difficulties with this code, and I just can't find any solutions, I will post my code below.
from tkinter import *
a = []
class test(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title('testing')
self.pack(fill=BOTH, expand=1)
self.d = DoubleVar()
self.d.set('None')
def grab():
b = ent.get()
a.append(b)
c = [s.strip('qwertyuiopasdfghjklzxcvbnm') for s in a]
self.d.set(c[-1])
if c[-1] == '':
self.d.set('None')
ent = Entry(self)
ent.grid(row=0, column=0)
but = Button(self, text='Get', command=grab)
but.grid(row=1, column=0)
Label(self, textvariable=self.d).grid(row=2, column=0)
root = Tk()
app = test(root)
root.mainloop
I guess my objective is to be able to ignore, or delete the letters that are placed inside of the entry box, as you can see, I've used the strip method, but it doesn't work the way I would like it to. If anyone could offer some advice, or a code, or link me to a question that I overlooked, that would be amazing, and I would be greatful.
EDIT: It already clears letters before and after, but nothing in between
A validator would be the correct pattern to solve this problem. Interactively validating Entry widget content in tkinter has an implementation in tkinter that you should be able to use.
A little bit of lambda should do the trick
a = "123a3456b"
filter(lambda '0' <= x <= '9', a)
print a
"1233456"
You're getting letters inside the numbers because you're putting the string into a list first.
strip() removes leading and trailing characters, so if you have a string: aaa000bbb111ccc, stripping letters from it will only remove the outer-most letters. If you split the string, however, and then strip letters from each element of the stripped string, you'll effectively remove all the letters. Then, you can join() the remaining parts of the list together to get back to your string. Consider this example:
>>> import string # string.ascii_letters returns a string of all letters (upper and lower), just easier than typing them
>>> def check(x):
return ''.join([char.strip(string.ascii_letters) for char in x])
>>> var = 'aaa000bbb111ccc'
>>> var_as_list = [var]
>>> check(var)
'000111'
>>> check(var_as_list)
'000bbb111'
So, c should be:
c = ''.join([s.strip('qwertyuiopasdfghjklzxcvbnm') for s in b.get()])
You should also consider some further validation, if you want the field to only contain floats. Here's one method to trace any changes to a StringVar() instance and restrict changes to it to only being numbers and periods:
from tkinter import *
import string
def check(*args):
# make a 'whitelist' of allowable characters
whitelist = string.digits + '.'
# set var to its current value - any characters not in whitelist
var.set(''.join([i for i in var.get() if i in whitelist]))
root = Tk()
var = StringVar()
var.set('0.0')
Entry(root, textvariable=var).grid(row=0, column=0)
Label(root, textvariable=var).grid(row=1, column=0)
var.trace('w', check) # if var changes, call check()
mainloop()
a clean way to do this is simply:
filter(lambda s: not str.isalpha(s), data)
My program should check if the first three letters of the input word are similar to a predefined word.
I've made a GUI with Tkinter and want to get the letters of the input field.
Somehow I can't implement it in like I would do without Tkinter.
That's how I do it just for the shell:
text = raw_input('Enter a word: ')
if (text[0] + text[1] + text[2] == 'sag'):
print "sagen"
else:
print "error"
So, when I input the word "sagst" it checks the first three letters and should put out "sagen". Works fine.
I learned that e.g. inputfield.get() gets the input of the entry "inputfield".
But how can I check the first letters of that "inputfield"?
A small selection:
from Tkinter import*
root = Tk()
def check():
if (text[0] + text[1] + text[2] == 'sag'):
print "True"
else:
print "False"
inputfield = Entry(root)
inputfield.pack()
but = Button(root,text='Check!', command = check)
but.pack()
text = inputfield.get()
root.mainloop()
Does not work...
I hope you can understand my question and will answer soon. (Sorry for my bad english and my bad Python skills) ;-)
Thanks!
Your check function will have to retrieve the textfield after the button has been pressed:
def check():
text = inputfield.get()
print text.startswith('sag')
I've changed your test a little, using .startswith(), and directly printing the result of that test (print will turn boolean True or False into the matching string).
What happens in your code is that you define inputfield, retrieve it's contents (obviously empty), and only then show the TKInter GUI window by running the mainloop. The user never gets a chance to enter any text that way.
You can also check this without the need for a button (Now it will check whenever the user presses "Enter"):
from Tkinter import *
root = Tk()
def check(*event):
text = inputfield.get()
print text.startswith('sag')
inputfield = Entry(root)
inputfield.bind('<Return>',check)
inputfield.pack()
root.mainloop()
You can also do other things to have your widget validate the entry as you type. (The link is old, but it also points to newer features that allow you to do this without subclassing).
You're not actually putting the value in the input field into the text variable.
I renamed the value from text to input_text because it was confusing to me. I also changed from using text[0] + text[1] + text[2] to using startswith(). This will keep you from getting IndexErrors on short strings, and is much more pythonic.
from Tkinter import*
root = Tk()
def check():
input_text = inputfield.get()
if input_text.startswith('sag'):
print "True"
else:
print "False"
inputfield = Entry(root)
inputfield.pack()
input_text = inputfield.get()
print input_text # Note that this never prints a string, because it only prints once when the input is empty.
but = Button(root, text='Check!', command=check)
but.pack()
root.mainloop()
The key change is that the check function needs to actually get the value in the inputfield.
Here is a version which uses an Entry widget which validates its contents as the user types (so the user does not have to click a button or even press Return).
import Tkinter as tk
class MyApp(object):
'''
http://effbot.org/zone/tkinter-entry-validate.htm
http://effbot.org/tkinterbook/entry.htm
http://www.tcl.tk/man/tcl8.5/TkCmd/entry.htm#M-validate
'''
def __init__(self, master):
vcmd = (master.register(self.validate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(master, validate = 'key',
validatecommand = vcmd)
self.entry.pack()
self.entry.focus()
def validate(self, action, index, value_if_allowed,
prior_value, text, validation_type, trigger_type, widget_name):
dtype = {'0':'delete', '1':'insert', '-1':'other'}[action]
n = min(3, len(value_if_allowed))
valid = False
if dtype == 'insert':
if value_if_allowed[:n] == 'sag'[:n]: valid = True
else: valid = False
else: valid = True
print(valid)
return True
root = tk.Tk()
app = MyApp(root)
root.mainloop()