Why don't these tkinter programs behave identically? - python

I have 2 codes I made. The 2nd one is based off of the first one. Unfortunately for some reason the 2nd one doesn't work even though the first one does.
First code:
import time
from Tkinter import *
root = Tk()
t=StringVar()
num=1
t.set(str(num)
thelabel = Label(root, textvariable=t).pack()
def printnum (x):
while x<= 100:
t.set(str(x))
x += 1
root.update()
time.sleep(30)
printnum(num)
root.mainloop()
This code works like a charm. Here is the other one.
Second code:
#!/usr/bin/python
# -*- coding: latin-1 -*-
import Adafruit_DHT as dht
import time
from Tkinter import *
root = Tk()
k=StringVar()
num = 1
k.set(str(num))
thelabel = Label(root, textvariable=k).pack
def printnum(x):
while x <= 10000000000000:
h,t = dht.read_retry(dht.DHT22, 4)
newtext = "Temp%s*C Humidity=%s" %(t,h)
k.set(newtext)
x += 1
root.update
time.sleep(30)
printnum(num)
root.mainloop()
The code runs but it doesn't do anything, no window pops up like the other code does. Please help I cannot figure out how to fix this. Or why the first one works and the second one doesn't.

You're overwriting the previous value of t with the temperature from read_retry on this line:
h,t = dht.read_retry(dht.DHT22, 4)
Then, when you try to call set, t is now a float, so doesn't have a set method. Use a different variable name instead of t for one of them.

root.update doesn't do anything. You need to add ():
root.update()
That being said, your algorithm is the wrong way to run periodic tasks in tkinter. See https://stackoverflow.com/a/37681471/7432

Related

Getting real time data in Tkinter

Consider the code below:
from tkinter import *
screen = Tk()
e =Entry()
e.pack()
screen.mainloop()
Now how to get to display the length of the characters entered in the e entry widget in real-time? It doesn't matter if the data is displayed in the GUI or Corresponding terminal
There are atleast 3 ways to do this here with one being better than the other:
Using trace from StringVar:
def func(*args):
print(len(var.get()))
var = StringVar()
e = Entry(screen,textvariable=var)
e.pack()
var.trace('w',func)
Every time the value of var is changed, func will be called.
Using bind to each key release:
def func(*args):
print(len(e.get()))
e.bind('<KeyRelease>',func)
Using after(ms,func) to keep repeating the function:
def func():
print(len(e.get()))
screen.after(500,func)
func()
As you can see, the first method is more efficient as it does not unnecessarily prints out values when you select all the items(with Ctrl+A) and so on. Using after() will be the most ridiculous method as it will keep printing the length always as there are no restrictions provided.

How can i make this code do what it's supposed to do?

So, what I am trying to do is when you open the window, it starts a process in which every 0.2 seconds it changes the first and 3rd value of the color (in which it converts the elements of the range into a hex value and then a string) to go from rgb( 86, 32, 86) to rgb(126, 32, 126). Although I thought this might just work, it doesn't. I only get a background of the first color and that's all.
from tkinter import *
import time
root = Tk()
for i in range(86,126):
h = hex(i)
h = str(h)
h = h[2] + h[3]
root.configure(background=("#" + h + "32" + h ))
time.sleep(0.2)
root.mainloop()
You must use the after function to give the window system time to process updates. Calling window update functions in a loop like that on the main thread will lock up the window until the loop terminates.
Try moving the code in the for loop into a new function, say updateBackground, and making it call itself recursively using after:
def updateBackground(i):
# ...
if i < 126:
root.after(200, lambda: updateBackground(i + 1))
Note that I used a lambda in order to increment i.
Credit: https://stackoverflow.com/a/36670519/1757964
Your main issue with this code is the use of sleep(). Because Tkinter is a single thread application and is event driven what ends up happening when you use sleep() the entire Tkinter instance freezes.
To work around this Tkinter provides a method called After() that is designed to schedule an event to happen after a time. So to get the same affect you are trying to get with sleep we can instead create a function that can call itself after 0.2 sec and provide it with the starting number and ending number.
from tkinter import *
root = Tk()
def do_something(start_number, end_number):
if start_number <= end_number:
h = str(hex(start_number))[2:] # combined all your work on h to one line
root.configure(background=("#{}32{}".format(h, h)))
start_number += 1
root.after(200, do_something, start_number, end_number)
do_something(86, 126)
root.mainloop()
Note that the color change is mild and hard to see. If you increase the `end_number thought it will become more obvious.

Python Memory Game with Tkinter - Trouble with definitions and buttons

I am creating a memory game and am running into a problem with the buttons. I create the buttons using a for loop and place them into an array, but I have no idea how to call a definition OnButtonClick for each one of them. Each button should have a random picture, chosen from eight options, with no more than two duplicates.
from Tkinter import *
import Tkinter
import random
root = Tkinter.Tk()
poop=PhotoImage(file="poop.gif")
apple=PhotoImage(file="apple.gif")
earth=PhotoImage(file="earth.gif")
fish=PhotoImage(file="fish.gif")
frowny=PhotoImage(file="frowny.gif")
heart=PhotoImage(file="heart.gif")
smiley=PhotoImage(file="images.gif")
water=PhotoImage(file="water.gif")
back=PhotoImage(file="card back.gif")
images = [poop,apple,earth,fish,frowny,heart,smiley,water]
row = 1
column = 0
buttonList=[]
def OnButtonClick():
self.Button.config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back, width=150,height=250,command=OnButtonClick()))
buttonList[i].grid(row = row,column = column)
column += 1
if column == 4:
column = 0
row += 1
root.mainloop()
How would I go about changing the picture when the buttons are pressed?
I didn't check if your code is working properly but if it works than you have to do something like this:
def OnButtonClick(number):
buttonList[number].config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back,width=150,height=250,command=lambda e=i: OnButtonClick(e)))
buttonList[i].grid(row=row, column=column)

Python: change entry colour dynamically with Tkinter

I am getting problems with Tkinter after() method.
Actually, what I want to do is to change the background colour of some entry boxes as soon as times passes. Let's take this piece of code (which is different from the script I'm working on, but the situation described is the same):
import Tkinter as tk
root = tk.Tk()
root.option_add("*Entry.Font","Arial 32 bold")
emptyLabel=tk.Label()
emptyLabel.grid(row=4) #Empty label for geometry purpose
entryList=[]
for x in range(4):
entryList.append([])
for y in range(4):
entryList[x].append('')
entryList[x][y]=tk.Entry(root, bg="white",width=2,justify="center",
takefocus=True,insertofftime=True)
entryList[x][y].grid(row=x,column=y)
solvebt=tk.Button(root,text='Solve').grid(row=5,column=2)
newgamebt=tk.Button(root,text='New').grid(row=5,column=1)
#BROKEN PART STARTS HERE
def changebg(x,y):
entryList[x][y]['bg']='yellow'
for x in range(4):
for y in range(4):
entryList[x][y].after(300,changebg(x,y))
#Same result with root.after(300,changebg(x,y))
root.mainloop()
The problem is that when I start the program, I would expect it to show me as it "paints", one at time, all of the entry boxes in yellow. What happens, instead, is that the program freezes for (300*16) milliseconds and then, all of a sudded, every entry boxes is yellow!
The problem is here:
def changebg(x,y):
entryList[x][y]['bg']='yellow'
for x in range(4):
for y in range(4):
entryList[x][y].after(300,changebg(x,y))
#Same result with root.after(300,changebg(x,y))
You're calling changebg to immediately in the double for loop -- You're then passing the return value (None) to root.after. This won't lead to the delay that you describe. Perhaps your actual code looks like:
for x in range(4):
for y in range(4):
entryList[x][y].after(300,lambda x=x,y=y : changebg(x,y))
That will lead to the behavior you actually describe. Ultimately, what you need is to flatten your list of widgets and then pass then one at a time -- registering the next one if it exists:
import itertools
all_entries = itertools.chain.from_iterable(entryList)
def changebg(ientries):
ientries = iter(ientries) #allow passing a list in as well ...
entry = next(ientries,None)
if entry is not None:
entry['bg'] = 'yellow' #change the color of this widget
root.after(300,lambda : changebg(ientries)) #wait 300ms and change color of next one.
changebg(all_entries)

Limiting entry on a tk widget

I have trouble finding a way to limit the entry length of entry widgets, I would like to limit it to 20 characters, i.e. when I click on a sequence or the other I would like to be able to edit it but stay in the 20 characters limit. In or order to keep the code light , should I use a regex , a loop or check the entry with an event ?
Here is my code:
import Tkinter
from Tkinter import *
import tkFileDialog
root = Tkinter.Tk()
edit1 =StringVar()
edit2 =StringVar()
s = StringVar()
s = "GATACACGCGCGCGTATATATTACGCGCGCGATACA"
lb01=Label(root,text="sequence1")
lb01v=Entry(root,textvariable=edit1,width=20)
lb01v.delete(0, END)
lb01v.insert(0, s[6:20])
lb01.grid(sticky=W,row=1,column=1)
lb01v.grid(row=1,column=2)
lb02=Label(root,text="sequence2")
lb02v=Entry(root,textvariable=edit2,width=20)
lb02v.delete(0, END)
lb02v.insert(0, s[0:6])
lb02.grid(sticky=W,row=2,column=1)
lb02v.grid(row=2,column=2)
root.mainloop()
Ok I did try with the trace variable, on a short piece of test code , this is excactly what I was searching for !! I like the fact you can prototype so easily in Python ;)
def main():
pass
if __name__ == '__main__':
main()
from Tkinter import *
def callback(sv):
c = sv.get()[0:9]
print "c=" , c
sv.set(c)
root = Tk()
sv = StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
e = Entry(root, textvariable=sv)
e.pack()
root.mainloop()
I know its too late to add any answers to this, just found a simpler way to represent what Wabara had answered. This will help if you need multiple entry limits and each to a user-defined length limit. Here's a code working on Python 3.6.5:
def main():
pass
if __name__ == '__main__':
main()
from tkinter import *
def limit_entry(str_var,length):
def callback(str_var):
c = str_var.get()[0:length]
str_var.set(c)
str_var.trace("w", lambda name, index, mode, str_var=str_var: callback(str_var))
root = Tk()
abc = StringVar()
xyz = StringVar()
limit_entry(abc,3)
limit_entry(xyz,5)
e1 = Entry(root, textvariable=abc)
e2 = Entry(root, textvariable=xyz)
e1.pack()
e2.pack()
root.mainloop()
The simplest solution is to put a trace on the variable. When the trace fires, check the length of the value and then delete any characters that exceed the limit.
If you don't like that solution, Tkinter also has built-in facilities to do input validation on entry widgets. This is a somewhat under-documented feature of Tkinter. For an example, see my answer to the question Python/Tkinter: Interactively validating Entry widget content
I will start off by making an alphabet to measure from. The alphabet is a string and has 26 letters meaning its too long for our use. we want 20 letters only, so our output should be "A" thru "T" only.
I would define a function to make it happen and dump each string thru it that I would want cut to 20 characters or less.
I am making the below code in such a way that it takes as an input any string that is called it takes that input in and processes it to 20 characters in length only...
def twenty(z):
a = z[0:20]
return a
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
so to execute our newly made code, we need only call the print twenty command with the variable we want cut to 20 characters in the parenthesis.
print twenty(alphabet)
-----------------------------------------------------------------
OUTPUT:
ABCDEFGHIJKLMNOPQRST
So you see, it worked, we input the entire alphabet into the program and it cut the string down to 20 letters only. now every time in your code you want to cut text down to 20 letters, just run the command
twenty(variable)
and it will make sure you have no more letters than that.
Explanation:
def twenty is to define a function with one input that you can call on over and over simply by typing twenty(variable)
the next line is a = z[0:20] Meaning call variable "a" to equal the input from position 0 to position 20 and dont worry about anything past that.
return command is how you get an output from the def function. anytime you create a def function, you should end it with a line.

Categories