python - Tkinter flickering canvas/rectangles in my sorting visualizer - python

Im trying to make a sorting visualizer and everything is going smoothly, but i have a problem.
First of all, i know that there's a lot of similar post but not a single one that can help me.
I have this problem that when the size of the sorting data is to high(>40) the screen starts to flicker when I run the sorting algorithm
Here's a video I recorded with the problem i have
I have some ideas on what the problem might be, but since im kinda new to tkinter with python im struggling to find out what exactly is. Here's my python code on the graphing/drawing data function
def runSort():
global data
data=Algorithm.bubblesort(data, graphData)
print("Sorting...")
def graphData(data, colorData):
canvas.delete("all")
can_height = 530
can_width = 1177
x_width = can_width/(len(data)+1)
offset=5
spacing=10
normalized_data=[i /max(data) for i in data]
for i, height in enumerate(normalized_data):
x0=i*x_width+offset+spacing
y0=can_height- height * 480
x1=(i+1)*x_width+offset
y1=can_height
canvas.create_rectangle(x0,y0,x1,y1,fill=colorData[i])
if(len(data)<=50):
canvas.create_text(x0+2,y0, anchor=SW,text=str(data[i]))
root.update_idletasks()
And here it is my sorting algorithm code
import time
class Algorithm():
def bubblesort(array, drawData):
for i in range(len(array)):
for j in range(len(array)-1-i):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
drawData(array, ["cyan2" if x==j or x==j+1 else "coral3" for x in range(len(array))])
if len(array)<50:
time.sleep(0.5)
else:
time.sleep(0.1)
drawData(array, ["OliveDrab2" for x in range(len(array))])
return array
I hope someone can help me because I have no idea on how to solve this, or if it is possible. If you need more of my code be free to ask and i will gladly give you my code. Thanks! and Greetings from Mexico.

Related

How can I loop over random numbers to get coordinates for my class?

I need a loop over all of my clans, which are instances of a class. Each clan needs to be assigned a position, a x and a y coordinate. Preferably as two lists or as a single tuple (but no idea how I specify that). This is how it works for the 1st clan. Afterwards I always have to check, if the position is already assigned. If so, you have to search for a new position until it is free.
I then coded my class like this:
width = 20
height = 20
no_of_clans = 50
import random
class clan:
def __init__(self, index, position_list):
self.index = index
self.position_list = position_list
def index(no_of_clans):
return list(range(1, no_of_clans +1))
def position_list(self):
for i in range(1, no_of_clans +1):
positions = ()
if i ==1: #i can do it either like this
positions = [(random.randint(0, width)), (random.randint(0, height))]
positions.append(x,y)
else: #or like this, I do not know, which one is better, both are running
x = (random.randint(0, width))
y = (random.randint(0, height))
#check if x and y not already used
#assert
positions.append(x,y)
return positions
print(positions)
how can I assign positions randomly when also using 0.5 steps? I always get an error message.
I never get a list of my positions back, but I do not know what I did wrong.
I know those are probably fairly basic questions but I am quite new to python an already searched for 5h today and I really do ot know where to start anymore. I would be really happy if someon could help me. Thank you so much in advance <3
I found the answer to my original question in this post:
Coordinates of the edges of a honeycomb grid in python
Will update if I am finished with the class-thing :)

List_Name.clear causes crash in python

I'm trying to create a snake algorithm that beats the game on its own, I've gone for the Hamiltonian cycle method with the capability of creating shortcuts, I was trying to create the cycle by using a pathfinding algorithm and compute the longest path where the head of the snake is the start, the tail the end and the 2 blocks in between are walls, the pathfinding library in python uses a matrix to represent the map of pixels, so to generate my matrix I use this block of code:
from pathfinding.core.grid import Grid
from pathfinding.finder.a_star import AStarFinder
from pathfinding.core.diagonal_movement import DiagonalMovement
matrix=[]
row=[]
yi=0
xi=0
while yi<800:
if row != []:
matrix.append(row)
yi += 20
row.clear() #This causes crash
while xi<1400:
row.append(1)
if xi == 40 or xi == 60:
if yi == 20:
row.append(0)
xi += 20
grid = Grid(matrix=matrix)
start = grid.node(4, 1)
end = grid.node(1, 1)
finder = AStarFinder(diagonal_movement=DiagonalMovement.always)
path, runs = finder.find_path(start, end, grid)
print('operations:', runs, 'path length:', len(path))
print(grid.grid_str(path=path, start=start, end=end))
But whenever I run it, it crashes,
I've narrowed it down to the line
row.clear()
but I have no clue why it does this, if I remove it no walls are created, other methods of emptying the list like :row=[] or
while i<len(row):
row.remove(i)
row += 1
give me the same result, I get no error message, nothing prints it just crashes, its even more clear on the entire code because the window displaying the game of snake doesn't display anything and the windows crash window appears, I'm using Windows 10, python 3.8.2
I'm quite new to programming so please excuse my inefficient code, I do it for fun and performance brings me little pleasure, any help is greatly appreciated
I hope I didn't miss anything obvious making you waste time but as long as my code is fixed I'm a happy chappy.
Thanks
Turns out I'm really stupid and forgot to add xi = 0 so it could start the indented loop again instead of just looping into infinity

How do I print a statement if a value of a variable changes in python?

Say my code is something like:
while True:
nheight = tc.getblockcount()['count']
with tc.getblockcount... being a rpc call, the result is an integer stored in the variable nheight. This value changes 30 seconds or so, so how do I make it print a statement when it changes? Right now I've done something like this:
height = tc.getblockcount()['count']
while True:
nheight = tc.getblockcount()['count']
if height != nheight:
print("Val Changed")
height = nheight
time.sleep(0.4)
So the hope is that when nheight changes, it detects it because it differs from height, which had the original same value. Then it updates the value height so that it doesnt keep printing Val Changed because the value changed once. Then it sleep for 0.4 seconds and loops again
The problem right now is that it prints "Val Changed" once but doesnt again, even when the value changes (which i've verified by inserting a few print statements)
Could someone help me with making something which will print something when the value of nheight changes?
Also if possible, since the rest of my code isn't made using OOP, and I don't understand it well, the examples are given without OOP?
(there's something called the observer pattern which i think does this, is that correct?)
Thanks a lot!!
since i dont know what tc.getblockcount is here is a function that returns mostly the same number but 10% of the time it changes
def value_iter():
last_value = 1
while True:
yield last_value
if random.random() < 0.1:
last_value = last_value + 1
def getblockcount(it=value_iter()):
return next(it)
height = getblockcount()
while True:
nheight = getblockcount()
print(nheight,"=?=",height)
if nheight != height:
print("UPDATE!\n\n")
height=nheight
as you can see if you copy this it works as expected
Update: I got it to work with some help, problem was with another part of the code
Thanks!

How to display an Image with python, without pyglet or similar?

So, what I am trying to do in detail:
I have a device that acts as a display, although is technically not one (that's not really important, it can be handled like a normal display) and I want to display a series of images on it. When each image is displayed i call a function that takes readings from another device. So far I have used pyglet for that and I can get it to display the images in the correct order and where I want them, but the readings taken seem not to correspond to the right image.
The simplest way to explain what I want to do is:
for a in range(10):
display('image%d'%a)
values(a) = measure()
Imagine values being initiated in the correct form.
What I tried so far was:
import pyglet
import numpy as np
size = 64
no = 512/size
a,b,c = (0,0,0)
values = np.zeros((no,no,3))
display = pyglet.window.get_platform().get_default_display()
screens = []
for screen in display.get_screens():
screens.append(screen)
window = pyglet.window.Window(screen = screens[1], fullscreen=1)
#here i choose the mentioned device, since it is connected to my computer via display port
#window.event
def update(dt):
global a,b,c
if a == no/2. and b == no/2.:
values[a,b,c] = 0
window.clear()
else:
image = pyglet.image.load('folder_%d/grating_%d_%d_%d.bmp' % (size,a,b,c))
window.clear()
print (a,b,c)
image.blit(0,0)
values[a,b,c] = measure()
c+=1
if c == 3:
b += 1
c = 0
if b == no:
a += 1
b = 0
if a == no:
pyglet.app.exit()
pyglet.clock.schedule_interval(update, .2)
pyglet.app.run()
where measure() is my own function. "no" is an index for my images, they range from (0,0,0),(0,0,1),(0,0,2),(0,1,0)... to (no,no,2) and are called to be called one by one. Also the case of a = b = no/2 is a special case that is of no special importance to my problem, I think.
first of all: I am pretty sure this is neither elegant nor efficient but I am not creating software that anyone else is ever likely to use. I am also aware that using global variables is bad practice but their use is due to me not knowing how to use eventloops correctly.
I am not happy with this solution, because the readings i take always seem to correspond to the previous image.
I guess I misuse the eventloop badly, but the pyglet documentation does not really help me here.
Also I feel like I am building a whole truck just to carry a bag across the street...
I have already looked into pyplot as an alternative, since the imshow() function works in the way I want, but the plotting libraries seem to display images in random sizes, which I cannot figure out how to control properly.
I appreciate any help on using pyglet correctly as well as alternative libraries that can help.
Thank you already,
Mopsi
An extra-long comment, that requires formatted code
From your example, you don't need a,b,c outside of the update function and all the global stuff is about having values that stay alive across invocations. If I'm correct this is better suited by a closure, like
...
def make_update(no, window):
from itertools import product
abcs = product(range(no),range(no),range(3))
#window.event
def _update(...):
try:
a, b, c = next(abcs)
except StopIteration:
... wind up ...
...
return _update
update = make_update(no, window)
...
Alright, I did not actually solve the problem but found a workaround:
I just flatten my image nomenclature, e.g
0_0_0 -> 000
0_0_1 -> 001
0_0_2 -> 002
0_1_0 -> 003
etc.
With values now being an array with dimensions [no*no*3,1]
And since for the n-th iteration and therefore n-th measurement I see the (n-1)th image, I simply add a dummy image and assign the n-th measured value to values[n-1].
The first measurement being useless is no problem here, since values[-1] is the last element, which gets overwritten with a meaningful measurement in the end.
Since the number of images is known and predetermined I can reshape my array of values afterwards.
If anyone cares to explain why no the displayed image is shifted by one iteration (with no image at all being displayed at the first iteration) please go ahead.

python list Index out of range error

I am working on a python tetris game that my proffessor assigned for the final project of a concepts of programming class. I have got just about everything he wanted to work on it at this point but I am having a slight problem with one part of it. Whenever I start moving pieces left and right I keep getting "index out of range error". This only happens when it is up against a piece. Here are the culprits that are giving me grief.
def clearRight(block=None):
global board, activeBlock, stackedBlocks
isClear = True
if(block == None):
block = activeBlock
if(block != None):
for square in block['squares']:
row = square[1]
col = square[0]+1
if(col >= 0 and stackedBlocks[row][col] !=None):
isClear=False
return isClear
def clearLeft(block=None):
global board, activeBlock, stackedBlocks
isClear = True
if(block == None):
block = activeBlock
if(block != None):
for square in block['squares']:
row = square[1]
col = square[0]-1
if(col >= 0 and stackedBlocks[row][col] !=None):
isClear=False
return isClear
I am not looking to get anyone to fix it for me, I'm only looking for tips on how to fix it myself. Thanks in advance for any help that is given.
There a typo that would cause that problem in the first method.
When you're checking each cell in the block shifted one right, you don't check if they are off the grid.
if (col >= 0 and ...)
probably should be
if (col < num_cols and ...)
I also agree with CrazyDrummer, make a generic clear function
Spoilers ...
def clear(x_offset, block=None):
if not block:
block = activeBlock
if not block: return True
for x,y in block:
x += x_offset
if not (0 <= x < num_cols) or stackedBlocks[x, y]:
return False
return True
Look at what's different when you're getting the exception. Try printing out program state information to help you zero in. There's only one place where you access an array with variable indexes, so you can narrow your search radius a bit.
Separate suggestion: Make a generic clear that takes determines what direction you want to clear from by the parameters.
I highly recommend the book debugging rules!, it will aid you in searching out and properly fixing problems. :D

Categories