Tkinter filename float not incrementing correctly - python

I am trying to create a program that loads the next file numerically upon the press of a button. In order to check what the next file is I simply run a while loop checking for a file name in .1 increments. I.E, I have a file labeled 1.1, 1.3, and 1.4, I want the button to load them in that order, skipping 1.2 because it doesn't exist.
The problem arising is that for some reason instead of just incrementing the value that gets checked by .1, it is incrementing it by .10000000000(some random number here), making it so I can't check the files properly
import tkinter as tk
from tkinter import *
mainscreen = tk.Tk()
cuename = tk.Entry(mainscreen)
def loadnext_cue():
cued = float(cuename.get())
next_cue = cued + .1
while os.path.exists("%s .txt" % str(next_cue)) == False:
print(next_cue)
next_cue += 0.1
if next_cue >= 4:
error = Label(mainscreen, text="No Higher Cue")
error.pack(side=TOP)
time.sleep(2)
error.destroy()
break
if os.path.exists("%s .txt" % str(next_cue)):
callup = next_cue
with open("%s.txt" % str(callup), "r") as loadentry:
quote = loadentry.read()
T.delete("1.0", END)
T.insert(END, quote)
Because of what I'm using the program for I know for now at least the file names will only go into the 10's place decimal (I.E. 1.2)
So, starting with file number 1.0, looking for the next file up, the output I'm getting looks something like this:
1.1
1.2000000000000002
1.3000000000000003
1.4000000000000004
1.5000000000000004
1.6000000000000005
1.7000000000000006
1.8000000000000007
1.9000000000000008
2.000000000000001
2.100000000000001
2.200000000000001
2.300000000000001
2.4000000000000012
2.5000000000000013
2.6000000000000014
2.7000000000000015
2.8000000000000016
2.9000000000000017
3.0000000000000018
3.100000000000002
3.200000000000002
3.300000000000002
3.400000000000002
3.500000000000002
3.6000000000000023
3.7000000000000024
3.8000000000000025
3.9000000000000026
4.000000000000003
It seems to go up by exactly .1 on the first iteration but then does whatever it wants after that. Any and all help would be appreciated

It may be due to the fact that they're floating point numbers. For example,
>>print(0.3+0.6)
0.8999999999
It prints 0.89999999 instead of 0.9. In your case,
num = 1
for _ in range(30):
num+=0.1
print(num)
Prints the output that you are getting. Instead what you could do is round them to the tens place so that you gen the answer you are looking for.
import tkinter as tk
from tkinter import *
mainscreen = tk.Tk()
cuename = tk.Entry(mainscreen)
def loadnext_cue():
cued = float(cuename.get())
next_cue = cued + .1
while os.path.exists("%s .txt" % str(next_cue)) == False:
print(next_cue)
next_cue += 0.1
next_cue = round(next_cue, 1) # rounds it to the tenths place
if next_cue >= 4:
error = Label(mainscreen, text="No Higher Cue")
error.pack(side=TOP)
time.sleep(2)
error.destroy()
break
if os.path.exists("%s .txt" % str(next_cue)):
callup = next_cue
with open("%s.txt" % str(callup), "r") as loadentry:
quote = loadentry.read()
T.delete("1.0", END)
T.insert(END, quote)

Your filenames are of the form x.y where x and y are both integers, but you are using a Python float to store the filename. Floats in Python represent decimal numbers with finite precision, and when you perform arithmetic you can experience a loss of precision. That is why when you perform 1.1 + 0.1 you get a very small error in the result -- the 'random number'.
I suggest to modify your code to store the parts x and y in separate variables as integers. You can increment the parts separately according to your logic, and then compose them into the string filename when needed.

Related

How do I make sure all of my values are computed in my loop?

I am working on a 'keep the change assignment' where I round the purchases to the whole dollar and add the change to the savings account. However, the loop is not going through all of the values in my external text file. It only computes the last value. I tried splitting the file but it gives me an error. What might be the issue? my external text file is as so:
10.90
13.59
12.99
(each on different lines)
def main():
account1 = BankAccount()
file1 = open("data.txt","r+") # reading the file, + indicated read and write
s = 0 # to keep track of the new savings
for n in file1:
n = float(n) #lets python know that the values are floats and not a string
z= math.ceil(n) #rounds up to the whole digit
amount = float(z-n) # subtract the rounded sum with actaul total to get change
print(" Saved $",round(amount,2), "on this purchase",file = file1)
s = amount + s
x = (account1.makeSavings(s))
I'm fairly sure the reason for this is because you are printing the amount of money you have saved to the file. In general, you don't want to alter the length of an object you are iterating over because it can cause problems.
account1 = BankAccount()
file1 = open("data.txt","r+") # reading the file, + indicated read and write
s = 0 # to keep track of the new savings
amount_saved = []
for n in file1:
n = float(n) #lets python know that the values are floats and not a string
z= math.ceil(n) #rounds up to the whole digit
amount = float(z-n) # subtract the rounded sum with actaul total to get change
amount_saved.append(round(amount,2))
s = amount + s
x = (account1.makeSavings(s))
for n in amount_saved:
print(" Saved $",round(amount,2), "on this purchase",file = file1)
This will print the amounts you have saved at the end of the file after you are finished iterating through it.

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.

Bubblesort visualization in tkinter

I can't figure out how can I finish one simple program written in Python. Program basically generates array of ten random numbers and then sorts them using bubblesort algorithm. Whole shorting process should be shown on screen - such as this one
My current code is this:
import tkinter
import random
canvas = tkinter.Canvas(bg='white',width='800',height='400')
canvas.pack()
c = []
for i in range(0,10):
c=c+[random.randrange(10)]
print(c)
print('Zoradenie...', c)
def sort(c):
x=300
for i in range(0,10):
for j in range(0,len(c)-1-1):
if c[j+1]<c[j]:
c[j+1],c[j]=c[j],c[j+1]
canvas.create_text(300,80,text=c[j],fill='Red')
x+=25
canvas.update()
canvas.after(1000)
print(c)
return c
sort(c)
But I can't figure out how to show numbers on screen. Any ideas?
To display the digits on the canvas, you must create a text item for each digit. See the end of my code. The harder part is moving the digits. One way is to delete and recreate; the other is to move. I choose the latter.
The hardest part, perhaps, is the time delays. If one uses mainloop, one should use after rather than time.sleep (which blocks the looping) and not use for-loops for animation. The problem is that the function (here sort) that naturally contains for-loops must be broken into pieces whose joint operation may be hard to understand. If one is running just one function and does not care about user interaction (for instance, a pause button), one can use time.sleep and update. I have done so here to make what is going on clearer.
from random import randrange
from time import sleep
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, bg='white', width='800', height='400')
canvas.pack()
ndigits = 10
digits = [randrange(10) for _ in range(ndigits)]
tdelta1, tdelta2 = .8, .2
xstart = 300
xdelta = 25
y = 80
def color(i, swap):
"Temporarily color digits i and i+i according to swap needed."
x = xstart + xdelta * i
dcolor = 'Red' if swap else 'green'
canvas.itemconfigure(items[i], fill=dcolor)
canvas.itemconfigure(items[i+1],fill=dcolor)
canvas.update()
sleep(tdelta1)
canvas.itemconfigure(items[i], fill='Black')
canvas.itemconfigure(items[i+1], fill='Black')
canvas.update()
sleep(tdelta2)
def swap(i):
digits[i], digits[i+1] = digits[i+1], digits[i]
canvas.move(items[i], xdelta, 0)
canvas.move(items[i+1], -xdelta, 0)
items[i], items[i+1] = items[i+1], items[i]
def bubsort():
"Sort digits and animate."
for stop in reversed(range(1, ndigits)):
# stop = index of position whose entry will be determined.
for i in range(stop):
swap_needed = digits[i] > digits[i+1]
color(i, swap_needed)
if swap_needed:
swap(i)
color(i, False)
# Create display items and pause.
items = [canvas.create_text(xstart + xdelta*i, y, text=str(digit))
for i, digit in enumerate(digits)]
canvas.update()
sleep(tdelta1)
bubsort()
This code makes it fairly easy to replace the text digit display with, for instance, a colored bar display. To develop this further, I would define a class of items combining int values and display items as attributes. There would them be only one array of combined items. With comparison methods defines, the array could be passed to any sort function.

Python, if statement and float

I have a function in Python that reads a ds18b20 temp sensor. The sensor gives me a faulty value (-0.062) about 5% of the time that I read it. This is not a problem but I do not want to log the value since it looks ugly in my graphs.
I can't manage to "catch" the value in an if-statement to replace it with "#error". The code below runs nicely but it appears that the if-statement is faulty and does not work - it just runs everything under the else.
I have tried everything, even "catching" all values between 1000 and 1500 (present temperature reading before dividing by 1000) to check if it would work with any temperature, but it does not.
Does anyone have any idea why my if-statement does not work?
def readtwo():
tfile = open("/sys/bus/w1/devices/28-0000040de8fc/w1_slave")
text = tfile.read()
tfile.close()
secondline = text.split("\n")[1]
temperaturedata = secondline.split(" ")[9]
temperature = float(temperaturedata[2:])
temperature = temperature / 1000
if temperature == -0.062:
return("#error")
else:
return(temperature)
Testing base 10 floats for (in)equality is almost always the wrong thing to do, because they almost always cannot be exactly represented in a binary system.
From what I see of your snippet, you should compare against the string, then convert to float if it is not the dreaded -0.062:
def readtwo():
tfile = open("/sys/bus/w1/devices/28-0000040de8fc/w1_slave")
text = tfile.read()
tfile.close()
secondline = text.split("\n")[1]
temperaturedata = secondline.split(" ")[9]
temperature = temperaturedata[2:]
if temperature == '-0062':
return("#error")
else:
temperature = float(temperature) / 1000
return(temperature)
You might also be able to clean up the rest of the code a little:
def readtwo():
with open("/sys/bus/w1/devices/28-0000040de8fc/w1_slave", 'r') as f:
secondline = f.readlines()[1]
temp = secondline.split(' ')[9][2:]
if '-62' in temp:
return '#error'
else:
return float(temp)/1000
Regardless to my comment about the decimal module, floating point arithemitc has it's problems (in python as well). The foremost of which is that due to representation errors, two numbers that are equal on paper, will not be equal when compared by the program.
The way around it, is to look at the relative error between two numbers, rather than simply compare them.
in pseudo:
if abs(num1 - num2)/ abs(num2) < epsilon:
print "They are close enough"
And in your case:
if abs(temparture + 0.062)/0.062 < 10**-3:
return("#error")
Basically, we check that the numbers are "close enough" to be considered the same.

Python Beginner: Selective Printing in loops

I'm a very new python user (had only a little prior experience with html/javascript as far as programming goes), and was trying to find some ways to output only intermittent numbers in my loop for a basic bicycle racing simulation (10,000 lines of biker positions would be pretty excessive :P).
I tried in this loop several 'reasonable' ways to communicate a condition where a floating point number equals its integer floor (int, floor division) to print out every 100 iterations or so:
for i in range (0,10000):
i = i + 1
t = t + t_step #t is initialized at 0 while t_step is set at .01
acceleration_rider1 = (power_rider1 / (70 * velocity_rider1)) - (force_drag1 / 70)
velocity_rider1 = velocity_rider1 + (acceleration_rider1 * t_step)
position_rider1 = position_rider1 + (velocity_rider1 * t_step)
force_drag1 = area_rider1 * (velocity_rider1 ** 2)
acceleration_rider2 = (power_rider2 / (70 * velocity_rider1)) - (force_drag2 / 70)
velocity_rider2 = velocity_rider2 + (acceleration_rider2 * t_step)
position_rider2 = position_rider2 + (velocity_rider2 * t_step)
force_drag2 = area_rider1 * (velocity_rider2 ** 2)
if t == int(t): #TRIED t == t // 1 AND OTHER VARIANTS THAT DON'T WORK HERE:(
print t, "biker 1", position_rider1, "m", "\t", "biker 2", position_rider2, "m"
The for loop auto increments for you, so you don't need to use i = i + 1.
You don't need t, just use % (modulo) operator to find multiples of a number.
# Log every 1000 lines.
LOG_EVERY_N = 1000
for i in range(1000):
... # calculations with i
if (i % LOG_EVERY_N) == 0:
print "logging: ..."
To print out every 100 iterations, I'd suggest
if i % 100 == 0: ...
If you'd rather not print the very first time, then maybe
if i and i % 100 == 0: ...
(as another answer noted, the i = i + 1 is supererogatory given that i is the control variable of the for loop anyway -- it's not particularly damaging though, just somewhat superfluous, and is not really relevant to the issue of why your if doesn't trigger).
While basing the condition on t may seem appealing, t == int(t) is unlikely to work unless the t_step is a multiple of 1.0 / 2**N for some integer N -- fractions cannot be represented exactly in a float unless this condition holds, because floats use a binary base. (You could use decimal.Decimal, but that would seriously impact the speed of your computation, since float computation are directly supported by your machine's hardware, while decimal computations are not).
The other answers suggest that you use the integer variable i instead. That also works, and is the solution I would recommend. This answer is mostly for educational value.
I think it's a roundoff error that is biting you. Floating point numbers can often not be represented exactly, so adding .01 to t for 100 times is not guaranteed to result in t == 1:
>>> sum([.01]*100)
1.0000000000000007
So when you compare to an actual integer number, you need to build in a small tolerance margin. Something like this should work:
if abs(t - int(t)) < 1e-6:
print t, "biker 1", position_rider1, "m", "\t", "biker 2", position_rider2, "m"
You can use python library called tqdm (tqdm derives from the Arabic word taqaddum (تقدّم) which can mean "progress) for showing progress and use write() method from tqdm to print intermittent log statements as answered by #Stephen
Why using tqdm is useful in your case?
Shows compact & fancy progress bar with very minimal code change.
Does not fill your console with thousands of log statement and yet shows accurate iteration progress of your for loop.
Caveats:
Can not use logging library as it writes output stdout only. Though you can redirect it to logfile very easily.
Adds little performance overhead.
Code
from tqdm import tqdm
from time import sleep
# Log every 100 lines.
LOG_EVERY_N = 100
for i in tqdm(range(1,1000)):
if i%LOG_EVERY_N == 0:
tqdm.write(f"loggig : {i}")
sleep(0.5)
How to install ?
pip install tqdm
Sample GIF that shows console output

Categories