The goal is to draw a line (like in MS paint) using OpenGL and Pyqt5.
The problem is all i get is a black window. If something like glRectf(20,20,-20,20) is written in paintGL() it shows up correct.
code -
from OpenGL.GL import *
from PyQt5.QtOpenGL import *
from PyQt5.QtCore import QTimer
a = b = None
class Coordinate():
def __init__(self,x,y):
self.x = x/2 -200 # mapping x [0,800] to [-200,200]
self.y = -2*y/3 + 200 # mapping y [0,600] to [200,-200]
def getx(self):
return self.x
def gety(self):
return self.y
class GlWindow(QGLWidget):
def __init__(self):
print("Entering GL")
super(GlWindow,self).__init__()
self.setAutoBufferSwap(True)
# timer = QTimer()
# timer.timeout.connect(self.update)
# timer.start(10)
def mousePressEvent(self,e):
a = Coordinate(e.x(),e.y())
print(a.getx(),a.gety())
def mouseMoveEvent(self,e):
b = Coordinate(e.x(),e.y())
print(b.getx(),b.gety())
self.updateGL()
def mouseReleaseEvent(self,e):
b = Coordinate(e.x(),e.y())
print(b.getx(),b.gety())
self.updateGL()
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glColor3f(0.0,0.0,1.0)
if a and b is not None:
print("Drawing line")
glBegin(GL_LINES)
glVertex2f(a.getx(),a.gety())
glVertex2f(b.getx(),b.gety())
glEnd()
def resizeGL(self, w, h):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-200, 200, -200, 200, -200.0, 200.0)
glViewport(0, 0, w, h)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def initializeGL(self):
glClearColor(0.10, 0.10, 0.10, 1.0) # Grey
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
glShadeModel(GL_SMOOTH)
Stdout -
Entering GL
66.5 40.0
66.5 40.0
66.5 40.66666666666666
67.0 40.66666666666666
67.5 40.66666666666666
69.0 42.0
70.5 42.66666666666666
72.5 44.66666666666666
74.0 46.0
75.5 46.66666666666666
76.5 48.0
77.5 48.66666666666666
78.5 49.33333333333334
79.0 49.33333333333334
79.0 49.33333333333334
79.5 49.33333333333334
79.5 49.33333333333334
79.5 49.33333333333334
The GlWindow() instance is created in another widget (MainWindow : QMainWindow, for menubar) and set as central Widget. I tried Qtimer and update(), it did not work.
The problem is with the the global variables a and b. They are not being updated in the mouse event callbacks. Try using global a and global b statements in your callbacks.
A better approach would be to make these variables as class attributes and update them as self.a = #whatever.
Related
For my homework I have to create a program that maps dots to a Tkinter window, and creates a serpenstky triangle out of it. I can create it easily with lines, but I dont understand how I am supposed to get the math to work with dots.
My main question is how would I create a full object with dots using math in python for Tkinter?
from Tkinter import *
from random import randint
# the 2D point class
class Point(object):
def __init__(self,x = 0.0 ,y = 0.0):
self._x = float(x)
self._y = float(y)
#property
def x(self):
return self._x
#x.setter
def x(self):
self._x = x
#property
def y(self):
return self._y
#y.setter
def y(self):
self._y = y
#parts from previous program that I left in here just in case
def __str__(self):
return '(' + str(self.x) + ',' + str(self.y) + ')'
def dist(self,other):
return ((self.x - other.x)**2 + (self.y - other.y)**2)**0.5
def midpt(self,other):
x=float((self.x + other.x)/2)
y=float((self.y + other.y)/2)
return Point(x,y)
# the coordinate system class: (0,0) is in the top-left corner
# inherits from the Canvas class of Tkinter
class chaosGame(Canvas):
def __init__(self, master):
Canvas.__init__(self, master, bg="white")
self.colors = ["black"]
self.radius = 0
self.pack(fill=BOTH, expand=1)
def plotPoints(self, n):
min_x = 10
min_y = 0
k=1
max_x = 600
max_y = 520
mid_x = (min_x + max_x)/2
mid_y = (min_y + max_y)/2
x1 = 3**k-1
y1 = 3**k-1
for i in range(n):
point = Point((x1), (y1))
self.plot(point.x, point.y)
k+= 1
def vertPoints(self, n):
topPoint = Point((300), (0))
leftPoint = Point((0), (510))
rightPoint = Point((590), (510))
self.vplot(topPoint.x, topPoint.y)
self.vplot(leftPoint.x, leftPoint.y)
self.vplot(rightPoint.x, rightPoint.y)
def vplot(self, x, y):
color = self.colors[randint(0, len(self.colors) - 1)]
self.create_oval(x, y, x + 10, y + 10, fill= "red")
def plot(self, x, y):
color = self.colors[randint(0, len(self.colors) - 1)]
self.create_oval(x, y, x + self.radius, y + self.radius, fill=color)
##########################################################
WIDTH = 600
HEIGHT = 520
# the number of points to plot
NUM_POINTS = 50000
# create the window
window = Tk()
window.geometry("{}x{}".format(WIDTH, HEIGHT))
window.title("Triangles")
# create the coordinate system as a Tkinter canvas inside the window
s = chaosGame(window)
# plot some random points
s.plotPoints(NUM_POINTS)
s.vertPoints(3)
# wait for the window to close
window.mainloop()
I'm trying to animate a 2D perlin noise in the 3rd dimension using Tkinter un Python 2.7.
It's all working, but the performances are very low (about 2 fps).
I think that the problem is in the "animate" function, in which I create a new image every time I update the noise.
How can I achieve a good frame rate? In this code the image is 640x480px, but I need it to be at least 1080x720px.
I'm open to every type of suggestions (even changing the GUI library).
from Tkinter import *
from noise import snoise3, pnoise3
from PIL import Image, ImageTk
import threading
class Noise:
def __init__(self):
self.root = Tk()
self.w = 640
self.h = 480
self.canvas = Canvas(self.root, width = self.w, height = self.h)
self.canvas.pack()
self.img = Image.new("1",(self.w,self.h))
self.pix = self.img.load()
self.z = 0
octv = 8
freq = 16.0 * octv;
for y in xrange(self.h):
for x in xrange(self.w):
self.pix[x,y] = int(snoise3(x/freq,y/freq,self.z,octaves = octv)*127.0+128.0)
self.image = ImageTk.PhotoImage(self.img)
self.canvas.create_image(self.w/2,self.h/2,image=self.image)
self.canvas.pack()
self.root.after(0, self.tr)
self.root.mainloop()
def tr(self):
self.t1 = threading.Thread(target=self.animate)
self.t1.daemon = True
self.t1.start()
def animate(self):
octv = 8
freq = 16.0 * octv;
while True:
self.z += 0.01
for y in xrange(self.h):
for x in xrange(self.w):
self.pix[x,y] = int(snoise3(x/freq,y/freq,self.z,octaves = octv)*127.0+128.0)
self.image = ImageTk.PhotoImage(self.img)
self.canvas.create_image(self.w/2,self.h/2,image=self.image)
self.canvas.pack()
Noise()
Thank you all ^^
import pygame, sys
pygame.init()
surf = pygame.display.set_mode((1280, 720))
black = (0, 0, 0)
surf.fill(black)
fps_clock = pygame.time.Clock()
pygame.display.flip()
class Player(pygame.sprite.Sprite):
def __init__(self, name, xx, yy):
self.name = name
self.image = pygame.Surface((22, 22))
self.image.fill((130, 100, 200))
self.rect = self.image.get_rect(x = xx, y = yy)
self.x_vel = 0
self.y_vel = 0
def speed(self, speed):
self.speed = speed
def update(self, keys):
if keys[pygame.K_DOWN]:
self.y_vel = 3
elif keys[pygame.K_UP]:
self.y_vel = -3
else:
self.y_vel = 0
if keys[pygame.K_LEFT]:
self.x_vel = -3
elif keys[pygame.K_RIGHT]:
self.x_vel = 3
else:
self.x_vel = 0
self.rect.x += self.x_vel
self.rect.y += self.y_vel
def update1(self, keys):
if keys[pygame.K_s]:
self.y_vel = 3
elif keys[pygame.K_w]:
self.y_vel = -3
else:
self.y_vel = 0
if keys[pygame.K_a]:
self.x_vel = -3
elif keys[pygame.K_d]:
self.x_vel = 3
else:
self.x_vel = 0
self.rect.x += self.x_vel
self.rect.y += self.y_vel
def draw(self, surface):
surf.fill(black)
surface.blit(self.image, self.rect)
player = Player('Tank', 100, 300)
player2 = Player('Tank2', 200, 500)
def main():
while True:
keys = pygame.key.get_pressed()
player.draw(surf)
player2.draw(surf)
player.update(keys)
player2.update1(keys)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
fps_clock.tick(60)
main()
Trying to create 2 sprites, one which can be controlled using the arrow keys, the other using the WASD keyset. It only blits one sprite on the image, which can be controlled using WASD. The other can be controlled by arrow keys if player2.update1(keys) is commented. Please don't beat down on me that much, still kind of a novice.
The problem is that you fill the whole screen with black before you draw each player. That means that when player two is drawn player one gets drawn over with black. Remove line 52 surf.fill(black) from def draw() and add it to the main game loop so that it is only called once.
def main():
while True:
surf.fill(black)
I am new to Python and Pygame. I want to have a screen in pygame with multiple copies of the same images moving around independently. I have tried to write it as a class and then call instances of it inside the while loop, but it doesn't work. Could someone show how can i basically do such a thing using a class?
I've tried to keep everything simple
Example:
import pygame
pygame.init()
WHITE = (255,255,255)
BLUE = (0,0,255)
window_size = (400,400)
screen = pygame.display.set_mode(window_size)
clock = pygame.time.Clock()
class Image():
def __init__(self,x,y,xd,yd):
self.image = pygame.Surface((40,40))
self.image.fill(BLUE)
self.x = x
self.y = y
self.x_delta = xd
self.y_delta = yd
def update(self):
if 0 <= self.x + self.x_delta <= 360:
self.x += self.x_delta
else:
self.x_delta *= -1
if 0 <= self.y + self.y_delta <= 360:
self.y += self.y_delta
else:
self.y_delta *= -1
screen.blit(self.image,(self.x,self.y))
list_of_images = []
list_of_images.append(Image(40,80,2,0))
list_of_images.append(Image(160,240,0,-2))
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
for image in list_of_images:
image.update()
pygame.display.update()
clock.tick(30)
pygame.quit()
Each image can be called individually from the list and moved by simply changing Image.x/y to whatever you want
I am currently using matplotlib.animation.FuncAnimation() to display an animation of my work, on a figure.
It is working very well, and I understand the arguments I am using ( interval, time range , ...) However, I was wondering if there was a way to implement (maybe directly to the figure) a panel containing the animation, a scroll-bar or whatever, which allows me to :
Move forward or backwards quickly to the time zone of interest.
Show at what point of the animation I am ( 10%, then 20%,...).
Basically, is a way to control the animation in python on the figure like the way I would control it as a video file played by a video player?
If needed, this is what looks like the code for this animation :
def init():
im1.set_data(XYslice[0, :, :])
im2.set_data(XZslice[0, Nplans/2:, :])
return([im1, im2])
def animate(t):
im1.set_data(XYslice[t, :, :])
im2.set_data(XZslice[t, Nplans/2:, :])
return [im1, im2]
anim = animation.FuncAnimation(fig, animate, np.arange(Ntime), interval=200,
blit=True, init_func=init, repeat=True)
What you are talking about is a GUI. The simplest example uses the matplotlib inbuilt widgets:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from matplotlib.widgets import Slider, Button
#Setup figure and data
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
delta = 0.5
t = np.arange(0.0, 100.0, 0.1)
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 5.
cmap = plt.cm.rainbow
im = ax.pcolormesh(X, Y, Z, cmap=cmap)
fig.colorbar(im)
axcolor = 'lightgoldenrodyellow'
axtime = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
stime = Slider(axtime, 'Time', 0.0, 100.0, valinit=50.0)
#Routines to reset and update sliding bar
def reset(event):
stime.reset()
def update(val):
time = stime.val/10.
Z = (Z1 - Z2) * time
im.set_array(Z.ravel())
fig.canvas.draw()
#Bind sliding bar and reset button
stime.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
button.on_clicked(reset)
plt.show()
This should be a start. If you want it to look better (and add more functionality) then you need to go to a GUI framework like wxpython, check out this example.
An example which is more inline with your data-structure would go as follows:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from matplotlib.widgets import Slider, Button
#Setup figure and data
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
delta = 0.5
t = np.linspace(0.0, 100.0, 256)
x = np.linspace(-4.0, 4.001, 512)
y = np.linspace(-4.0, 4.001, 512)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
XZslice = np.zeros((256,512,512))
for i in range(t.shape[0]):
XZslice[i,:,:] = (Z1 - Z2) * t[i]/10.
cmap = plt.cm.rainbow
im = ax.pcolormesh(XZslice[128,:,:], cmap=cmap)
fig.colorbar(im)
axcolor = 'lightgoldenrodyellow'
axtime = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
stime = Slider(axtime, 'Time', 0.0, 100.0, valinit=50.0)
#Routines to reset and update sliding bar
def reset(event):
stime.reset()
def update(val):
time = int(stime.val/100.* 256)
im.set_array(XZslice[time,:,:].ravel())
fig.canvas.draw()
#Bind sliding bar and reset button
stime.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
button.on_clicked(reset)
plt.show()