How to fix "RecursionError: maximum recursion depth exceeded" in python - python-3.x

I'm writing a program that draws many circles at random positions in Python.
It breaks and spits a "maximum recursion depth exceeded"
I tried to increase the recursion limit but the python shell crushes and restarts
class Game():
def __init__(self, root):
self. root = root
self.c = Canvas(root, bg = 'white')
self.ball = []
self.start = 0
self.elapsed = 0
self.time = 0
self.i = 0
self.root.bind('<Key>', self.key)
self.show()
def show(self):
self.start = time.perf_counter()
for self.i in range(200):
self.ball.append([Balls(self.c)])
Balls.create_balls(self)
self.elapsed = time.perf_counter()
self.time = self.elapsed - self.start
print(self.time)
self.c.pack(fill = BOTH, expand = 1)
self.update()
def update(self):
self.root.after(30, self.update)
def key(self, event):
if event.char == 'c':
#self.ball[57] = self.c.create_oval(Balls.x-12, Balls.y-12, Balls.x+12, Balls.y+12, fill = 'red')
pass
class Balls(Game):
def __init__(self, c):
super(Balls, self).__init__(c)
self.c = c
self.x = random.randint(50.0, 450.0)
self.y = random.randint(50.0, 650.0)
def create_balls(self):
self.ball[self.i] = self.c.create_oval(self.x-12, self.y-12, self.x+12, self.y+12, fill = 'blue')
def main():
root = tkinter.Tk()
root.geometry('500x700')
root.title('Balls Game')
root.resizable(0, 0)
game = Game(root)
root.mainloop()
if __name__ == '__main__':
main()
And the error is very big but is the same thing repeated several times.
This is the error:
https://i.stack.imgur.com/bPcbd.png

Where exactly does it break?
You shouldn't call .bind every time, you only need to bind an event once at the start, so:
...
self.root.bind('<Key>', self.key)
self.show()
is fine.
(Also self.ball.append([Balls(self.c)]) appends a list with one Balls object to the self.ball list, and then you call Balls.create_balls(self) which might add even more items. Is this desired bahaviour?)

Related

How can I bind a function to the return key when my Tk frames are generated?

I am trying to bind the function validateSubmit to the enter key so the test may be taken at faster speeds, however I cannot figure out where or what to try to bind it to. Inside of the main at the bottom, I commented out the bind that I feel I get the closest results with, however I've been at this long enough to know that its currently beyond my scope of knowledge. This program is my foray into OOP. I also receive a strange error from validateSubmit, however it seems to be caused by tkinter's internal type-casting and has not seemed to do anything more than echo an error back in the terminal.
##########################################################################################
#Imports
import tkinter as tk
from tkinter import ttk
from tkinter import Tk
from random import randint
##########################################################################################
class Storage():
def __init__(self):
self.timeChoice = 0
self.gameScore = 0
self.answer = 0
self.highScore = 0
def set_timeChoice(self,x):
self.timeChoice = x
def set_gameScore(self,x):
self.gameScore = x
def set_answer(self,x):
self.answer = x
def set_highScore(self,x):
self.highScore = x
def save_highScore(self):
timeChoiceVar = str(self.timeChoice)
with open('data' + timeChoiceVar + '.txt',mode='w') as file:
file.write(str(self.highScore))
def get_highScore(self):
timeChoiceVar = str(self.timeChoice)
try:
with open('data' + timeChoiceVar + '.txt',mode='r') as file:
self.highScore = file.read()
except:
with open('data' + timeChoiceVar + '.txt',mode='w') as file:
file.write('0')
##########################################################################################
class AdditionApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.switch_frame(SetTimePage)
def switch_frame(self, frame_class):
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.grid(column=0,row=0,sticky=tk.W)
##########################################################################################
class SetTimePage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.timeVar = tk.IntVar()
data.set_timeChoice(60)
time_frame = ttk.LabelFrame(self,text=' Test Timer Selection ')
time_frame.grid(column=0,row=0,padx=6,pady=8,sticky='WE')
radio1 = tk.Radiobutton(time_frame,text='1 Minute',variable=self.timeVar,value=60,command=lambda: data.set_timeChoice(self.timeVar.get()))
radio1.grid(column=0,row=0,sticky=tk.W)
radio1.select()
radio2 = tk.Radiobutton(time_frame,text='2 Minutes',variable=self.timeVar,value=120,command=lambda: data.set_timeChoice(self.timeVar.get()))
radio2.grid(column=0,row=1,sticky=tk.W)
radio5 = tk.Radiobutton(time_frame,text='5 Minutes',variable=self.timeVar,value=300,command=lambda: data.set_timeChoice(self.timeVar.get()))
radio5.grid(column=0,row=2,sticky=tk.W)
radio10 = tk.Radiobutton(time_frame,text='10 Minutes',variable=self.timeVar,value=600,command=lambda: data.set_timeChoice(self.timeVar.get()))
radio10.grid(column=0,row=3,sticky=tk.W)
start_button = ttk.Button(self,text=' Start ',command=lambda: master.switch_frame(TestPage))
start_button.grid(column=0,row=1,sticky='WE',pady=4)
##########################################################################################
class TestPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.answer = tk.IntVar()
self.scoreVar = 0
data.set_gameScore(0)
data.get_highScore()
self.test_frame = tk.Label(self,text=data.timeChoice)
self.test_frame.grid(column=3,row=0,sticky=tk.N)
self.score_frame = tk.Label(self,text='Score: %d' % data.gameScore)
self.score_frame.grid(column=3,row=1,sticky=tk.N)
self.high_score_frame = tk.Label(self,text='High Score: %s' % data.highScore)
self.high_score_frame.grid(column=3,row=2,sticky=tk.N)
self.solve_frame = ttk.LabelFrame(self,text=' Solve: ')
self.solve_frame.grid(column=0,row=1,columnspan=2,padx=12,sticky=tk.W)
self.equation_label = tk.Label(self.solve_frame,text='')
self.equation_label.grid(column=0,row=0,padx=6,sticky=tk.W)
self.answer_box = ttk.Entry(self.solve_frame,width=2,textvariable=self.answer)
self.answer_box.grid(column=1,row=0,padx=12,sticky=tk.W)
self.answer_box.bind('<Return>', self.validateSubmit)
self.back_button = ttk.Button(self.solve_frame,text=' Back ',command=lambda: master.switch_frame(SetTimePage))
self.back_button.grid(column=3,row=0,padx=12,sticky=tk.N)
self.countdown(data.timeChoice)
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
self.generateEquation()
if self.remaining < 0:
self.test_frame.configure(text=" Time's up! ")
self.submit_answer.configure(state='disabled')
self.answer_box.configure(state='disabled')
else:
self.test_frame.configure(text="Seconds remaining: %d" % self.remaining)
self.high_score_frame.configure(text='High Score: %s' % data.highScore)
self.submit_answer.bind('<Return>', self.validateSubmit)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
def generateEquation(self):
self.x = randint(0,10)
self.y = randint(0,10)
self.z = 0
self.equation_label.configure(text='%d + %d' % (self.x , self.y))
self.answer_box.delete(0,3)
self.answer_box.focus()
self.submit_answer = ttk.Button(self.solve_frame,text=' Submit ',command=self.validateSubmit)
self.submit_answer.grid(column=2,row=0,padx=12,sticky=tk.E)
def validateSubmit(self,event=None):
while self.remaining >= 0:
self.z = self.answer.get()
if self.x + self.y == self.z:
self.scoreVar += 1
data.set_gameScore(self.scoreVar)
self.score_frame.configure(text='Score: %d' % data.gameScore)
self.generateEquation()
if int(data.highScore) < data.gameScore:
data.set_highScore(data.gameScore)
data.save_highScore()
elif self.x + self.y != self.z :
self.generateEquation()
##########################################################################################
if __name__ == "__main__":
data = Storage()
program = AdditionApp()
program.title(' Addition Test ')
#program.bind('<Return>', TestPage.validateSubmit)
program.mainloop()
data.save_highScore
If you want the user to be able to press return after entering an answer instead of pressing the Submit button, then the binding needs to go on the entry widget since that is the widget with keyboard focus.
self.answer_box.bind('<Return>', self.validateSubmit)
Also, you need to make sure that validateSubmit can accept the event option that is passed in. Since you want to use it both for a binding and a button click, the easiest way to do that is to make the event argument optional:
def validateSubmit(self, event=None):

How to combine lists or alternatively create list of lists

I have a memory game where you have to find pairs of tiles with identical images. I want to modify the game so that you have to find pairs of a tile with image and a tile with corresponding text. For example find a tile with an image of a cat and a tile with the text "cat".
To understand the problem, here's the code that I believe is most relevant, showing how I currently use a list of images to create pairs of tiles:
class Tile(object):
def __init__(self, canvas, x, y, image, cardback):
self.image = image
more stuff...
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
self.images = [
photoDog,
more images...
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.images) - 1)
animalImg = self.images[randomInd]
selected.append(animalImg)
selected.append(animalImg)
del self.images[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 108 + 10, y * 108 + 40, selected.pop(), photoCardback))
I'm not sure if I should create another list of texts, or change the list of images to a list of lists with an image and a text.
In case it's needed, here's the full code:
import tkinter as tk
from random import randint
from random import shuffle
import pygame
pygame.init()
class Controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
if True:
self.frames = {}
for F in (PageMG,):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageMG")
self.geometry("800x480")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
class Tile(object):
def __init__(self, canvas, x, y, image, cardback):
self.cardback = cardback
self.canvas = canvas
self.y = y
self.x = x
self.image = image
def drawFaceDown(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "white")
self.canvas.create_image(self.x + 50, self.y + 50, image=self.cardback)
self.isFaceUp = False
def drawFaceUp(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "white")
self.canvas.create_image(self.x + 50, self.y + 50, image=self.image)
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 100):
if(event.y > self.y and event.y < self.y + 100):
return True
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
photoRestart = tk.PhotoImage(file="restart.png")
imgRestart = tk.Label(self, anchor="s", image=photoRestart)
imgRestart.image = photoRestart
buttonRestart = tk.Button(self, activebackground="white",
image=photoRestart, highlightthickness=0, borderwidth=0,
command=lambda: self.restart())
buttonRestart.place(x=560, y=200)
photoDog = tk.PhotoImage(file="Dyr/dog.png")
photoElefant = tk.PhotoImage(file="Dyr/elefant.png")
photoFlamingo = tk.PhotoImage(file="Dyr/flamingo.png")
photoFlodhest = tk.PhotoImage(file="Dyr/flodhest.png")
photoKamel = tk.PhotoImage(file="Dyr/kamel.png")
photoKatt = tk.PhotoImage(file="Dyr/katt.png")
photoKroko = tk.PhotoImage(file="Dyr/krokodille.png")
photoNeshorn = tk.PhotoImage(file="Dyr/neshorn.png")
photoSkilpadde = tk.PhotoImage(file="Dyr/skilpadde.png")
photoStruts = tk.PhotoImage(file="Dyr/struts.png")
photoZebra = tk.PhotoImage(file="Dyr/zebra.png")
photoLove = tk.PhotoImage(file="Dyr/love.png")
photoCardback = tk.PhotoImage(file="cardback.png")
self.cardback = photoCardback
self.riktig_sound = pygame.mixer.Sound("riktig.wav")
self.click_sound = pygame.mixer.Sound("camerashutter.wav")
self.configure(width=650, height=480, bg="white")
self.canvas = tk.Canvas(self, bg="white", width=550, height=480, highlightthickness=0, borderwidth=0)
self.canvas.place(x=0, y=-30)
self.tiles = []
self.images = [
photoDog,
photoElefant,
photoFlamingo,
photoFlodhest,
photoKamel,
photoKatt,
photoKroko,
photoNeshorn,
photoSkilpadde,
photoStruts,
photoZebra,
photoLove
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.images) - 1)
animalImg = self.images[randomInd]
selected.append(animalImg)
selected.append(animalImg)
del self.images[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 108 + 10, y * 108 + 40, selected.pop(), photoCardback))
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
self.flippedThisTurn = 0
self.canvas.bind("<Button-1>", self.mouseClicked)
def mouseClicked(self, event):
for tile in self.tiles:
if tile.isUnderMouse(event) and (self.flippedThisTurn < 2):
if (not(tile.isFaceUp)):
self.clickSound()
tile.drawFaceUp()
self.flippedTiles.append(tile)
self.flippedThisTurn += 1
if (self.flippedThisTurn == 2):
if (self.flippedTiles[-1].image == self.flippedTiles[-2].image): #check last two elements
self.riktig()
self.after(1000, self.checkTiles)
def checkTiles(self):
self.flippedThisTurn = 0
if not(self.flippedTiles[-1].image == self.flippedTiles[-2].image):
self.flippedTiles[-1].drawFaceDown()
self.flippedTiles[-2].drawFaceDown()
del self.flippedTiles[-2:]
def restart(self):
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
def riktig(self):
pygame.mixer.Sound.play(self.riktig_sound)
pygame.mixer.music.stop()
def clickSound(self):
pygame.mixer.Sound.play(self.click_sound)
pygame.mixer.music.stop()
if __name__ == '__main__':
c = Controller()
c.mainloop()
I think the easiest approach, which causes the least changes to your current code is to:
Make your list of images (done)
Make a list of text-based corresponding tiles (to do)
Make a combined list of the two above and draw each image only once instead of twice as you are doing now (easy)
Make a dictionary that you can use to look up matches (see below)
Modify your "check tiles" function to see if they match using the dictionary (one-line change)
The dictionary created can be used bi-directionally so it will find match regardless of how comparison is done.
In [6]: image_tiles = ['dog_img', 'cat_img', 'hen_img']
In [7]: tag_tiles = ['dog', 'cat', 'hen']
In [8]: all_tiles = image_tiles + tag_tiles # use this to draw tiles
In [9]: matches = { k:v for (k, v) in zip(image_tiles, tag_tiles) }
In [10]: # add opposite lookup
In [11]: matches.update([(k, v) for (k, v) in zip(tag_tiles, image_tiles)])
In [12]: matches
Out[12]:
{'dog_img': 'dog',
'cat_img': 'cat',
'hen_img': 'hen',
'dog': 'dog_img',
'cat': 'cat_img',
'hen': 'hen_img'}

Getting spinner working without threading

I am trying to get a spinner working, but without using threads (I had a working version, but it gave me erratic behaviour. After reading online people told me that it's best to avoid threading with tkinter). So I wanted to give the after function a try.
The goal is to have a spinner pop up before starting something is done and have it disappear once the 'something' is done. Here is a minimalistic example of what I have:
import tk
from time import sleep
class Spinner(tk.Toplevel):
def __init__(self, master, text = None, barsize = 10, speed = 100, spinnerSize = 50):
super().__init__(master = master)
self.barsize = barsize
self.speed = speed
self.size = spinnerSize
self.done = False
self.progress = 0
self.forward = True
if text is None:
self.text = 'Something is happening'
else:
self.text = text
self.title(self.text)
self.out = tk.Label(master = self,
text = self.spinnerCalc(),
height = 1,
borderwidth = 0,
width = 80,
bg = self['background'])
self.out.pack()
self.update_self()
def update_self(self):
print(self.progress)
if self.forward:
if self.progress == self.size - self.barsize:
self.forward = False
self.progress -= 1
else:
self.progress += 1
else:
if self.progress == 0:
self.forward = True
self.progress += 1
else:
self.progress -= 1
self.out.config(text = self.spinnerCalc())
if self.done:
self.grab_release()
self.destroy()
else:
# print('entered continue update')
self.update()
# print('step 2')
self.grab_set()
# print('step 3')
self.after(self.speed, self.update_self)
# print('step 4')
def spinnerCalc(self):
#returns something like |------█████--------------|
bar = '|'
barsize = self.barsize
size = self.size
for i in range (self.progress):
bar += '-'
for i in range (barsize):
bar += '\u2588'
for i in range (size-barsize-self.progress):
bar += '-'
bar += '|'
return bar
def stop(self):
self.done = True
root = tk.Tk()
def spin():
spinner = Spinner(root)
a_bunch_of_stuff_happening(10)
spinner.stop()
def a_bunch_of_stuff_happening(amount):
count = 0
for i in range(100000000):
count += i
print(count)
tk.Button(master = root,
text = 'Press me',
command = spin).pack()
tk.Button(master = root,
text = 'Close me',
command = root.destroy).pack()
root.mainloop()
The problem is that the update_self is not running. I do not want to mainloop the Spinner as that will block my a_bunch_of_stuff_happening() function. The a_bunch_of_stuff_happening is also a complicated function which changes attributes of the main root class.
I am stuck with what else to try without going into threading again (which was a big pain last time). Anyone have advice what else I could try to get this to work?

How to safely quit a Toplevel() window on a thread

I want to include a spinner in my program to show some indication that program is doing something when some heavy background calculations are being made. Here is the spinner I came up with, but I am having trouble quitting it safely. I am not sure how else I can make my idea work, and this is my latest code for it. Anyone have any hint on how I can implement this functionality?
import tkinter as tk
from threading import Thread
from time import sleep
class Spinner_top(tk.Toplevel):
def __init__(self, master, text = None, barsize = 10, speed = 0.10, spinnerSize = 50):
super().__init__(master = master)
self.barsize = barsize
self.speed = speed
self.size = spinnerSize
self.done = False
if text is None:
self.text = 'Program is thinking, thus progress is moving'
else:
self.text = text
self.title(self.text)
self.out = tk.Label(master = self,
height = 1,
borderwidth = 0,
width = 80,
bg = self['background'])
self.out.pack()
self.update()
self.thread = Thread(target = self.fill_self)
self.thread.start()
def fill_self(self):
print('start called')
# print(self)
# print('update2')
forward = True
progress = 0
print('entered while loop')
while True:
msg = self.spinnerCalc(progress)
print('message changed')
self.out.configure(text = msg)
print('message updated')
self.update()
print('updated self')
if forward:
if progress == self.size - self.barsize:
forward = False
progress -= 1
else:
progress += 1
else:
if progress == 0:
forward = True
progress += 1
else:
progress -= 1
print(self.done)
if self.done:
break
sleep(self.speed)
return
def spinnerCalc(self, progress):
bar = '|'
barsize = self.barsize
size = self.size
for i in range (progress):
bar += '-'
for i in range (barsize):
bar += '\u2588'
for i in range (size-barsize-progress):
bar += '-'
bar += '|'
return bar
def stop(self):
print('stop called')
self.done = True
self.thread.join()
print('got pass join()')
self.quit()
self.destroy()
root = tk.Tk()
spinner = [None]
def start():
if spinner[0] is None:
spinner[0] = Spinner_top(root,'Program is thinking')
def stop():
if spinner[0] is not None:
spinner[0].stop()
spinner[0] = None
def experiment():
if spinner[0] is None:
spinner[0] = Spinner_top(root,'Try something')
spinner[0].stop()
tk.Button(root,
text = 'start spinner',
command = start).pack()
tk.Button(root,
text = 'stop spinner',
command = stop).pack()
tk.Button(root,
text='experiment',
command = experiment).pack()
tk.Button(root,
text = 'quit',
command = root.destroy).pack()
root.mainloop()
I am running into 2 problems with this:
1. When starting using the 'start spinner' button, the program freezes up.
2. When starting with the 'experiment' button, the code cannot get to print('message updated') line.
#trying to use the spinner using after as per #Nae suggestion (resulted in more problems :/
class Spinner_top(tk.Toplevel):
def __init__(self, master, text = None, barsize = 10, speed = 100, spinnerSize = 50):
super().__init__(master = master)
self.barsize = barsize
self.speed = speed
self.size = spinnerSize
self.progress = 0
self.done = False
if text is None:
self.text = 'Program is thinking, thus progress is moving'
else:
self.text = text
self.title(self.text)
self.out = tk.Label(master = self,
height = 1,
borderwidth = 0,
width = 80,
bg = self['background'])
self.out.pack()
self.fill_self()
def fill_self(self):
print('start called')
# print(self)
# print('update2')
self.forward = True
progress = 0
print('entered while loop')
def foo():
msg = self.spinnerCalc(progress)
print('message changed')
self.out.configure(text = msg)
print('message updated')
self.update()
print('updated self')
if self.forward:
if self.progress == self.size - self.barsize:
self.forward = False
self.progress -= 1
else:
self.progress += 1
else:
if progress == 0:
self.forward = True
self.progress += 1
else:
self.progress -= 1
# print(self.done)
if not self.done:
self.after(self.speed, func = foo)
foo()
return
def spinnerCalc(self, progress):
bar = '|'
barsize = self.barsize
size = self.size
for i in range (progress):
bar += '-'
for i in range (barsize):
bar += '\u2588'
for i in range (size-barsize-progress):
bar += '-'
bar += '|'
return bar
def stop(self):
print('stop called')
self.done = True
self.quit()
self.destroy()
After trying to figure this out, I have just resorted to simple spinner. Better than nothing and technically does the job. If anyone finds a way to make this spinner idea work with an updating gui though, I'd appreciate it.
class Simple_Spinner(tk.Toplevel):
def __init__(self,master,text=None):
super().__init__(master)
self.grab_set()
self.protocol("WM_DELETE_WINDOW", self.do_nothing)
if text is None:
text = 'Program is processing'
tk.Label(self,text=text).pack()
self.update()
def do_nothing(self):
return
def stop(self):
self.grab_release()
self.destroy()
If you can't measure the progress and just want to show it's busy, and user is not supposed to do other things meanwhile, just change the window mouse cursor to a watch:
>>> import tkinter
>>> root = tkinter.Tk()
>>> root.configure(cursor='watch')
and revert in the end with
>>> root.configure(cursor='arrow')
you can adapt this to your code and is about as simple as I can think.

How to change a QImage that is already set up and shown within QWidget?

I want to change an image with my mouse. So, everytime I click somewhere, the image should change. I can show an image only one time. So I need to separate the initialization of everything that is needed to show an image from the part of code that is responsable for building an image.
Here is what I have got by far
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import pyqtSlot
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.gx=1
self.gy=1
self.tlb=QLabel()
self.lbl=QLabel()
self.image = QImage(512, 512, QImage.Format_RGB32)
self.hbox = QHBoxLayout()
self.pixmap = QPixmap()
self.initUI()
def mousePressEvent(self, QMouseEvent):
px = QMouseEvent.pos().x()
py = QMouseEvent.pos().y()
size = self.frameSize()
self.gx = px-size.width()/2
self.gy = py-size.height()/2
self.fillImage()
def initUI(self):
self.hbox = QHBoxLayout(self)
self.pixmap = QPixmap()
size = self.frameSize()
self.fillImage()
self.lbl = QLabel(self)
self.lbl.setPixmap(self.pixmap)
self.hbox.addWidget(self.lbl)
self.setLayout(self.hbox)
self.move(300, 200)
self.setWindowTitle('Red Rock')
self.tlb = QLabel(str(self.gx)+" : "+str(self.gy), self)
self.tlb.move(12,3)
self.show()
def fillImage(self):
for x in range(0, 512):
t = -1+(x/512)*2
color = (1 - (3 - 2*abs(t))*t**2)
for y in range(0, 512):
t1 = -1+(y/512)*2
color1 = (1 - (3 - 2*abs(t1))*t1**2)
result = (255/2)+(color * color1 * (t*self.gx+t1*self.gy) )*(255/2)
self.image.setPixel(x, y, qRgb(result, result, result))
self.pixmap = self.pixmap.fromImage(self.image)
self.tlb = QLabel(str(self.gx)+" : "+str(self.gy), self)
print(self.gx)
self.update()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The print(self.gx) shows me that self.gx is changed, but the image isn't changed at all.
What do I do wrong?
You will have to tell the GUI that it needs to refresh the image.
In QT it seems you will need to call the update() or repaint() methods of the widget.
I've added self.lbl.setPixmap(self.pixmap) into fillImage before self.repaint() and self.update() and now it works, then i changed a little the code and now it looks like this
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import pyqtSlot
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.gx=1
self.gy=1
self.lbl=QLabel()
self.tlb = None
self.image = QImage(512, 512, QImage.Format_RGB32)
self.hbox = QHBoxLayout()
self.pixmap = QPixmap()
self.length = 1
self.initUI()
def mousePressEvent(self, QMouseEvent):
px = QMouseEvent.pos().x()
py = QMouseEvent.pos().y()
size = self.frameSize()
self.gx = px-size.width()/2
self.gy = py-size.height()/2
h = (self.gx**2+self.gy**2)**0.5
self.gx/=h
self.gy/=h
self.gx*=self.length
self.gy*=self.length
self.fillImage()
def wheelEvent(self,event):
self.length+=(event.delta()*0.001)
print(self.length)
def initUI(self):
self.hbox = QHBoxLayout(self)
self.pixmap = QPixmap()
self.move(300, 200)
self.setWindowTitle('Red Rock')
self.addedWidget = None
self.fillImage()
self.setLayout(self.hbox)
self.show()
def fillImage(self):
for x in range(0, 512):
t = -1+(x/512)*2
color = (1 - (3 - 2*abs(t))*t**2)
for y in range(0, 512):
t1 = -1+(y/512)*2
color1 = (1 - (3 - 2*abs(t1))*t1**2)
result = (255/2)+(color * color1 * (t*self.gx+t1*self.gy) )*(255/2)
self.image.setPixel(x, y, qRgb(result, result, result))
self.pixmap = self.pixmap.fromImage(self.image)
if self.lbl == None:
self.lbl = QLabel(self)
else:
self.lbl.setPixmap(self.pixmap)
if self.addedWidget == None:
self.hbox.addWidget(self.lbl)
self.addedWidget = True
if self.tlb==None:
self.tlb = QLabel(str(self.gx)+" : "+str(self.gy), self)
self.tlb.move(12,3)
else:
self.tlb.setText(str(self.gx)+" : "+str(self.gy))
self.repaint()
self.update()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Resources