Python backtracking maze really slow - python

check the edit,
I wrote some code while watching The Coding Train on youtube.. the maze backtracking generator. The youtuber wrote the code in javascript and I tried to understand the code while writing in python. It seems he had to change the framerate because his program was so fast just to see the generating part. While mine after some 10ish squares it was already so slow.
It cant be an hardware problem, I've got an i5-4690K CPU and a good matching GPU, it must be something in the code! But I can't find what it is.
I rewatched the episodes so I could see what was wrong, but it seems I wrote everything just fine.
from tkinter import *
import math
import random
import time
# initialization canvas
root = Tk()
canvas = Canvas(root, width=400, height=400, bg="#333333")
canvas.pack()
# some global variables
w = 40;
cols = math.floor(int(canvas["width"])/w)
rows = math.floor(int(canvas["height"])/w)
grid = []
current = None
class Cell():
line_color = "#AAAAAA"
visited_color = "green"
visited = False
rectangle = None
def __init__(self, i, j):
self.i = i
self.j = j
self.wall = [True, True, True, True] # top , right, bottom, left
def __repr__(self):
return "({}, {})".format(self.i, self.j)
def draw_lines(self):
x = self.i*w
y = self.j*w
if self.visited :
self.rectangle = canvas.create_rectangle(x, y, x+w, y+w, fill="purple", outline="")
canvas.update()
if self.wall[0]:
canvas.create_line(x, y, x+w, y, fill=self.line_color)
else:
canvas.create_line(x, y, x+w, y, fill="purple")
if self.wall[1]:
canvas.create_line(x+w, y, x+w, y+w, fill=self.line_color)
else:
canvas.create_line(x+w, y, x+w, y+w, fill="purple")
if self.wall[2]:
canvas.create_line(x, y+w, x+w, y+w, fill=self.line_color)
else:
canvas.create_line(x, y+w, x+w, y+w, fill="purple")
if self.wall[3]:
canvas.create_line(x, y, x, y+w, fill=self.line_color)
else:
canvas.create_line(x, y, x, y+w, fill="purple")
def checkNeighbors(self):
neighbors = []
top = None
bottom = None
left = None
right = None
if index(self.i, self.j-1) != -1:
top = grid[index(self.i, self.j-1)]
if index(self.i, self.j+1) != -1:
bottom = grid[index(self.i, self.j+1)]
if index(self.i-1, self.j) != -1:
left = grid[index(self.i-1, self.j)]
if index(self.i+1, self.j) != -1:
right = grid[index(self.i+1, self.j)]
if top is not None and top.visited is False:
neighbors.append(top)
if right is not None and right.visited is False:
neighbors.append(right)
if bottom is not None and bottom.visited is False:
neighbors.append(bottom)
if left is not None and left.visited is False:
neighbors.append(left)
if len(neighbors) > 0:
r = random.randint(0, len(neighbors)-1)
return neighbors[r]
else:
return None
def removeWalls(a, b):
x = a.i - b.i
y = a.j - b.j
if x != 0:
if x == 1:
a.wall[3] = False
b.wall[1] = False
else:
a.wall[1] = False
b.wall[3] = False
if y != 0:
if y == 1:
a.wall[0] = False
b.wall[2] = False
else:
a.wall[2] = False
b.wall[0] = False
def index(i, j):
if j < 0 or j > rows - 1 or i < 0 or i > cols - 1:
return -1
return j + i * cols
def setup():
global current
for i in range(rows):
for j in range(cols):
cell = Cell(i, j)
grid.append(cell)
current = grid[0]
next_one = None
def draw():
global current
global next_one
stack = []
almost = False
while True:
current.visited = True
for cell in grid:
cell.draw_lines()
next_one = current.checkNeighbors()
if next_one:
stack.append(current)
removeWalls(current, next_one)
current = next_one
elif len(stack) > 0:
cell = stack.pop()
current = cell
for cell in grid:
print(cell.visited)
setup()
draw()
root.mainloop()
I'm sorry to put all the code, but I think all of it is relevant for what comes to performance, haven't put any useful comments, sorry I'm trying to become a better programmer and change that bad habit
BIG EDIT:
I tested to just draw my maze once I finished calculating it, and it takes less than a second, so I figure it has to be with the amount of widgets (lines) I'm creating..? How could I minimize the widgets so I could see the maze being created like I wanted to?

You draw lines even if they didn't change. Instead, draw only the changed cells.
Besides, you have endless loop. Even if stack contains no more elements, you don't stop. Here you should break the loop.
Here is an improvement:
def draw():
for cell in grid:
cell.draw_lines()
global current
global next_one
stack = []
almost = False
while True:
current.visited = True
next_one = current.checkNeighbors()
if next_one:
stack.append(current)
removeWalls(current, next_one)
current.draw_lines()
next_one.draw_lines()
current = next_one
elif len(stack) > 0:
cell = stack.pop()
current = cell
else:
break

Related

Animate algorithm

I want to visualize an algorithm (Graham scan) in python with tkinter.
I want to animate the algorithm and I am stuck.
I basically want to draw and delete lines but I don't understand canvas.after() well enough to make it work.
draw_line() returns the line object but when I call it in canvas.after(..., draw_line, ...) I don't see a way to get the return value or how to call another canvas.after() to change the color/delete that line if the function draw_line() hasn't been called yet because of the delay.
Thanks in advance.
from tkinter import *
import math
import random
class Point:
def __init__(self, _x, _y, _a=0):
self.x = _x
self.y = _y
self.angle = _a
def get_co(self):
return self.x, self.y
def draw_hull(hull):
for i in range(len(hull) - 1):
canvas.create_line(hull[i][0], hull[i][1], hull[i + 1][0], hull[i + 1][1], fill="red", width=2)
def draw_line(p1, p2, color="yellow"):
return canvas.create_line(p1.x, p1.y, p2.x, p2.y, fill=color, width=2)
def convex_hull(list_points):
# find bottom point
bottom_point = Point(math.inf, math.inf)
for point in list_points:
if point[1] < bottom_point.y:
bottom_point = Point(point[0], point[1])
# calculate angles between the bottom point and the other points
points = []
for point in list_points:
if point != bottom_point.get_co():
new_point = Point(point[0], point[1])
angle = calculate_angle(bottom_point, new_point)
new_point.angle = angle
points.append(new_point)
# sort the points by angle
swaps = None
while swaps != 0:
swaps = 0
for i in range(len(points) - 1):
point1 = points[i]
point2 = points[i + 1]
if point1.angle > point2.angle:
points[i], points[i + 1] = points[i + 1], points[i]
swaps += 1
# go through the points and add them to the convex hull
# if the angle between 3 points ever exeeds 180 degrees, discard the middle point
hull = [bottom_point, points[0]]
i = 1
while i < len(points):
####### DRAW LINE #######
canvas.after(i*500, draw_line, hull[-2], hull[-1])
##############
# check angle
angle = calculate_angle(hull[-2], hull[-1], points[i])
if angle == -1:
########## DELETE LINE ##########
# change color of line to red and delete it a bit later
# canvas.itemconfig(line, fill="red")
# canvas.after(i*500+250, canvas.delete, line)
####################
# pop the point of the stack
hull.pop()
else:
########## CHANGE COLOR OF LINE ##########
# change color of line to green
# canvas.itemconfig(line, fill="green")
####################
# move to the next point
hull.append(points[i])
i += 1
# add bottom point again for loop
hull.append(bottom_point)
# give easy return list (with coordinate-tupels not class objects)
output = []
for point in hull:
output.append(point.get_co())
return output
def calculate_angle(point1, point2, point3=None):
if point3 is None:
if point2.x - point1.x == 0:
return 90
elif point2.x - point1.x > 0:
return math.degrees(math.atan((point2.y - point1.y)/(point2.x - point1.x)))
else:
return 180 - math.degrees(math.atan((point2.y - point1.y)/(point1.x - point2.x)))
else:
v1 = Point(point1.x - point2.x, point1.y - point2.y)
v2 = Point(point3.x - point2.x, point3.y - point2.y)
det = (v1.x * v2.y) - (v2.x * v1.y)
if det < 0:
return 1
else:
return -1
window = Tk()
window.geometry("1000x600")
canvas = Canvas(window, width=1000, height=600)
canvas.pack()
POINTSIZE = 2
points = []
for i in range(100):
x = random.randint(50, 950)
y = random.randint(50, 550)
points.append((x, y))
canvas.create_oval(x - POINTSIZE, y - POINTSIZE, x + POINTSIZE, y + POINTSIZE, fill="black")
hull = convex_hull(points)
# draw_hull(hull)
window.mainloop()
If you have questions about the code, let me know. Because I dont know where to start to explain, since I made major changes to your code.
Anyway, I would be glad if you share your code again, once you are done with, on CodeReview and please do let me know. Because it was fun to work with and your code seems incomplete to me.
Happy Coding:
import tkinter as tk
import random
import math
class Point:
def __init__(self, _x, _y, _a=0):
self.x = _x
self.y = _y
self.angle = _a
return None
def get_co(self):
return self.x, self.y
class Window(tk.Tk):
def __init__(self,_w,_h):
super().__init__()
self.POINTSIZE = 2
self.points = []
self.swaps = None
self.count = 1
self.delay = 200
self.title('Graham scan simulation')
self.toolbar = tk.Frame(self,background='grey')
self.refresh_button = tk.Button(self.toolbar,text='Refresh',
command=self.refresh)
self.start_button = tk.Button(self.toolbar,text='Start',
command = self.convex_hull)
self.canvas = tk.Canvas(self,width=_w,height=_h)
self.toolbar.pack(side=tk.TOP,fill=tk.BOTH,expand=True)
self.refresh_button.pack(side=tk.LEFT)
self.start_button.pack(side=tk.LEFT)
self.canvas.pack(side=tk.BOTTOM,fill=tk.BOTH,expand=True)
def generate_points(self):
for point_instance in self.points:
yield point_instance
def find_bottom_point(self):
bottom_point = Point(math.inf,math.inf)
for point in self.generate_points():
if point.y < bottom_point.y:
bottom_point = point
return bottom_point
def calculate_angle(self,point1, point2):
if point2.x - point1.x == 0:
return 90
elif point2.x - point1.x > 0:
return math.degrees(math.atan((point2.y - point1.y)/(point2.x - point1.x)))
else:
return 180 - math.degrees(math.atan((point2.y - point1.y)/(point1.x - point2.x)))
def calculate_angels_by_bottom_point(self,bottom_point):
for point in self.generate_points():
if point != bottom_point:
angle = self.calculate_angle(bottom_point,point)
point.angle = angle
def sort_points(self,event_variable):
if self.swaps != 0:
self.swaps = 0
for i in range(len(self.points)-1):
point1 = self.points[i]
point2 = self.points[i + 1]
if point1.angle > point2.angle:
self.points[i], self.points[i + 1] = self.points[i + 1], self.points[i]
self.swaps += 1
if self.swaps == 0:
event_variable.set(1)
self.after(20,self.sort_points,event_variable)
def check_angle(self,point1,point2,point3):
v1 = Point(point1.x - point2.x, point1.y - point2.y)
v2 = Point(point3.x - point2.x, point3.y - point2.y)
det = (v1.x * v2.y) - (v2.x * v1.y)
if det < 0:
return 1
else:
return -1
def draw_line(self,p1,p2,color='yellow'):
return self.canvas.create_line(p1.x,p1.y, p2.x,p2.y, fill='yellow',tags='line')
def clear_red_lines(self,p1,p2):
shapes = self.canvas.find_withtag('line')
for shape in shapes:
if self.canvas.itemcget(shape,'fill') == 'red':
coords = self.canvas.coords(shape)
overlapped = self.canvas.find_overlapping(*coords)
for i in overlapped:
coords2 = self.canvas.coords(i)
if coords == coords2:
self.canvas.delete(i)
self.canvas.delete(shape)
def animated_draw(self,hull):
if self.count != len(self.points):
line = self.draw_line(hull[-2],hull[-1])
check_mark = self.check_angle(hull[-2],hull[-1],self.points[self.count])
if check_mark == -1:
self.canvas.itemconfig(line,fill='red')
self.after(self.delay-100,lambda:self.clear_red_lines(hull[-2],hull[-1]))
hull.pop()
else:
self.canvas.itemconfig(line,fill='green')
hull.append(self.points[self.count])
self.count += 1
self.after(self.delay,self.animated_draw,hull)
def convex_hull(self):
bottom_point = self.find_bottom_point()
self.calculate_angels_by_bottom_point(bottom_point)
event_variable = tk.IntVar(self,value=0)
self.sort_points(event_variable)
self.wait_variable(event_variable)
self.points.pop(0)
self.animated_draw(hull = [bottom_point, self.points[0]])
def generate_listpoints(self,_amount):
'''using a generator for memory purpose'''
for i in range(_amount):
x = random.randint(50, 950)
y = random.randint(50, 550)
yield x,y
def refresh(self):
self.swaps = None
self.count = 1
self.points= []
self.populate_canvas()
def populate_canvas(self):
self.canvas.delete('all')
for x,y in self.generate_listpoints(100):
self.points.append(Point(x, y))#instead of creating throwing instances away.
self.canvas.create_oval(x - self.POINTSIZE,
y - self.POINTSIZE,
x + self.POINTSIZE,
y + self.POINTSIZE,
fill="black")
root = Window(1000,600)
root.mainloop()

Trying to create the game minesweeper in python using Tkinter but have a problem finding the number of neighbors around a tile

The numbers show how many neighbors the tile has and should show: 3 neighbors on each corner, 5 on all four edges, and everything else 8. but for some reason the right edge shows 6 instead of five.
Here is an image of what I just done:
from tkinter import *
from random import choice
root = Tk()
root.geometry("544x544")
# tile class
class Tile:
def __init__(self, x, y, state=0):
self.x = x
self.y = y
self.state = state
self.button = Button(root,command=self.button_command, image=empty_block, height=28, width=28)
self.listOfNeighbors = []
self.neighbors = 0
def button_command(self):
print(self.x, self.y)
def findNeighbors(self):
self.listOfNeighbors.append(board[self.y-1][self.x])
self.listOfNeighbors.append(board[self.y][self.x-1])
self.listOfNeighbors.append(board[self.y-1][self.x-1])
try: self.listOfNeighbors.append(board[self.y+1][self.x])
except: pass
try: self.listOfNeighbors.append(board[self.y+1][self.x-1])
except: pass
try: self.listOfNeighbors.append(board[self.y-1][self.x+1])
except: pass
try: self.listOfNeighbors.append(board[self.y+1][self.x+1])
except: pass
try: self.listOfNeighbors.append(board[self.y][self.x+1])
except: pass
self.sortNeighbors()
def sortNeighbors(self):
for i in self.listOfNeighbors:
if self.y == 0:
if i.y == 15: self.listOfNeighbors.remove(i);print(self.x, self.y," ", i.x, i.y)
elif self.x == 0:
if i.x == 15: self.listOfNeighbors.remove(i);print(self.x, self.y," ", i.x, i.y)
self.neighbors = len(self.listOfNeighbors)
self.button.config(image=neighbors_images[self.neighbors])
#variable
empty_block = PhotoImage(file="images/empty-block.png")
bomb_unclicked = PhotoImage(file="images/unclicked-bomb.png")
bomb_clicked = PhotoImage(file="images/bomb-at-clicked-block.png")
neighbors_images = [
PhotoImage(file="images/0.png"),
PhotoImage(file="images/1.png"),
PhotoImage(file="images/2.png"),
PhotoImage(file="images/3.png"),
PhotoImage(file="images/4.png"),
PhotoImage(file="images/5.png"),
PhotoImage(file="images/6.png"),
PhotoImage(file="images/7.png"),
PhotoImage(file="images/8.png"),
]
board = []
for y in range(16):
temp = []
for x in range(16):
temp.append(Tile(x, y))
temp[-1].button.grid(row=y, column=x)
board.append(temp)
for i in range(40):
choice(choice(board)).state = 1
for y in board:
for x in y:
x.findNeighbors()
root.mainloop()
As far as I see, the problem comes from line 42 in which I trying to remove from the list of neighbors any Tile that is outside the board.
You are making it harder on yourself. I suggest being explicit and avoid adding and then removing unnecessary neighbours:
def findNeighbors(self):
NEIGHBOURS = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, -1),
(1, 0),
(1, 1)
]
for dx, dy in NEIGHBOURS:
if 0 <= self.x + dx < len(board[0]) and 0 <= self.y + dy < len(board):
self.listOfNeighbors.append(board[self.y + dy][self.x + dx])
self.sortNeighbors()
def sortNeighbors(self):
self.neighbors = len(self.listOfNeighbors)
self.button.config(image=neighbors_images[self.neighbors])

Need help on snake program about user's input

So, I'm writing a snake program using the tkinter Library. The program is globally working but I have a little problem with the inputs' treatment indeed if i give two input too quickly only the last one will be interpret. And i don't really know how to solve this i try to force the update after every player's input but it's clearly not the good solution because it force the snake to move and make it able to teleport so I'm would be glad if someone has an idea to solve this issue. There is my code I'm sure that it could be improved but for now I would like to focus on the first issue.
import tkinter as tk
import numpy.random as rd
class snake:
def __init__(self,n,m):
self.n = n
self.m = m
self.body = [(n//2,m//2),(n//2,m//2-1)]
self.lenght = 2
self.food = (0,0)
self.relocate_food()
self.Game = -2
self.vector = (0,1) #(0,-1) = up, (0,1) = right, (0,1) = down, (-1,0) = left
self.speed = 120
def up(self):
self.vector = (-1,0)
def right(self):
self.vector = (0,1)
def down(self):
self.vector = (1,0)
def left(self):
self.vector = (0,-1)
def relocate_food(self):
x = rd.randint(0,self.n)
y = rd.randint(0,self.m)
i = 0
test = True
while i<self.lenght and test:
if (x,y) == self.body[i]:
test = False
self.relocate_food()
else:
i += 1
if i == self.lenght:
self.food = (x,y)
def collide(self):
head = self.body[0]
for i in range(1,self.lenght):
if head == self.body[i]:
self.Game = -1
break
x,y = head
if x>=self.n or y>=self.m or x<0 or y<0:
self.Game = -1
def eat(self):
head = self.body[0]
if head == self.food:
self.lenght +=1
x0, y0 = self.body[-1]
x1, y1 = self.body[-2]
x = x0 - x1
y = y0 - y1
self.body.append((x0+x,y0+y))
self.relocate_food()
if self.lenght%5 == 0:
self.speed = int(self.speed * 0.90)
def move(self):
dx, dy = self.vector
last_x, last_y = self.body[0]
new_x = last_x + dx
new_y = last_y + dy
self.body[0] = (new_x, new_y)
for k in range(1, self.lenght):
x, y = self.body[k]
self.body[k] = (last_x,last_y)
last_x, last_y = x, y
return
class screen(snake):
def __init__(self,root,n,m):
snake.__init__(self,n,m)
root.minsize(n*20,m*20)
root.maxsize(n*20,m*20)
root.configure(background='white')
self.root = root
self.n = n
self.m = m
self.speed = 130
self.canvas = tk.Canvas(root, width = n*20, height =m*20,bg='black')
self.canvas.bind_all("<Key-Up>",self.move_up)
self.canvas.bind_all("<Key-Down>",self.move_down)
self.canvas.bind_all("<Key-Left>",self.move_left)
self.canvas.bind_all("<Key-Right>",self.move_right)
self.canvas.grid(row=1,column=0)
self.draw_snake()
self.draw_food()
def draw_snake(self):
y,x = self.body[0]
self.canvas.create_rectangle(x*20,y*20,(x+1)*20,(y+1)*20,fill= 'red4')
for k in range(1,self.lenght):
y,x = self.body[k]
self.canvas.create_rectangle(x*20,y*20,(x+1)*20,(y+1)*20,fill= 'red')
def draw_food(self):
y,x =self.food
self.canvas.create_rectangle(x*20,y*20,(x+1)*20,(y+1)*20,fill= 'green')
def move_up(self,event):
if self.Game == -2:
self.Game =0
self.up()
self.update()
else:
self.up()
def move_down(self,event):
if self.Game == -2:
self.Game =0
self.down()
self.update()
else:
self.down()
def move_left(self,event):
if self.Game == -2:
self.Game =0
self.left()
self.update()
else:
self.left()
def move_right(self,event):
if self.Game == -2:
self.Game =0
self.right()
self.update()
else:
self.right()
def update(self):
if self.Game == -2:
return
self.move()
self.eat()
self.collide()
if self.Game == -1:
self.root.destroy()
return
self.canvas.delete("all")
self.draw_snake()
self.draw_food()
self.root.after(self.speed,self.update)
window = tk.Tk()
snake = screen(window,35,35)
snake.update()
window.mainloop()
This is not really a bug. Your animation uses an 'update' function that is executed every 120ms. So if you hit 2 arrow keys within 120ms (i.e. between two successive calls of 'update'), only the last hit is considered, because only one translation vector can be considered for each snake update. Nobody can blame you on that point, as time controlled animation is a discrete process with a given time window. It's the only solution to get fluid and regular animation (all video games are based on such a process), so that's clearly correct.
However, your code may still be improved on several aspects. For instance, at each animation frame, you delete all Canvas items and create a whole new set of items ('create_rectangle') for the snake elements and the food. This is not very efficient. It would be better to simply change the coordinates of the items (check the Canvas.coords function from the doc). Note that animating a snake simply requires to move the previous tail position to the new head position, to give the illusion of a moving beast. So moving only 1 item (2 items when eating food) is necessary at each frame, which is must faster to process.
Thank Furas for the basic idea it was what i needed. New code with my correction :
def __init__(self, root,n,m):
"""
"""
self.input = []
"""
"""
def move_up(self,event):
if self.Game == -2:
self.Game =0
self.up()
self.update()
else:
self.input.append(0)
"""
Same for all the move
"""
def update(self):
if self.Game == -2:
return
if len(self.input)>3: #Make sure that the player doesn't stack instruction
self.pop()
try:
input = self.input.pop(0)
except:
input = -1
if input == 0:
self.up()
elif input == 1:
self.right()
elif input == 2:
self.down()
elif input == 3:
self.left()
self.move()
self.eat()
self.collide()
if self.Game == -1:
self.root.destroy()
return
self.canvas.delete("all")
self.draw_snake()
self.draw_food()
self.root.after(self.speed,self.update)

How to pass pixbuf from a class in a module,after user interaction, in Python

Im working on a python module to handle screenshots. The module itself works fine if alone. however when i import it into my main project i cant figure out how to return a pixbuf after waiting for the user to select a region to capture.
here is the screenshot module:
class CaptureRegion():
def __init__(self):
self.ThePic = None
self.drawArea = Gtk.DrawingArea()
self.drawArea.connect('draw',self.onDraw)
mBox = Gtk.VBox(False,2)
mBox.pack_start(self.drawArea,True,True,2)
self.MaskWin = Gtk.Window()
self.MaskWin.set_position(Gtk.WindowPosition.CENTER)
self.MaskWin.connect("key-press-event", self.KeyPress)
self.MaskWin.connect("button-press-event", self.ButtonPress)
self.MaskWin.connect("button-release-event", self.ButtonRelease)
self.MaskWin.connect("motion-notify-event", self.Motion)
self.MaskWin.add(mBox)
self.button_x = 0
self.button_y = 0
self.button_down = False
self.sel_rect = Gdk.Rectangle(-10, -10, 10, 10)
self.capfull = CaptureScreen()
self.fullpixbuf = self.capfull.Go()
def onDraw(self, widget, event):
print("Draw")
myGdkWindow = self.MaskWin.get_window()
self.cr = Gdk.cairo_create(myGdkWindow)
self.cr.set_operator(cairo.OPERATOR_SOURCE)
self.cr.set_source_rgb(1,1,1)
Gdk.cairo_set_source_pixbuf(self.cr, self.fullpixbuf, 5, 5)
self.cr.paint()
self.cr.set_line_width(1)
self.cr.set_source_rgb(0,0,0)
if self.button_down == True:
x = self.sel_rect.x
y = self.sel_rect.y
w = self.sel_rect.width
h = self.sel_rect.height
if w <= 0 or h <= 0:
return True
self.cr.rectangle(x,y,w,h)
self.cr.set_source_rgba(0.2, 0.6, 0.8, 0.35)
self.cr.set_line_width(2)
self.cr.stroke()
return True
def Motion(self,widget,event):
if self.button_down == False:
return False
x1,y1 = self.button_x, self.button_y
x2,y2 = event.x, event.y
x = int(min(x1,x2))
y = int(min(y1,y2))
w = int(abs(x1-x2))
h = int(abs(y1-y2))
old = self.sel_rect
self.sel_rect.x = x
self.sel_rect.y = y
self.sel_rect.width = w
self.sel_rect.height = h
win = self.MaskWin.get_window()
sx, sy, sw, sh = self.MaskWin.get_window().get_geometry()
self.drawArea.queue_draw_area(sx,sy,sw,sh)
def ButtonPress(self,widget,event):
if not event.button == 1:
return False
print("button down")
self.debounce = 0
self.button_x, self.button_y = event.x, event.y
self.button_down = True
return True
def ButtonRelease(self,widget,event):
self.button_down = False
x = self.sel_rect.x
y = self.sel_rect.y
w = self.sel_rect.width
h = self.sel_rect.height
self.drawArea.queue_draw_area(x-1, y-1, w-1, h-1)
if self.debounce == 0:
self.debounce += 1
print("Snipping x:%s y:%s w:%s h:%s" %(x,y,w,h))
self.ThePic = Gdk.pixbuf_get_from_window(self.MaskWin.get_window(), x, y, w, h)
self.MaskWin.hide()
#option 2: to return ThePic here after the selection is made thru a callback to a function in the main class
# i.e.
#self.CallbackInMain(self.ThePic) (not sure how to callback to the main class, self is wrong maybe?)
def KeyPress(self,widget,event):
if event.keyval == Gdk.KEY_Escape:
self.ThePic = None
return
def WaitThread(self):
for i in range(1, 50):
time.sleep(0.1)
print(i)
self.done_waiting.set()
def Go(self):
self.MaskWin.fullscreen()
self.MaskWin.show_all()
self.MaskWin.set_decorated(False)
#option 1 find a way to wait for the selection to complete like this:
#(this code doesnt end, the self.done_waiting.set() call doesnt stop it)
# self.done_waiting = threading.Event()
# self.thread = threading.Thread(target=self.WaitThread)
# self.thread.start()
# self.done_waiting.wait(1)
# print("Main thread is done waiting")
return(self.ThePic)
class CaptureScreen():
def __init__(self):
self.ThePic = None
def Go(self):
screen = Gdk.get_default_root_window()
x, y, width, height = screen.get_geometry()
self.fullpixbuf = Gdk.pixbuf_get_from_window(screen, x, y, width, height)
return(self.fullpixbuf)
im calling the class like this:
import Screentshot as Shot
self.captureregion = Shot.CaptureRegion()
pic = self.captureregion.Go()
pic.savev("region.png",'png',(),())
with the CaptureScreen class im just capturing the whole screen so i can return the pixbuf directly from the CaptureScreen.Go() function. however with the CaptureRegion class i have to wait for the user to select an area, and im not sure how to have CaptureRegion.Go() return the pixbuf after the users selection. I have two ideas, one use threading to wait for the selection to complete and then return the pixbuf from Capture Region.Go() (option 1 in code) , or second somehow have the screenshot class callback to function in main with the pixbuf as an arg(option 2 in code)
Im not very familiar with classes nor have i ever used the threading module. ive searched and found lots of info, i just cant quite wrap my head around what i need to do.
Of course now that i finally post a question about this, i figured it out. i just needed to add a second arg to Go(self,mainself) and call back to a function in the main code.
this is what i changed:
def ButtonRelease(self,widget,event):
self.button_down = False
x = self.sel_rect.x
y = self.sel_rect.y
w = self.sel_rect.width
h = self.sel_rect.height
self.drawArea.queue_draw_area(x-1, y-1, w-1, h-1)
if self.debounce == 0:
self.debounce += 1
print("Snipping x:%s y:%s w:%s h:%s" %(x,y,w,h))
self.ThePic = Gdk.pixbuf_get_from_window(self.MaskWin.get_window(), x, y, w, h)
self.MaskWin.hide()
self.GotTheShot = True
self.mainself.ScreenShotCallBack(self.ThePic)
def Go(self,main):
self.mainself = main
self.GotTheShot = False
self.MaskWin.fullscreen()
self.MaskWin.show_all()
self.MaskWin.set_decorated(False)
and here is the call and callback:
def ScreenShotRegion(self):
pic = self.captureregion.Go(self)# self,mainself... self passed by default
pic.savev("region.png",'png',(),())
def ScreenShotCallBack(self,capturepixbuf):
self.window.show_all()
capturepixbuf.savev("region.png",'png',(),())

Python - Change made to class in array not saved

I've been working all day on a bit of code for a maze generation algorithm. In this snippet:
if nextCoords[2] == '<':
cells[x][y].l = False
x, y = nextCoords[0], nextCoords[1]
cells[x][y].r = False
The first line of the if statement, cells[x][y].l = False executes as expected, but for some reason cells[x][y].r = False does not keep the change made.
Here's the full code (with some prints from hopeless debugging):
import random
class Cell():
def __init__(self, x, y):
self.x = x
self.y = y
self.visited = False
self.isStart = False
self.isEnd = False
self.isNormal = True
self.u = True
self.d = True
self.l = True
self.r = True
def __str__(self):
# For console applications
return "{0}{1}{2}{3}{4}{5}{6}".format(
'^' if not self.u else ' ',
'v' if not self.d else ' ',
'<' if not self.l else ' ',
'>' if not self.r else ' ',
'+' if self.isStart else '',
'#' if self.isEnd else '',
' ' if self.isNormal else ''
)
def __repr__(self):
return self.__str__()
def mazegen(w=3, h=3, printOut=False):
x = 0
y = 0
cells = [[Cell(x, y) for x in range(w)] for y in range(h)]
stack = []
cells[x][y].isStart = True
cells[x][y].isNormal = False
while True:
allVisited = True
for i in cells:
for j in i:
if not j.visited:
allVisited = False
if allVisited:
finalCell = cells[ stack[-1][0] ][ stack[-1][1] ]
finalCell.isNormal = False
finalCell.isEnd = True
break
cells[x][y].visited = True
choices = []
if x > 0:
choices.append((x-1, y, '<'))
if x < w-1:
choices.append((x+1, y, '>'))
if y > 0:
choices.append((x, y-1, '^'))
if y < h-1:
choices.append((x, y+1, 'v'))
betterChoices = []
for c in choices:
if not cells[ c[0] ][ c[1] ].visited:
betterChoices.append(c)
if betterChoices != []:
nextCoords = random.choice(betterChoices)
else:
popped = stack.pop()
x, y = popped[0], popped[1]
continue
stack.append((x, y))
print(nextCoords[2])
if nextCoords[2] == '^':
cells[x][y].u = False
print("This cell is now {}.".format(cells[x][y]))
x, y = nextCoords[0], nextCoords[1]
cells[x][y].d = False
print("The next cell is {}.".format(cells[x][y]))
if nextCoords[2] == 'v':
cells[x][y].d = False
print("Old: ({0}, {1})".format(x, y))
print(cells[x][y])
x, y = nextCoords[0], nextCoords[1]
print("New: ({0}, {1})".format(x, y))
print(cells[x][y])
cells[x][y].u = False
print(cells[x][y])
if nextCoords[2] == '<':
cells[x][y].l = False
x, y = nextCoords[0], nextCoords[1]
cells[x][y].r = False
if nextCoords[2] == '>':
cells[x][y].r = False
x, y = nextCoords[0], nextCoords[1]
cells[x][y].l = False
print("Moved {}\n".format(nextCoords[2]))
if printOut:
for i in cells:
for j in i:
print("| ", end='')
print(j, end=' | ')
print()
print("----------"*w)
return cells
after i cleaned up your code it's now working and looking fine :]
import pygame, sys, random
class Cell:
visited=isStart=isEnd=False
u=d=l=r=isNormal=True
def __init__(self,x,y): self.x,self.y = x,y
def mazegen(w,h):
x,y = 0,0
cells = [[Cell(x,y) for y in range(h)] for x in range(w)]
stack = []
cells[x][y].isStart = True
cells[x][y].isNormal = False
while True:
allVisited = True
for i in cells:
for j in i:
if not j.visited: allVisited = False
if allVisited:
finalCell = cells[ stack[-1][0] ][ stack[-1][1] ]
finalCell.isNormal = False
finalCell.isEnd = True
break
cells[x][y].visited = True
choices = []
if x>0: choices.append((x-1, y, '<'))
if x<w-1: choices.append((x+1, y, '>'))
if y>0: choices.append((x, y-1, '^'))
if y<h-1: choices.append((x, y+1, 'v'))
betterChoices = []
for c in choices:
if not cells[c[0]][c[1]].visited: betterChoices.append(c)
if betterChoices != []:
nextCoords = random.choice(betterChoices)
else:
popped = stack.pop()
x,y = popped[0],popped[1]
continue
stack.append((x,y))
if nextCoords[2] == '^':
cells[x][y].u = False
x,y = nextCoords[0], nextCoords[1]
cells[x][y].d = False
if nextCoords[2] == 'v':
cells[x][y].d = False
x,y = nextCoords[0], nextCoords[1]
cells[x][y].u = False
if nextCoords[2] == '<':
cells[x][y].l = False
x,y = nextCoords[0], nextCoords[1]
cells[x][y].r = False
if nextCoords[2] == '>':
cells[x][y].r = False
x,y = nextCoords[0], nextCoords[1]
cells[x][y].l = False
return cells
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
screen = pygame.display.set_mode((400,400))
pygame.display.set_caption("Maze Generator!")
cells = mazegen(10,10)
while True:
key = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type==pygame.QUIT: pygame.quit(); sys.exit()
if event.type==pygame.KEYDOWN:
if key[pygame.K_LALT] and event.key==pygame.K_F4: pygame.quit(); sys.exit() # alt + f4
screen.fill(BLUE)
for i in cells:
for cell in i:
img = pygame.Surface((20,20))
img.fill(WHITE)
if cell.u:pygame.draw.line(img,BLACK,(0,0),(20,0))
if cell.d: pygame.draw.line(img,BLACK,(0,20),(20,20))
if cell.l: pygame.draw.line(img,BLACK,(0,0),(0,20))
if cell.r: pygame.draw.line(img,BLACK,(20,0),(20,20))
screen.blit(img,(cell.x*20, cell.y*20))
pygame.display.flip()
also here's my really compact example. (i use pygame just to save it as an image. lol)
import pygame, random
def maze(*gs):
gs = (gs[0]+3-gs[0]%4,gs[1]+3-gs[1]%4) # rounding grid size for cleaner presentation
adjacent = lambda x,y: [(a,b) for a,b in ((x-2,y),(x+2,y),(x,y-2),(x,y+2)) if (a,b) not in tiles and a+1 and b+1 and a<gs[0] and b<gs[1]]
pos = (gs[0]//2,gs[1]//2); tiles, points, direction = [pos], [], (0,0)
for l in range(((gs[0]-1)//2)*((gs[1]-1)//2)-1): # total loops in algorithm
adj = adjacent(*pos) # get available adjacents
for i in points[::-1]:
if adj: break # we have adjacents, we don't need to backtrack
adj = adjacent(*i) # check if corner has spare adjacents
if adj: points, pos = points[:points.index(i)], i # if so, remove corner, backtrack pos
rand = random.choice(adj); new_dir = rand[0]-pos[0], rand[1]-pos[1] # get random dir
if new_dir != direction and len(adj)>1: points += [pos] # corner and more adj remain
tiles, pos, direction = tiles+[(rand[0]-new_dir[0]//2,rand[1]-new_dir[1]//2),rand], rand, new_dir # add path, move in direction
pygame.init() # lets use pygame
surface = pygame.Surface(gs); surface.fill((0,0,0)) # create black image
for i in tiles: surface.set_at(i,(255,255,255)) # add white path
pygame.image.save(surface,"maze.png") # save as png
maze(100,100) # create 100x100 pixel maze

Categories