Update canvas without delete the existing objects - qt

I have a QCanvas with multiple objects on it. How could I update the canvas and put new object onto it without delete the existing ones?
I want the canvas to draw every existing objects, even if I draw a new one. A new object is generated on every mouse event.
class CANVAS(QtGui.QWidget):
def __init__(self , parent):
super(CANVAS , self).__init__(parent)
self.setGeometry( 0 , 30 , 530 , 530 )
self.frame = QtGui.QFrame(self)
self.CLICKED = 1
self.FUNCTION = 0
self.x =""
self.y =""
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
qp.setPen(QtCore.Qt.red)
t = points.point()
print self.x
if self.CLICKED == 1:
qp.drawRect(int(self.x), int(self.y), 10, 10)
self.CLICKED = 0
if self.FUNCTION == 1:
for k in range(0,360,1):
radius = 50
a = float(self.x) + radius * np.cos(k)
b = float(self.y) + radius * np.sin(k)
qp.drawPoint(a,b)
qp.drawPoint(int(self.x), int(self.y))
print "circle done"
self.FUNCTION = 0
elif self.FUNCTION == 2:
start_P = points.point(int(self.x), int(self.y))
a = 15
b = 20
upperL = points.point((int(self.x) + (10 * a)), int(self.y))
P = [start_P, upperL]
dummy1 = LinInt(P, qp)
leftL = points.point((int(self.x)), ((int(self.y))+(10*b)))
P = [start_P, leftL]
dummy2 = LinInt(P, qp)
tmp = dummy1.pop()
rightL = points.point((tmp.getX()),((int(self.y))+(10*b)))
P = [tmp, rightL]
LinInt(P, qp)
P = [leftL, rightL]
LinInt(P, qp)
self.FUNCTION = 0
print "rectangle done"
elif self.FUNCTION == 3:
print "curve"
def mousePressEvent(self, event):
self.x = ""
self.y = ""
coordinates = event.x()
for i in str(coordinates):
if i.isdigit():
self.x+=i
coordinates = event.y()
for i in str(coordinates):
if i.isdigit():
self.y+=i
self.CLICKED = 1
self.update()

In the constructor, create an empty list. In your drawPoints, do:
if self.CLICKED == 1:
rect = QRect(int(self.x), int(self.y), 10, 10)
self.rects.append(rect)
qp.drawRects(self.rects)

Related

Pygame : Pyscroll & Pytmx working on Tiled - Isometric map [duplicate]

I am facing some issues while trying to create a staggered isomteric game map with pygame.
So far I can draw a rectangular one but I have no clue on how to rotate it.
Here is the code:
from pygame.locals import *
import pygame
green = (40,255,30)
brown = (40,60,90)
grass = 0
dirt = 1
colours = {
grass: green,
dirt: brown,
}
tilemap = [
[grass,dirt,dirt,dirt, grass],
[dirt,grass,dirt,dirt, dirt],
[grass, grass,dirt,dirt, grass],
[grass, grass,dirt,dirt, grass],
[dirt,dirt,dirt,dirt,grass]
]
TILESIZE = 50
MAPWIDTH = 5
MAPHEIGHT = 5
pygame.init()
DISPLAYMAP = pygame.display.set_mode((MAPWIDTH*TILESIZE,MAPHEIGHT*TILESIZE))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for row in range(MAPWIDTH):
print
for column in range(MAPHEIGHT):
color = colours[tilemap[row][column]]
pygame.draw.rect(DISPLAYMAP, color, (column * TILESIZE, row * TILESIZE, TILESIZE, TILESIZE))
pygame.display.update()
Here is the result:
And I would like to have something looking like this:
Any help would be really appreciated.
Thank you
Use pygame.transform.rotate() and pygame.transform.scale() to create an isometric pygame.Surface object for each kind of tile:
isometric_tiles = {}
for key, color in colours.items():
tile_surf = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
blit the tiles instead of drawing rectangles:
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = (column + (MAPHEIGHT - row)) * isometric_size // 2
y = 20 + (column + row) * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
Complete example:
from pygame.locals import *
import pygame
green = (40,255,30)
brown = (40,60,90)
grass = 0
dirt = 1
colours = {
grass: green,
dirt: brown,
}
tilemap = [
[grass,dirt,dirt,dirt, grass],
[dirt,grass,dirt,dirt, dirt],
[grass, grass,dirt,dirt, grass],
[grass, grass,dirt,dirt, grass],
[dirt,dirt,dirt,dirt,grass]
]
TILESIZE = 50
MAPWIDTH = 5
MAPHEIGHT = 5
isometric_tiles = {}
for key, color in colours.items():
tile_surf = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
pygame.init()
DISPLAYMAP = pygame.display.set_mode((MAPWIDTH*TILESIZE*2,MAPHEIGHT*TILESIZE))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = (column + (MAPHEIGHT - row)) * isometric_size // 2
y = 20 + (column + row) * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
pygame.display.update()
For the staggered representation you have to change the arrangement of the tiles:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = 20 + column * isometric_size + (row % 2) * isometric_size // 2
y = 20 + row * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
pygame.display.update()

Why do I get an Recursion error whenever i am deleting a letter from a modified stirng And how do I solve it?

I am trying to make a choice game where you can write and rewrite a name of the character in the game but whenever I try to delete a letter whilst testing the code I get an Recursion error
What is the cause to this problem and how to avoid it?
Here is my code
import pygame as pg
pg.init()
import time
win = pg.display.set_mode((900,600))
pg.display.set_caption("Choices")
go = 0
user_text = ''
lbrown = 225, 156, 0
brown = 192, 128, 0
dbrown = 128, 64, 0
black = 0, 0, 0
dgrey = 64, 64, 64
grey = 128,128,128
lgrey = 192,192,192
white = 255, 255, 255
red = 255, 0, 0
orange = 255,128,0
yellow = 255,223,0
lgreen = 128,255,0
green = 0,255,0
dgreen = 0,128,0
turcoise = 0,192,128
lblue = 0,128,255
blue = 0,0,255
dblue = 0,0,128
blurple = 128,0,192
purple = 192,0,192
pirple = 167, 0, 167
rpink = 255,0,128
pink = 255,0,255
startclick = 0
class BG(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface([400,100])
self.image.fill(white)
self.rect = self.image.get_rect()
class START(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface([200,100])
self.image.fill(green)
self.rect = self.image.get_rect()
class START_TEXT(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
font = pg.font.SysFont('Comic Sans MS', 40)
self.image = font.render('Start',False, pink)
self.rect = self.image.get_rect()
bg = BG()
bg.rect.x = 25
bg.rect.y = 100
start = START()
start.rect.x = 450
start.rect.y = 260
start_txt = START_TEXT()
start_txt.rect.x = 495
start_txt.rect.y = 280
all_sprite = pg.sprite.Group()
all_sprite.add(start,start_txt)
win.fill(purple)
def redraw():
all_sprite.draw(win)
pg.display.update()
run = True
while run:
pos = pg.mouse.get_pos()
print(startclick)
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
if start.rect.collidepoint(pos):
start.image.fill(dgreen)
if event.type == pg.MOUSEBUTTONDOWN:
startclick = 1
win.fill(black)
all_sprite.remove(start,start_txt)
else:
start.image.fill(green)
key = pg.key.get_pressed()
if key[pg.K_BACKSPACE]:
all_sprite.remove(bg, user_text)
user_text = user_text[:-1]
go = 1
else:
if event.type == pg.KEYDOWN:
user_text += event.unicode
usertex = user_text
class User_TxT(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
font = pg.font.SysFont('Comic Sans MS', 40)
self.image = font.render(usertex, True, pink)
self.rect = self.image.get_rect()
user_txt = User_TxT()
user_txt.rect.x = 70
user_txt.rect.y = 120
if startclick == 1:
all_sprite.add(bg, user_txt)
pg.display.flip()
redraw()
quit()
pg.quit()
And the Recursion Error
packages\pygame\sprite.py", line 471, in
if isinstance(sprite, Sprite):
RecursionError: maximum recursion depth exceeded while calling a Python object
Why is this happening?
I've modified your User_TxT sprite class to support an update function to change its text. This will prevent the need to remove and recreate the sprite which causes your recursion error.
import pygame as pg
pg.init()
import time
win = pg.display.set_mode((900,600))
pg.display.set_caption("Choices")
go = 0
user_text = ''
lbrown = 225, 156, 0
brown = 192, 128, 0
dbrown = 128, 64, 0
black = 0, 0, 0
dgrey = 64, 64, 64
grey = 128,128,128
lgrey = 192,192,192
white = 255, 255, 255
red = 255, 0, 0
orange = 255,128,0
yellow = 255,223,0
lgreen = 128,255,0
green = 0,255,0
dgreen = 0,128,0
turcoise = 0,192,128
lblue = 0,128,255
blue = 0,0,255
dblue = 0,0,128
blurple = 128,0,192
purple = 192,0,192
pirple = 167, 0, 167
rpink = 255,0,128
pink = 255,0,255
startclick = 0
class BG(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface([400,100])
self.image.fill(white)
self.rect = self.image.get_rect()
class START(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface([200,100])
self.image.fill(green)
self.rect = self.image.get_rect()
class START_TEXT(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
font = pg.font.SysFont('Comic Sans MS', 40)
self.image = font.render('Start',False, pink)
self.rect = self.image.get_rect()
class User_TxT(pg.sprite.Sprite):
def __init__(self, usertex):
pg.sprite.Sprite.__init__(self)
self.update(usertex)
def update(self, usertex):
font = pg.font.SysFont('Comic Sans MS', 40)
self.image = font.render(usertex, True, pink)
self.rect = self.image.get_rect()
bg = BG()
bg.rect.x = 25
bg.rect.y = 100
start = START()
start.rect.x = 450
start.rect.y = 260
start_txt = START_TEXT()
start_txt.rect.x = 495
start_txt.rect.y = 280
user_txt = User_TxT(user_text)
all_sprite = pg.sprite.Group()
all_sprite.add(start,start_txt)
win.fill(purple)
def redraw():
all_sprite.draw(win)
pg.display.update()
run = True
while run:
pos = pg.mouse.get_pos()
print(startclick)
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
if start.rect.collidepoint(pos):
start.image.fill(dgreen)
if event.type == pg.MOUSEBUTTONDOWN:
startclick = 1
win.fill(black)
all_sprite.remove(start,start_txt)
else:
start.image.fill(green)
key = pg.key.get_pressed()
if key[pg.K_BACKSPACE]:
# all_sprite.remove(bg, user_text)
user_text = user_text[:-1]
go = 1
else:
if event.type == pg.KEYDOWN:
user_text += event.unicode
# usertex = user_text
user_txt.update(user_text)
user_txt.rect.x = 70
user_txt.rect.y = 120
if startclick == 1:
all_sprite.add(bg, user_txt)
startclick = 0
pg.display.flip()
redraw()
quit()
pg.quit()
I've tried to make minimal changes to your code to avoid confusion.
EDIT to add some additional explanation:
I changed the User_TxT sprite class to take an input string as its initial text string to display and to also have an update() function so that its text string can be changed. This function is also what we need to do when initialising the sprite, so to prevent duplication, it's called from __init__(). That way you only need to have one sprite. You were also redefining and instantiating the User_TxT class for every event, which is unnecessary.
To help identify the code differences, you can use a diff tool, like WinMerge on Windows, or the command line diff tool on Linux.
If you run into further issues with your code, you should ask a new question, if you create a Minimal, Reproducible Example it will make it easier to help you.

Physical button interacting with tkinter button widget

I'm making an app to run with my Raspberry Pi with Tkinter GUI. The app allready runs well. Now I want to use 2 physical buttons wired to GPIOS that interact with two of the buttons widgets I have in the app. Thanks to helpers in forums I found the way to do it as You can see in the code. But now I need to disabled in some way the physical button in order to avoid accidental push meanwhile the scripts run, as I can easly do with the widgets. I've been googling a lot but I din not found any example of whaT i need. Can some one give me orientation about the method to obtain this. Thanks in advance.
from Tkinter import *
import RPi.GPIO as GPIO
root = Tk()
root.geometry("320x480")
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
shootdelay = 12 #12
shootinterval = 7#7
shootnumber = 5#12
videodelay = 3
selft = 5
newset = 0
endvideo = 5
intertext = "(D:" + str(shootdelay) + ")(I:" + str(shootinterval) + ")(T:" + str(shootnumber) + ")"
flagselftim = 0
flagvideo = 0
videosetmode = 0
flagsetings = 0
flagcancel = 0
flagnewset = 0
secDelay = shootdelay
secInterv = shootinterval
secSelftim = selft
secvideo = videodelay
remainshots = shootnumber
secshoots = shootnumber - 1
seccompte = 0
readyshoots =
class intervalometer:
def __init__(self, master):
self.textBox = Text(root, height=1, width=1, relief=SUNKEN, font=('arial narrow', 17, 'normal'), bg="green",
fg="white")
self.textBox.grid(row=1, column=1, padx=3, pady=2, sticky=NSEW)
self.textBox.insert(END, "READY")
self.botshoot = Button(root, width=18, font=('arial narrow', 19, 'normal'), text="START",
activebackground="#00dfdf")
self.botshoot.grid(row=4, rowspan=2, column=0, columnspan=2, ipady=15, pady=2, sticky=NSEW)
self.botshoot.configure(command=self.start)
self.botStop = Button(root, heigh=2, font=('arial', 18, 'normal'), text="STOP/RESET", activebackground="red")
self.botStop.grid(row=13, rowspan=3, column=0, pady=1, sticky=NSEW)
self.botStop.configure(state=DISABLED, command=self.stop)
self.botQuit = Button(root, width=3, font=('arial', 18, 'normal'), text="QUIT", activebackground="red",
state=NORMAL)
self.botQuit.grid(row=13, rowspan=3, column=1, pady=1, sticky=NSEW)
self.botQuit.configure(command=self.closewindow)
GPIO.add_event_detect(18, GPIO.RISING, callback=self.start, bouncetime=200)
def start(self, *args, **kwargs):
global flagselftim
self.count = 0
self.cancel_id = None
self.botshoot.configure(state=DISABLED)
self.botStop.configure(state=NORMAL)
self.botQuit.configure(state=DISABLED)
self.start_shoot_delay()
def stop(self):
global secDelay
global secSelftim
global selft
global flagvideo
global videosetmode
global secvideo
global remainshots
global secInterv
global readyshoots
flagvideo = 0
videosetmode = 0
secDelay = shootdelay
secInterv = shootinterval
secvideo = videodelay
selft = 5
secSelftim = selft
remainshots = shootnumber
readyshoots = 1
if self.cancel_id is not None:
self.textBox.after_cancel(self.cancel_id)
self.cancel_id = None
self.textBox.insert(END, 0)
self.textBox.delete("1.0", END)
self.botshoot.configure(text="START")
self.botshoot.configure(state=NORMAL)
self.botStop.configure(state=DISABLED)
self.botQuit.configure(state=NORMAL)
self.textBox.insert(END, "READY")
self.textBox.configure(font=('arial narrow', 17, 'normal'), bg="green", fg="white")
def closewindow(self):
root.destroy()
def start_shoot_delay(self):
global secDelay
if secDelay > 9:
contador = " " + str(secDelay)
else:
contador = " " + str(secDelay)
self.textBox.delete("1.0", END)
self.textBox.configure(font=('arial narrow', 17, 'normal'), bg="red", fg="white")
self.botshoot.configure(state=DISABLED)
if self.count < shootdelay:
self.count += 1
self.textBox.insert(END, contador)
self.cancel_id = self.textBox.after(1000, self.start_shoot_delay)
secDelay -= 1
else:
self.count = 0
secDelay = shootdelay
self.start_shoot_interval()
def start_shoot_interval(self):
global secInterv
if remainshots == shootnumber:
self.start_shootings()
else:
if secInterv > 9:
contador = " " + str(secInterv)
else:
contador = " " + str(secInterv)
self.textBox.delete("1.0", END)
self.textBox.configure(font=('arial narrow', 17, 'normal'), bg="red", fg="white")
if self.count < shootinterval:
self.count += 1
self.textBox.insert(END, contador)
self.cancel_id = self.textBox.after(1000, self.start_shoot_interval)
secInterv -= 1
else:
self.count = 0
secInterv = shootinterval
self.start_shootings()
def start_shootings(self):
global remainshots
global videosetmode
global readyshoots
global secSelftim
global selft
remainshots -=1
if secSelftim <5:
txtremain = "SHOOTING = " + str(1) + "/" + str(1)
else:
txtremain = "REMAINING = " + str(remainshots) + "/" + str(shootnumber)
print "BEEP shoot nr",readyshoots, "av", shootnumber
readyshoots +=1
if remainshots >0:
self.start_shoot_interval()
else:
print "BEEP-BEEP-BEEP : end of roll"
self.etstatus.configure(text="END OF ROLL")
root.update_idletasks()
root.after(500)
readyshoots = 1
selft = 5
self.textBox.insert(END, "READY")
self.textBox.configure(font=('arial narrow', 17, 'normal'), bg="green", fg="white")
self.botshoot.configure(state=NORMAL)
self.botStop.configure(state=DISABLED)
self.botQuit.configure(state=NORMAL)
remainshots = shootnumber
intervalometer(root)
root.mainloop()
I don't see how this is even an issue since the start method should lock up the entire program, including the input from the button. If the button detection is in it's own thread or something, you could just add a check. The easiest thing to check is the tk Button state:
def start(self, *args, **kwargs):
if self.botshoot['state'] == DISABLED:
print 'already running'
return # abort this method
self.botshoot.configure(state=DISABLED)
# etc ...

Only one button in a panel with multiple togglebuttons changes color - wxPython

I want to set the color of a toggle button of my choice in the panel that I have created. The problem is that in the numerous toggle buttons that I have displayed on my panel when I want to change the color of each one only the color of the last button changes. Here's my code:
import wx
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.panel = wx.Panel(self,wx.ID_ANY)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.flags_panel = wx.Panel(self, wx.ID_ANY, style = wx.SUNKEN_BORDER)
self.sizer.Add(self.flags_panel)
self.SetSizer(self.sizer,wx.EXPAND | wx.ALL)
self.flags = Flags(self.flags_panel, [8,12])
self.flags.Show()
class Flags (wx.Panel):
def __init__(self,panel, num_flags = []):#,rows = 0,columns = 0,radius = 0, hspace = 0, vspace = 0,x_start = 0, y_start = 0
wx.Panel.__init__(self,panel,-1, size = (350,700))
num_rows = num_flags[0]
num_columns = num_flags[1]
x_pos_start = 10
y_pos_start = 10
i = x_pos_start
j = y_pos_start
buttons = []
for i in range (num_columns):
buttons.append('toggle button')
self.ButtonValue = False
for button in buttons:
index = 0
while index != 15:
self.Button = wx.ToggleButton(self,-1,size = (10,10), pos = (i,j))
self.Bind(wx.EVT_TOGGLEBUTTON,self.OnFlagCreation, self.Button)
self.Button.Show()
i += 15
index += 1
j += 15
i = 10
self.Show()
def OnFlagCreation(self,event):
if not self.ButtonValue:
self.Button.SetBackgroundColour('#fe1919')
self.ButtonValue = True
else:
self.Button.SetBackgroundColour('#14e807')
self.ButtonValue = False
if __name__ == '__main__':
app = wx.App(False)
frame = Frame()
frame.Show()
app.MainLoop()
Your problem is quite simple. The last button is always changed because it's the last button defined:
self.Button = wx.ToggleButton(self,-1,size = (10,10), pos = (i,j))
Each time through the for loop, you reassign the self.Button attribute to a different button. What you want to do is extract the button from your event object and change its background color. So change your function to look like this:
def OnFlagCreation(self,event):
btn = event.GetEventObject()
if not self.ButtonValue:
btn.SetBackgroundColour('#fe1919')
self.ButtonValue = True
else:
btn.SetBackgroundColour('#14e807')
self.ButtonValue = False
See also:
http://www.blog.pythonlibrary.org/2011/09/20/wxpython-binding-multiple-widgets-to-the-same-handler/

PyGame: Viewport click & Drag

I'm trying to duplicate the behavior of some grid viewports (like 3ds Max's Orthometric view)
or map viewers like (GoogleMaps) where we have the map or grid, which is a whole lot bigger
than the screen, and we navigate by clicking somewhere in the viewport and dragging.
So far, i've managed to create a pretty big grid, draw it and make the viewport show only the tiles it should.
Here is my code so far:
import pygame, sys, math
from pygame.locals import *
FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
GRIDWIDTH = 256
GRIDHEIGHT = 256
GRIDSIZE = 256
TILESIZE = 40
BGCOLOR = (128, 128, 128)
FGCOLOR = (64, 64, 64)
GRID = []
FPSCLOCK = pygame.time.Clock()
indexX = None
indexY = None
minVPcoordX = 0
minVPcoordY = 0
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
viewportOffset = (0, 0)
vpStartXTile = 0
vpStartYTile = 0
viewportCoord = (0, 0)
coordX = 0
coordY = 0
movedDistanceX = 0
movedDistanceY = 0
speed = 4
def main():
global FPSCLOCK, DISPLAYSURF
global coordX, coordY
global offsetX, offsetY, negativeOffsetX, negativeOffsetY
global movedDistanceX, movedDistanceY
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
mouseX = 0
mouseY = 0
generateGrid(GRIDSIZE, GRIDSIZE)
LeftButton = False
mousePos = (0, 0)
dragStart = (0,0)
dragEnd = (0,0)
pygame.font.init()
arialFnt = pygame.font.SysFont('Arial', 16)
while True:
DISPLAYSURF.fill(BGCOLOR)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
#X
if coordX < maxVPcoordX:
coordX += speed
elif coordX < minVPcoordX:
coordX = 0
else:
coordX = maxVPcoordX
#Y
if coordY < maxVPcoordY:
coordY += speed
elif coordY < minVPcoordY:
coordY = 0
else:
coordY = maxVPcoordY
#-------------
viewportCoord = (coordX, coordY)
print(coordX, coordY)
vpStartXTile = math.floor(float(viewportCoord[0]/TILESIZE))
vpStartYTile = math.floor(float(viewportCoord[1]/TILESIZE))
GRIDstartTile = GRID[vpStartXTile][vpStartYTile]
negativeOffsetX = viewportCoord[0] - GRID[vpStartXTile][vpStartYTile][0]
negativeOffsetY = viewportCoord[1] - GRID[vpStartXTile][vpStartYTile][1]
offsetX = TILESIZE - negativeOffsetX
offsetY = TILESIZE - negativeOffsetY
repeatX = math.floor(WINDOWWIDTH/TILESIZE)
repeatY = math.floor(WINDOWHEIGHT/TILESIZE)
drawGrid(repeatX, repeatY)
outputLabel = arialFnt.render('(Top-Left)Coordinates: x%s - y%s' % (coordX, coordY), 1, (255,255,255))
DISPLAYSURF.blit(outputLabel, (10, 10))
# frame draw
pygame.display.set_caption("Memory Game - FPS: %.0f" % FPSCLOCK.get_fps())
pygame.display.flip()
pygame.display.update()
FPSCLOCK.tick(FPS)
def generateGrid(xTiles=None, yTiles=None):
global GRID
GRID = []
for i in range(xTiles):
GRID.append([None] * yTiles)
ix = 0
iy = 0
posX = -40
for x in range(len(GRID[ix])):
posX += TILESIZE
posY = -40
iy = 0
for y in range(xTiles):
posY += TILESIZE
position = (posX, posY)
GRID[ix][iy] = position
iy += 1
if ix < xTiles:
ix += 1
else:
return
def drawGrid(x=None, y=None):
lineWidth = 1
xPos = 0
yPos = 0
for i in range(x):
xStart = (xPos + offsetX, 0)
xEnd = (xPos + offsetX, WINDOWHEIGHT + negativeOffsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, xStart, xEnd, lineWidth)
xPos += TILESIZE
for i in range(y):
yStart = (0, yPos + offsetY)
yEnd = (WINDOWWIDTH + negativeOffsetX, yPos + offsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, yStart, yEnd, lineWidth)
yPos += TILESIZE
def moveGrid():
pass
def zoomIn():
pass
def zoomOut():
pass
main()
As you can see, it works as expected (i haven't implemented any form of click&drag
in this sample).
It seems that pygame doesn't have an event for this, so it must be a combination of
MOUSEBUTTONDOWN and MOUSEMOTION.
I've tried storing the positions of the previous frame with the get_pos() and subtracting the current frame's positions, but i can't figure out the next.
It accelerates way too fast..
I've also tried this with the get_rel() method of the mouse, with no success.
(Though i'm pretty sure i wasn't supposed to ++ the mouse position to the screen position)
I researched around to see if anyone else did this but i found only how to drag around something
on a fixed screen. I need the opposite - drag the screen around on a fixed grid.
So, if anyone has any idea or advice on how to make this mechanic, or any link that i could study about it, i would be grateful!
ps: I found something similar but it's written in JS and it's a pain to translate)
I got it working!
It still has some issues on the zoomIn/zoomOut additions but the main problem of dragging
the grid around is fixed.
import pygame, sys, math
from pygame.locals import *
FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
GRIDSIZE = 256
TILESIZE = 40
BGCOLOR = (128, 128, 128)
FGCOLOR = (64, 64, 64)
GRID = []
FPSCLOCK = pygame.time.Clock()
indexX = None
indexY = None
minVPcoordX = 0
minVPcoordY = 0
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
viewportOffset = (0, 0)
vpStartXTile = 0
vpStartYTile = 0
viewportCoord = (0, 0)
coordX = 0
coordY = 0
def main():
global FPSCLOCK, DISPLAYSURF
global coordX, coordY
global offsetX, offsetY, negativeOffsetX, negativeOffsetY
global movedDistanceX, movedDistanceY
global isDragging
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
mouseX = 0
mouseY = 0
generateGrid(GRIDSIZE, GRIDSIZE)
isDragging = False
mousePos = (0, 0)
dragStart = (0,0)
dragEnd = (0,0)
pygame.font.init()
arialFnt = pygame.font.SysFont('Arial', 16)
while True:
DISPLAYSURF.fill(BGCOLOR)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 2:
isDragging = True
elif event.button == 4:
zoomIn()
elif event.button == 5:
zoomOut()
elif event.type == MOUSEMOTION:
mouseRel = pygame.mouse.get_rel()
moveGrid(mouseRel)
elif event.type == MOUSEBUTTONUP:
isDragging = False
viewportCoord = (coordX, coordY)
vpStartXTile = math.floor(float(viewportCoord[0]/TILESIZE))
vpStartYTile = math.floor(float(viewportCoord[1]/TILESIZE))
GRIDstartTile = GRID[vpStartXTile][vpStartYTile]
negativeOffsetX = viewportCoord[0] - GRID[vpStartXTile][vpStartYTile][0]
negativeOffsetY = viewportCoord[1] - GRID[vpStartXTile][vpStartYTile][1]
offsetX = TILESIZE - negativeOffsetX
offsetY = TILESIZE - negativeOffsetY
repeatX = math.floor(WINDOWWIDTH/TILESIZE)
repeatY = math.floor(WINDOWHEIGHT/TILESIZE)
drawGrid(repeatX, repeatY)
outputLabel = arialFnt.render('(Top-Left)Coordinates: x%s - y%s' % (coordX, coordY), 1, (255,255,255))
DISPLAYSURF.blit(outputLabel, (10, 10))
# frame draw
pygame.display.set_caption("Memory Game - FPS: %.0f" % FPSCLOCK.get_fps())
pygame.display.flip()
pygame.display.update()
FPSCLOCK.tick(FPS)
def generateGrid(xTiles=None, yTiles=None):
global GRID
GRID = []
for i in range(xTiles):
GRID.append([None] * yTiles)
ix = 0
iy = 0
posX = -40
for x in range(len(GRID[ix])):
posX += TILESIZE
posY = -40
iy = 0
for y in range(xTiles):
posY += TILESIZE
position = (posX, posY)
GRID[ix][iy] = position
iy += 1
if ix < xTiles:
ix += 1
else:
return
def drawGrid(x=None, y=None):
lineWidth = 1
xPos = 0
yPos = 0
for i in range(x):
xStart = (xPos + offsetX, 0)
xEnd = (xPos + offsetX, WINDOWHEIGHT + negativeOffsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, xStart, xEnd, lineWidth)
xPos += TILESIZE
for i in range(y):
yStart = (0, yPos + offsetY)
yEnd = (WINDOWWIDTH + negativeOffsetX, yPos + offsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, yStart, yEnd, lineWidth)
yPos += TILESIZE
def moveGrid(rel):
global coordX, coordY, isDragging
if isDragging == True:
#X
if coordX <= maxVPcoordX and coordX >= minVPcoordX:
coordX = coordX - rel[0]
if coordX > maxVPcoordX:
coordX = maxVPcoordX
if coordX < minVPcoordX:
coordX = 0
#Y
if coordY <= maxVPcoordY and coordY >= minVPcoordY:
coordY = coordY - rel[1]
if coordY > maxVPcoordY:
coordY = maxVPcoordY
elif coordY < minVPcoordY:
coordY = 0
#-------------
def zoomIn():
global TILESIZE, maxVPcoordX, maxVPcoordY
TILESIZE += 4
print("Tile size: ", TILESIZE)
generateGrid(GRIDSIZE, GRIDSIZE)
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
def zoomOut():
global TILESIZE, maxVPcoordX, maxVPcoordY
TILESIZE -= 4
if TILESIZE <= 0:
TILESIZE = 4
print("Tile size: ", TILESIZE)
generateGrid(GRIDSIZE, GRIDSIZE)
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
main()

Resources