Grid Generation for minesweeper - python

Hi so I am making a minesweeper game and I am a bit stuck with the grid generation part. This is my code so far:
from random import randint
import pygame
def MineGen():
mineamount = 100
grid_across = 40
grid_up = 25
mine_list = []
my2dthatlist = []
numacoss = 0
for i in range (mineamount):
numacoss = randint(1,40)
my2dthatlist.append(numacoss)
numup = randint(1,25)
my2dthatlist.append(numup)
mine_list.append(my2dthatlist)
my2dthatlist = []
return mine_list
def GridGen():
grid_across = 40
grid_up = 25
GRIDD = [[0]* grid_across for i in range(grid_up)]
return GRIDD
def MineGrid(GridOutMine, mine_list):
mineplace = 0
placeX = 0
placeY = 0
for i in range(100):
mineplace = mine_list[i]
placeX = mineplace[0]
placeY = mineplace[1]
GridOutMine[placeX][placeY] = 1
print(GridOutMine)
mine_list = MineGen()
GridOutMine = GridGen()
MineGrid(GridOutMine, mine_list)
My issue is that i am getting a list index out of range for the
GridOutMine[placeX][placeY] = 1
part. I don't really know why this is. If you could give me some assistance in what to do, or just some general comments on my code, I would really appreciate it thanks.

That's because, unlike range, random.randint outputs numbers within the specified bounds inclusively. That is, randint(1, 25) could output 25, which is not a valid index for a list that's only 25 elements long (since the last index is 24).
In MineGen, you need to change randint(1, 25) to randint(1, 25-1) or randint(1, 24), and likewise for randint(1, 40) which needs to be randint(1, 39). I'd actually suggest randint(0, 24) and (0, 39), but I don't know if that's intentional.
There are many other things that could/should be improved about this code, but I'd suggest you ask for that kind of input over on CodeReview instead of here once your code is working (they don't fix broken code).
EDIT:
Also, you're indexing your grid in the wrong order. It's a list (25-long) of rows (40-long), so you need to index it in the Y dimension first, then X: GridOutMine[placeY][placeX] = 1

If you are trying to build a grid for the game and want to display buttons in a GUI your users can interact with, you may want to start with the following code as a basic framework for the rest of the code you will be writing.
import tkinter
import functools
class MineSweep(tkinter.Frame):
#classmethod
def main(cls, width, height):
root = tkinter.Tk()
window = cls(root, width, height)
root.mainloop()
def __init__(self, master, width, height):
super().__init__(master)
self.__width = width
self.__height = height
self.__build_buttons()
self.grid()
def __build_buttons(self):
self.__buttons = []
for y in range(self.__height):
row = []
for x in range(self.__width):
button = tkinter.Button(self)
button.grid(column=x, row=y)
button['text'] = '?'
command = functools.partial(self.__push, x, y)
button['command'] = command
row.append(button)
self.__buttons.append(row)
def __push(self, x, y):
print('Column = {}\nRow = {}'.format(x, y))
if __name__ == '__main__':
MineSweep.main(10, 10)
If you want a more complete example of a minesweeper game to either borrow ideas from or adapt for your own needs, the following program implements much of the functionality you might want from a finished game.
import tkinter
import functools
import random
from tkinter.simpledialog import askstring, Dialog
from tkinter.messagebox import showinfo
import os.path
################################################################################
class MineSweep(tkinter.Frame):
#classmethod
def main(cls, width, height, mines, scores):
root = tkinter.Tk()
root.resizable(False, False)
root.title('MineSweep')
window = cls(root, width, height, mines, scores)
root.protocol('WM_DELETE_WINDOW', window.close)
root.mainloop()
################################################################################
def __init__(self, master, width, height, mines, scores):
super().__init__(master)
self.__width = width
self.__height = height
self.__mines = mines
self.__wondering = width * height
self.__started = False
self.__playing = True
self.__scores = ScoreTable()
self.__record_file = scores
if os.path.isfile(scores):
self.__scores.load(scores)
self.__build_timer()
self.__build_buttons()
self.grid()
def close(self):
self.__scores.save(self.__record_file)
self.quit()
def __build_timer(self):
self.__secs = tkinter.IntVar()
self.__timer = tkinter.Label(textvariable=self.__secs)
self.__timer.grid(columnspan=self.__width, sticky=tkinter.EW)
self.__after_handle = None
def __build_buttons(self):
self.__reset_button = tkinter.Button(self)
self.__reset_button['text'] = 'Reset'
self.__reset_button['command'] = self.__reset
self.__reset_button.grid(column=0, row=1,
columnspan=self.__width, sticky=tkinter.EW)
self.__reset_button.blink_handle = None
self.__buttons = []
for y in range(self.__height):
row = []
for x in range(self.__width):
button = tkinter.Button(self, width=2, height=1,
text='?', fg='red')
button.grid(column=x, row=y+2)
command = functools.partial(self.__push, x, y)
button['command'] = command
row.append(button)
self.__buttons.append(row)
def __reset(self):
for row in self.__buttons:
for button in row:
button.config(text='?', fg='red')
self.__started = False
self.__playing = True
self.__wondering = self.__width * self.__height
if self.__after_handle is not None:
self.after_cancel(self.__after_handle)
self.__after_handle = None
self.__secs.set(0)
def __push(self, x, y, real=True):
button = self.__buttons[y][x]
if self.__playing:
if not self.__started:
self.__build_mines()
while self.__buttons[y][x].mine:
self.__build_mines()
self.__started = True
self.__after_handle = self.after(1000, self.__tick)
if not button.pushed:
self.__push_button(button, x, y)
elif real:
self.__blink(button, button['bg'], 'red')
elif real:
self.__blink(button, button['bg'], 'red')
def __blink(self, button, from_bg, to_bg, times=8):
if button.blink_handle is not None and times == 8:
return
button['bg'] = (to_bg, from_bg)[times & 1]
times -= 1
if times:
blinker = functools.partial(self.__blink, button,
from_bg, to_bg, times)
button.blink_handle = self.after(250, blinker)
else:
button.blink_handle = None
def __tick(self):
self.__after_handle = self.after(1000, self.__tick)
self.__secs.set(self.__secs.get() + 1)
def __push_button(self, button, x, y):
button.pushed = True
if button.mine:
button['text'] = 'X'
self.__playing = False
self.after_cancel(self.__after_handle)
self.__after_handle = None
self.__blink(self.__reset_button, button['bg'], 'red')
else:
button['fg'] = 'SystemButtonText'
count = self.__total(x, y)
button['text'] = count and str(count) or ' '
self.__wondering -= 1
if self.__wondering == self.__mines:
self.after_cancel(self.__after_handle)
self.__after_handle = None
self.__finish_game()
def __finish_game(self):
self.__playing = False
score = self.__secs.get()
for row in self.__buttons:
for button in row:
if button.mine:
button['text'] = 'X'
if self.__scores.eligible(score):
name = askstring('New Record', 'What is your name?')
if name is None:
name = 'Anonymous'
self.__scores.add(name, score)
else:
showinfo('You did not get on the high score table.')
HighScoreView(self, 'High Scores', self.__scores.listing())
def __total(self, x, y):
count = 0
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.__width and 0 <= y_index < self.__height:
count += self.__buttons[y_index][x_index].mine
if not count:
self.__propagate(x, y)
return count
def __propagate(self, x, y):
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.__width and 0 <= y_index < self.__height:
self.__push(x_index, y_index, False)
def __build_mines(self):
mines = [True] * self.__mines
empty = [False] * (self.__width * self.__height - self.__mines)
total = mines + empty
random.shuffle(total)
iterator = iter(total)
for row in self.__buttons:
for button in row:
button.mine = next(iterator)
button.pushed = False
button.blink_handle = None
################################################################################
class ScoreTable:
def __init__(self, size=10):
self.__data = {999: [''] * size}
def add(self, name, score):
assert self.eligible(score)
if score in self.__data:
self.__data[score].insert(0, name)
else:
self.__data[score] = [name]
if len(self.__data[max(self.__data)]) == 1:
del self.__data[max(self.__data)]
else:
del self.__data[max(self.__data)][-1]
def eligible(self, score):
return score <= max(self.__data)
def listing(self):
for key in sorted(self.__data.keys()):
for name in self.__data[key]:
yield name, key
def load(self, filename):
self.__data = eval(open(filename, 'r').read())
def save(self, filename):
open(filename, 'w').write(repr(self.__data))
################################################################################
class HighScoreView(Dialog):
def __init__(self, parent, title, generator):
self.__scores = generator
super().__init__(parent, title)
def body(self, master):
self.__labels = []
for row, (name, score) in enumerate(self.__scores):
label = tkinter.Label(master, text=name)
self.__labels.append(label)
label.grid(row=row, column=0)
label = tkinter.Label(master, text=str(score))
self.__labels.append(label)
label.grid(row=row, column=1)
self.__okay = tkinter.Button(master, command=self.ok, text='Okay')
self.__okay.grid(ipadx=100, columnspan=2, column=0, row=row+1)
return self.__okay
def buttonbox(self):
pass
################################################################################
if __name__ == '__main__':
MineSweep.main(10, 10, 10, 'scores.txt')
Reference: ActiveState Code » Recipes » MineSweep

Related

Attributes not inherited via tkinter.button

I am writing the number of step-by-step participants within the framework of the classical one. In normal code in def move function I try to reset attributes from one button to unexpected but when I use get_info I get nothing. I've been sitting for 5 hours, I can not figure out how to fix it.
I tried to remake all classes into 1, but even after that it didn't inherit the attributes I needed.
import random
import tkinter as tk
from PIL import Image, ImageTk
import threading
class Color(object):
EMPTY = 0
BLACK = 1
WHITE = 2
class Move:
xstart = -1
ystart = 0
xdest = 0
ydest = 0
class Button(tk.Button, object):
IMG = None
PlayStatus = None
Getstatus = 0
MovementPointsStart = 0
MovementPoints = 0
Heatl = 0
def __init__(self, color, master, x, y, *args, **kwargs):
self.color = color
super(Button, self).__init__(master, width=6, height=3, *args, **kwargs)
self.x = x
self.y = y
self.color = color
def __str__(self):
return f'Юнит {self.x} {self.y} {self.PlayStatus}'
def __repr__(self):
return f'Юнит {self.x} {self.y} {self.PlayStatus}'
def nextturn(self):
if self.MovementPointsStart > self.MovementPoints:
print(self.MovementPointsStart)
self.MovementPoints = self.MovementPointsStart
print('sosnul')
else:
pass
def get_info(self, Button, x, y):
info = 0
if self.color == Color.BLACK:
info = (type(self), self.PlayStatus, self.MovementPointsStart, str('/'), self.MovementPoints, 'Black')
else:
info = (type(self), self.PlayStatus, self.MovementPointsStart, str('/'), self.MovementPoints,'White')
return info
def get_moves(self, Button, turn):
print(turn)
i = 1
while i==1:
print('zestko')
if turn % 2 == 0 and self.color == Color.WHITE:
print('sosnul')
if self.MovementPoints > 0:
print('huiza')
if Button.get_color(Move.xstart, Move.ystart) == Color.WHITE or Button.get_color(Move.xdest, Move.ydest) == Color.EMPTY:
if Move.xdest < Move.xstart:
xend = Move.xstart - Move.xdest
else:
xend = Move.xdest - Move.xstart
if Move.ydest < Move.ystart:
yend = Move.ystart - Move.ydest
else:
yend = Move.ydest - Move.ystart
self.MovementPoints = self.MovementPoints - (xend + yend)
break
break
else:
print("У вас нет очков перемещения")
break
else:
if turn % 2 == 0:
print("Сейчас не ваш ход")
if turn % 2 == 1 and self.color == Color.BLACK:
print('sosnul')
if self.MovementPoints > 0:
print('huiza')
if Button.get_color(Move.xstart, Move.ystart) == Color.BLACK or Button.get_color(Move.xdest, Move.ydest) == Color.EMPTY:
if Move.xdest < Move.xstart:
xend = Move.xstart - Move.xdest
else:
xend = Move.xdest - Move.xstart
if Move.ydest < Move.ystart:
yend = Move.ystart - Move.ydest
else:
yend = Move.ydest - Move.ystart
self.MovementPoints = self.MovementPoints - (xend + yend)
break
else:
print("У вас нет очков перемещения")
break
else:
print("Сейчас не ваш ход")
break
class Game:
turn = 0
ROW = 10
COLUMNS = 10
win = tk.Tk()
win.title('Eve Offline')
win.geometry("900x560")
x=0
y=0
def __init__(self):
self.Button = []
for x in range(Game.ROW):
temp = []
for y in range(Game.COLUMNS):
btn = Button(Color.EMPTY, Game.win, bg='green', x=x, y=y, text='bebra')
temp.append(btn)
self.Button.append(temp)
def set_Button(self):
self.Button[1][2] = Button(Color.BLACK, Game.win, x=1, y=2, text='Suck')
self.Button[1][2].PlayStatus = 1
for x in range (Game.ROW):
for y in range (Game.COLUMNS):
self.Button[x][y].config(command=lambda button=self.Button[x][y]: self.get_click(button))
def create_window(self):
for x in range(Game.ROW):
for y in range(Game.COLUMNS):
btn = self.Button[x][y]
btn.grid(row=x, column=y)
Muvprikaz = tk.Button(bg='green', width=20, height=4, text='MOVE', activebackground='blue')
Muvprikaz.config(command=lambda button=self.Muvprikaz: self.Muvprikaz())
Muvprikaz.place(x=550, y=300)
NextturnB = tk.Button(bg='gray', width=20, height=4, text='Next Turn')
NextturnB.config(command=lambda button=self.nextturn(Game.turn): self.nextturn(button))
NextturnB.place(x=700, y=300)
def start(self):
self.set_Button()
self.create_window()
self.print_button()
self.win.mainloop()
def print_button(self):
for row_btn in self.Button:
print(row_btn)
def get_color(self, x, y):
return self.Button[y][x].color
def get_info(self, x, y):
return self.Button[y][x].get_info(self, x, y)
def move(self):
print(Move.xdest, Move.ydest)
print('sukablat')
print([Move.xdest],[Move.ydest])
self.Button[Move.xdest][Move.ydest].config(background='gray', text='SS')
self.Button[Move.xdest][Move.ydest].config(command=lambda button=self.Button[Move.xdest][Move.ydest]: self.get_click(button))
self.Button[Move.xdest][Move.ydest] = self.Button[Move.xdest][Move.ydest]
self.Button[Move.xstart][Move.ystart] = Button(Color.EMPTY, Game.win, bg='green', x=Move.xstart, y=Move.ystart, text='bebra')
for x in range (Game.ROW):
for y in range (Game.COLUMNS):
self.Button[x][y].config(command=lambda button=self.Button[x][y]: self.get_click(button))
self.create_window()
self.Button[Move.xstart][Move.ystart].Getstatus = 0
print('blatsuka')
self.Button[Move.xstart][Move.ystart].config(bg='green', text='bebra')
self.Update()
def nextturn(self, turn):
self.turn +=1
print(self.turn)
for x in range(Game.ROW):
for y in range(Game.COLUMNS):
if self.Button[x][y] == self.Button[x][y]:
self.Button[x][y].nextturn()
print("hui")
return turn
def get_click(self, clicked_button):
if Button.Getstatus == 0:
print(Button.Getstatus)
x = (clicked_button.x)
y = (clicked_button.y)
Button.Getstatus = Button.Getstatus+1
clicked_button.config(bg='red')
self.print_button()
print(self.get_info(x, y))
print(Button.Getstatus)
Move.xstart = x
Move.ystart = y
Button.Getstatus == 0
elif Button.Getstatus == 1:
Button.Getstatus = 0
print(Button.Getstatus)
clicked_button.config(bg='green')
def dest_click(self, clicked_button):
x = (clicked_button.x)
y = (clicked_button.y)
clicked_button.config(bg='blue')
Move.xdest = x
Move.ydest = y
print(Move.ydest, Move.xdest)
print('eblan')
self.Move()
def Move(self):
self.Button[Move.xstart][Move.ystart].get_moves(self, self.turn)
self.move()
def Muvprikaz(self):
if Move.xstart>-1:
self.print_button()
print(Move.xstart, Move.ystart)
for x in range (Game.ROW):
for y in range (Game.COLUMNS):
self.Button[x][y].config(command=lambda button=self.Button[x][y]: self.dest_click(button))
else:
pass
def Update(self):
for x in range (Game.ROW):
for y in range (Game.COLUMNS):
self.Button[x][y].config(command=lambda button=self.Button[x][y]: self.get_click(button))
Move.xstart = -1
Move.ystart = 0
Move.xdest = 0
Move.ydest = 0
g = 1
game = Game()
game.start()

Unable to detect mouse clicks for randomly generated buttons

I created a program that allows the user to randomly generate buttons on a grid, but I cannot detect if the user presses them or not. Here is what I have so far:
from graphics import *
from time import *
from random import *
class Button:
def __init__(self, win, center, width, height, label):
w,h = width/2.0, height/2.0
x,y = center.getX(), center.getY()
self.xmax, self.xmin = x+w, x-w
self.ymax, self.ymin = y+h, y-h
p1 = Point(self.xmin, self.ymin)
p2 = Point(self.xmax, self.ymax)
self.rect = Rectangle(p1,p2)
self.rect.setFill("blue")
self.rect.draw(win)
self.label = Text(center, label)
self.label.draw(win)
self.label.setSize(8)
self.activate()
def clicked(self, p):
#print("clicked", p.getX(), p.getY(), self.xmin, self.xmax)
return (self.active and
self.xmin <= p.getX() <= self.xmax and
self.ymin <= p.getY() <= self.ymax)
def getLabel(self):
return self.label.getText()
def activate(self):
self.label.setFill("black")
self.rect.setWidth(2)
self.active = True
def deactivate(self):
self.label.setFill("darkgray")
self.rect.setWidth(1)
self.active = False
def setColor(self, color):
self.rect.setFill(color)
class Grid:
def __init__(self, win, startX, startY, numCols, numRows, squareWidth, squareHeight):
self.ButtonMatrix = []
self.numCols = numCols
self.numRows = numRows
for y in range(startY, numRows):
buttonList = []
for x in range(startX,numCols):
label = str(x) + str(y)
buttonList.append(Button(win,Point(x,y), squareWidth, squareHeight, label))
self.ButtonMatrix.append(buttonList)
sleep(0.03)
def getClickPos(self, clickPt):
for y in range(self.numRows):
for x in range(self.numCols):
if self.ButtonMatrix[y][x].clicked(clickPt):
return y,x
def GenerateRandomColor(self, X,Y, color):
self.ButtonMatrix[X][Y].setColor(color)
#def insideBox(x,y):
def setSquareColor(self,r,c,color):
self.ButtonMatrix[r][c].setColor(color)
def setRowColor(self,rowNum,color):
for c in range(15):
self.ButtonMatrix[rowNum][c].setColor(color)
def main():
SIZE = 15
#application window
win = GraphWin("Memory Game", 600, 600)
win.setBackground(color_rgb(45,59,57))
win.setCoords(-3, -3, SIZE + 2, SIZE + 2)
grid = Grid(win, 0, 1, SIZE, SIZE, 1, 1)
quitButton = Button(win, Point(SIZE, SIZE+1), 2, 1, "Quit")
for i in range(10):
X = randrange(13)
Y = randrange(13)
grid.GenerateRandomColor(X,Y, "white")
Coords = X,Y
sleep(0.1)
print(Coords) #checking to see if each button coord will be printed out
pt = win.getMouse()
if grid.getClickPos(pt) == Coords:
print("pressed random button")
else:
print("did not press a random button")
if __name__ == "__main__":
main()

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',(),())

qtpropertyanimation only change the scale

I used animator class to generate ellipseobject and also add animation to them, the goal is only change the scale (from double size to normal size) of the dot, but not do the translate. Right now what I am facing is the dot does shrink, but it moves from the location where (x y value are double with its original x y value) to its original x y location.
Here is the animator class
class Animator:
def __init__(self, animation_config, num_rows, num_cols, background_colour, parent, user_idx = 0, resolution_width = 1920,
resolution_height = 1200, log_file_path = "coordinates.txt"):
with open(log_file_path, "w") as log_file:
log_file.write("")
circle = animation_config["circle"]
self.__cur_circle = None
self.__cur_colour = None
self.__animated_colour = QColor(*circle["colour"])
self.__log_file_path = log_file_path
# Initialize the circle table
diamond_square = DiamondSquare()
self.__user_idx = user_idx % 64
shift_xn = diamond_square.dx[user_idx]
shift_yn = diamond_square.dy[user_idx]
intv_x = (resolution_width - 20) / 10
intv_y = (resolution_height - 20) / 10
print(shift_xn,shift_yn)
self.__circle_table = []
y = 0
for i in range (11):
circles = []
x = 0
for j in range (11):
cir_x = (j * intv_x) + shift_xn
cir_y = (i * intv_y) + shift_yn
print(cir_x,cir_y)
if cir_x <= resolution_width and cir_y <= resolution_height:
circles.append(EllipseObject(parent,cir_x,cir_y, x, y, intv_x, intv_y,
hidden_colour = background_colour,
display_colour=self.__animated_colour))
x += 1
y += 1
self.__circle_table.append(circles)
# Initalize the first animation
self.__first = QPropertyAnimation()
self.__first.setPropertyName(b"scale")
self.__first.setDuration(animation_config["animation_duration"])
self.__first.setStartValue(2)
self.__first.setEndValue(1)
and here is my EllipseObject class
class EllipseObject(QGraphicsWidget):
def __init__(
self,
parent,
x = 0,
y = 0,
ind_x = 0,
ind_y = 0,
intv_x = 0,
intv_y = 0,
width = 20,
height = 20,
hidden = True,
hidden_colour = QColor(Qt.white),
display_colour = QColor(Qt.black)
):
super().__init__(parent)
self.__x = x
self.__y = y
self.__ind_x = ind_x
self.__ind_y = ind_y
self.__intv_x = intv_x
self.__intv_y = intv_y
self.__height = height
self.__width = width
self.hidden = hidden
self.hidden_colour = hidden_colour
self.display_colour = display_colour
def paint(self, painter, option, widget = None):
colour = QColor(Qt.white) if self.hidden else self.display_colour
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(colour)
painter.setBrush(QBrush(colour))
# painter.drawEllipse(self.__x, self.__y, self.__width, self.__height)
painter.drawEllipse(self.boundingRect())
# point = self.mapToScene(self.boundingRect().center())
# print("Draw this ",point.x(), point.y())
def get_coordinates(self):
point = self.mapToScene(self.boundingRect().center())
return (point.x(), point.y())
def boundingRect(self):
return QRectF(self.__x,
self.__y,
self.__height, self.__width)
What you are probably observing is that it is rising with respect to the upper left corner and will give you the appearance that it is moving. The QGraphicsItem has a property called transformOriginPoint with respect to which transformations, such as rotation and scaling, are performed and which is found at point (0, 0). In your case your code has several errors, you must set the position with setPos() and not through boundingRect(), so that the scaling is with respect to the center of the item establishes that the boundingRect() is symmetric.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class EllipseObject(QGraphicsWidget):
def __init__(
self,
parent=None,
x = 0,
y = 0,
ind_x = 0,
ind_y = 0,
intv_x = 0,
intv_y = 0,
width = 20,
height = 20,
hidden = True,
hidden_colour = QColor(Qt.white),
display_colour = QColor(Qt.black)
):
super().__init__(parent)
self.__x = x
self.__y = y
self.__ind_x = ind_x
self.__ind_y = ind_y
self.__intv_x = intv_x
self.__intv_y = intv_y
self.__height = height
self.__width = width
self.hidden = hidden
self.hidden_colour = hidden_colour
self.display_colour = display_colour
self.setPos(self.__x, self.__y)
def paint(self, painter, option, widget = None):
colour = QColor(Qt.white) if self.hidden else self.display_colour
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(colour)
painter.setBrush(QBrush(colour))
# painter.drawEllipse(self.__x, self.__y, self.__width, self.__height)
painter.drawEllipse(self.boundingRect())
def get_coordinates(self):
return (self.pos().x(), self.pos().y())
def boundingRect(self):
return QRectF(-0.5*self.__width,
-0.5*self.__height,
self.__height, self.__width)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = QGraphicsView()
scene = QGraphicsScene(w)
w.setScene(scene)
it = EllipseObject(x=100, y=100, hidden=False)
scene.addItem(it)
animation = QPropertyAnimation(it, b"scale")
animation.setDuration(2000)
animation.setStartValue(2)
animation.setEndValue(1)
animation.start()
w.show()
sys.exit(app.exec_())
Note:
Instead of using flag called hidden, use setVisible(boolean), show() or hide() directly. Qt already has many methods implemented, the wheel does not reinvent.

How to stop other processes or finish processes in Python / Tkinter program

I have been drawing some graphics with a Python / Tkinter program. The program has a main menu with menu items for drawing different figures. It works quite well but I came up against a problem. If the program is part way through drawing one figure and the user clicks to draw a second figure then the program draws the second figure, but when it has finished drawing the second figure it goes back and finishes drawing the first figure. What I want it to do is stop drawing the first figure and not go back to drawing the first figure even when the second figure has finished drawing. I created an simpler example program to demonstrate the scenario. To see the problem in this program click "Draw -> Red" and then click "Draw -> Blue" before the red has finished drawing. How do I get the program to abort any previous drawing? Here is the example program:
from tkinter import *
import random
import math
def line(canvas, w, h, p, i):
x0 = random.randrange(0, w)
y0 = random.randrange(0, h)
x1 = random.randrange(0, w)
y1 = random.randrange(0, h)
canvas.create_line(x0, y0, x1, y1, fill=p.col(i))
class Color:
def __init__(self, r, g, b):
self.red = r
self.gre = g
self.blu = b
def hexVal(self, v):
return (hex(v)[2:]).zfill(2)
def str(self):
return "#" + self.hexVal(self.red) + self.hexVal(self.gre) + self.hexVal(self.blu)
class Palette:
def __init__(self, n0, y):
self.colors = []
self.n = n0
self.m = 0
if y == "red":
self.red()
elif y == "blue":
self.blue()
def add(self, c):
self.colors.append(c)
self.m += 1
def red(self):
self.add(Color(127, 0, 0))
self.add(Color(255, 127, 0))
def blue(self):
self.add(Color(0, 0, 127))
self.add(Color(0, 127, 255))
def col(self, i):
k = i % (self.n*self.m)
z = k // self.n
j = k % self.n
c0 = self.colors[z]
c1 = self.colors[(z + 1) % self.m]
t0 = (self.n - j)/self.n
t1 = j/self.n
r = int(math.floor(c0.red*t0 + c1.red*t1))
g = int(math.floor(c0.gre*t0 + c1.gre*t1))
b = int(math.floor(c0.blu*t0 + c1.blu*t1))
c = Color(r, g, b)
return c.str()
def upd(canvas):
try:
canvas.update()
return True
except TclError:
return False
def tryLine(canvas, w, h, p, i, d):
try:
line(canvas, w, h, p, i)
if i % d == 0:
upd(canvas)
return True
except TclError:
return False
class MenuFrame(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.WIDTH = 800
self.HEIGHT = 800
self.canvas = Canvas(self.parent, width=self.WIDTH, height=self.HEIGHT)
self.pack(side=BOTTOM)
self.canvas.pack(side=TOP, fill=BOTH, expand=1)
self.parent.title("Line Test")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
self.parent.protocol('WM_DELETE_WINDOW', self.onExit)
menu = Menu(menubar)
menu.add_command(label="Red", command=self.onRed)
menu.add_command(label="Blue", command=self.onBlue)
menu.add_command(label="Exit", command=self.onExit)
menubar.add_cascade(label="Draw", menu=menu)
self.pRed = Palette(256, "red")
self.pBlue = Palette(256, "blue")
def onRed(self):
# How to abort here any processes currently running?
self.canvas.delete("all")
for i in range(0, 7000):
tryLine(self.canvas, self.WIDTH, self.HEIGHT, self.pRed, i, 100)
upd(self.canvas)
def onBlue(self):
# How to abort here any processes currently running?
self.canvas.delete("all")
for i in range(0, 7000):
tryLine(self.canvas, self.WIDTH, self.HEIGHT, self.pBlue, i, 100)
upd(self.canvas)
def onExit(self):
self.canvas.delete("all")
self.parent.destroy()
def main():
root = Tk()
frame = MenuFrame(root)
root.mainloop()
if __name__ == '__main__':
main()
That's the simple example? ;)
You can add a variable that tracks the currently selected method, then check if that variable exists before completing the for loop. Here's an even simpler example:
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
Button(self, text='Task A', command=self._a).pack()
Button(self, text='Task B', command=self._b).pack()
self.current_task = None # this var will hold the current task (red, blue, etc)
def _a(self):
self.current_task = 'a' # set the current task
for i in range(1000):
if self.current_task == 'a': # only continue this loop if its the current task
print('a')
self.update()
def _b(self):
self.current_task = 'b'
for i in range(1000):
if self.current_task == 'b':
print('b')
self.update()
root = Tk()
Example(root).pack()
root.mainloop()
Let me know if that doesn't make sense or doesn't work out for you.

Categories