Problem with pyglet drawing black lines instead of green pixels - python

So I'm a beginner programmer with Python I have been playing with pyglet and I've discovered a problem if you run this code:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
image =pyglet.resource.image("logo.png")
intt = None
def pixel(x1,y1):
color = []
size = []
for x in range(x1):
for y in range(y1):
size.append(x)
size.append(y)
color.append(0)
color.append(255)
color.append(0)
vertex_list = pyglet.graphics.vertex_list(x1 * y1,('v2i', size ),
('c3B', color ))
return vertex_list
#window.event
def on_draw():
window.clear()
#vertex_list = pixel(600,500)
#vertex_list.draw(GL_POINTS)
color = []
size = []
for x in range(100):
for y in range(500):
size.append(x+504)
size.append(y)
color.append(0)
color.append(255)
color.append(0)
vertex_list = pyglet.graphics.vertex_list(100 * 500, ('v2i', size),
('c3B', color))
vertex_list.draw(GL_POINTS)
pyglet.app.run()
You will find that the area shown has black lines in it. I've tried to fix it but it does not work and because pyglet is not pretty famous there aren't videos that describe that problem. I hope you know how to fix it because you are my last hope.

If you want to draw a filled area then draw a rectangular GL_POLYGON primitive instead of multiple GL_POINT primitives. That will be much faster and ensures that the area is completely filled:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
def pixel(x1, y1, x2, y2):
size = [x1, y1, x2, y1, x2, y2, x1, y2]
color = []
for _ in range(4):
color += [0, 255, 0]
vertex_list = pyglet.graphics.vertex_list(4, ('v2i', size ), ('c3B', color ))
return vertex_list
#window.event
def on_draw():
window.clear()
vertex_list = pixel(500, 0, 600, 500)
vertex_list.draw(GL_POLYGON)
pyglet.app.run()
If you want to draw an pyglet.image, then generate a texture and a pyglet.graphics.Batch:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
def quadTexture(x, y, w, h, texture):
vertex = [x, y, x+w, y, x+w, y+h, x, y+h]
tex = [0, 0, 1, 0, 1, 1, 0, 1]
batch = pyglet.graphics.Batch()
batch.add(4, GL_QUADS, texture, ('v2i', vertex ), ('t2f', tex ))
return batch
#window.event
def on_draw():
window.clear()
batch.draw()
file = "logo.png"
image = pyglet.image.load(file)
tex = pyglet.graphics.TextureGroup(image.get_texture())
batch = quadTexture(20, 20, image.width, image.height, tex)
pyglet.app.run()
Much easier is to use a pyglet.sprite. e.g:
from pyglet.gl import *
window = pyglet.window.Window(1000,500,"App",resizable=True)
window.set_minimum_size(500,250)
#window.event
def on_draw():
window.clear()
sprite.draw()
file = "logo.png"
image = pyglet.image.load(file)
sprite = pyglet.sprite.Sprite(image, x=20, y=20)
pyglet.app.run()

Related

How to focus on a specific instance of a class using cv2 mouse events

I'm doing a fun exercise where I draw a spline and control it using four handle points. I want to be able to drag any of the four points individually, and this works fine if I hardcode a specific state for each instance of the handle object, but I'd like to make the drag event part of the point class so I can spawn as many handles as I'd like. But now I'm at a bit of a loss because while I can get the most recently defined object to show the correct behavior, the cv2 mouse callback seems to only honor one object at a time.
import cv2
import numpy as np
# Lerp for handles
def lerp(start, end, dt):
lerp_list = []
t = 1
while t >= 0:
x = start[0] + (end[0] - start[0]) * t
y = start[1] + (end[1] - start[1]) * t
lerp_list.append([x, y])
t = round(t-dt, 2)
return np.int32(lerp_list)
# Lerp for spline
def lerp_spline(l1, l2):
lerp_list = []
for pt in range(len(l1)):
t = pt/(len(l1)-1)
x = l1[pt][0] + (l2[pt][0] - l1[pt][0]) * t
y = l1[pt][1] + (l2[pt][1] - l1[pt][1]) * t
lerp_list.append([x, y])
return np.int32(lerp_list)
class Handle:
def __init__(self, x, y):
self.dragging = False
self.x = x + origin[0]
self.y = y + origin[1]
self.pt = [self.x, self.y]
self.radius = 5
def on_mouse_event(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
if (x - self.x)**2 + (y - self.y)**2 < self.radius**2:
self.dragging = True
print("click")
elif event == cv2.EVENT_MOUSEMOVE:
if self.dragging:
self.x = x
self.y = y
self.pt = [self.x, self.y]
print("dragging")
elif event == cv2.EVENT_LBUTTONUP:
self.dragging = False
print("release")
# Canvas Params
height = 512
width = 512
canvas = np.zeros((height, width, 3), dtype=np.uint8)
origin = [int(width/2), int(height/2)]
# Initial Points for Handles
radius = 5
p0 = Handle(0, -150)
p1 = Handle(-224, 200)
p2 = Handle(192, 0)
p3 = Handle(232, 192)
# Mouse events
cv2.namedWindow('Spline')
cv2.setMouseCallback('Spline', p0.on_mouse_event, param=p0)
cv2.setMouseCallback('Spline', p1.on_mouse_event, param=p1)
cv2.setMouseCallback('Spline', p2.on_mouse_event, param=p2)
cv2.setMouseCallback('Spline', p3.on_mouse_event, param=p3)
while True:
# Wipe canvas each frame
canvas = np.zeros((height, width, 3), dtype=np.uint8)
# Spline Functions
t = .05
lp0 = lerp(p0.pt, p1.pt, t)
lp1 = lerp(p3.pt, p2.pt, t)
spline = lerp_spline(lp0, lp1)
# Draw Spline
canvas = cv2.polylines(canvas, [spline], False, (255, 255, 0), 1, lineType=cv2.LINE_AA)
# Draw Handles
canvas = cv2.circle(canvas, lp0[0], 2, (255, 0, 0), -1, lineType=cv2.LINE_AA)
canvas = cv2.circle(canvas, lp1[0], 2, (255, 255, 0), -1, lineType=cv2.LINE_AA)
canvas = cv2.circle(canvas, lp0[-1], 2, (0, 255, 255), -1, lineType=cv2.LINE_AA)
canvas = cv2.circle(canvas, lp1[-1], 2, (0, 0, 255), -1, lineType=cv2.LINE_AA)
canvas = cv2.line(canvas, p0.pt, p1.pt, (255, 255, 255), 1, lineType=cv2.LINE_AA)
canvas = cv2.line(canvas, p2.pt, p3.pt, (255, 255, 255), 1, lineType=cv2.LINE_AA)
# Show Canvas
cv2.imshow('Spline', canvas)
if cv2.waitKey(16) == ord('q'):
break
cv2.destroyAllWindows()
Apologies for the large amount of code. It is fully operational. The part I'm concerned with is from the Handle class down.
Currently, only the most recently referenced Handle object, p3, shows correct functionality. In order to simplify the example I've repeated the mouse callback four times, once for each Handle object. Not an elegant solution but hopefully illustrates the issue.
Intention and result:
I was hoping that the Handle class might behave something like a CSS class, where the mouse event is automatically assigned to all instances of the class, and each instance performs individually. This of course was wishful thinking. What ended up happening was that only the final mouse callback seems to count. So when the script is checking if the mouse is close to self.x or self.y, it's only checking against the most recently defined coordinates.
Thanks for the help!
I figured out a solution. By calling on_mouse_event() as a recursive function, it worked.
def on_mouse_event(event, x, y, flags, param):
for handle in handles:
handle.on_mouse_event(event, x, y, flags, param)
Then later called this function in a single mouse callback:
cv2.setMouseCallback('Spline', on_mouse_event)
Hopefully this obscure little problem will help somebody out someday :)

How to append positional arguments from a file.txt and use them for graphical shapes

I have the positional arguments saved in a file, i have opened the file used keywords to get only the circle positionals saved those positionals to s and, when I try to say circle(s) it says I need more arguments even though their all inside of s.
from tkinter import *
root = Tk()
root.title('Homework 10')
canvas = Canvas(root, width=400, height=400)
canvas.pack()
def circle(x, y, radius, color):
canvas.create_oval(x, y, x + radius, y + radius, fill=color, outline=color)
def rectangle(x, y, width, height, color):
canvas.create_rectangle(x, y, x+width, y+height, fill=color, outline=color)
def triangle(x, y, size, color):
canvas.create_polygon(x-size//2, y, x+size//2, y, x, y-size//2, fill=color,
outline=color)
def go():
with open('file.txt') as (f):
keywords = ('circle')
for s in f:
for word in s.split():
if word in keywords:
s = (s[6:])
circle(s)
How does something like this work?
"""
Assuming that files.txt contains information in the below format
circle 50 50 25 red
circle 100 100 50 blue
triangle 75 75 20 yellow
rectangle 200 200 100 150 purple
"""
from tkinter import *
root = Tk()
root.title('Homework 10')
canvas = Canvas(root, width=400, height=400, bg='white')
canvas.pack()
def circle(x, y, radius, color):
x,y,radius = map(int,(x,y,radius))
canvas.create_oval(x, y, x + radius, y + radius, fill=color, outline=color)
def rectangle(x, y, width, height, color):
x,y,width,height = map(int,(x,y,width,height))
canvas.create_rectangle(x, y, x+width, y+height, fill=color, outline=color)
def triangle(x, y, size, color):
x, y, size = map(int,(x, y, size))
canvas.create_polygon(x-size//2, y, x+size//2, y, x, y-size//2, fill=color,
outline=color)
def go():
with open('files.txt') as (f):
keywords = ('circle','triangle','rectangle')
for line in f.readlines():
words = line.strip().split(' ')
function = words[0]
args = tuple(words[1:])
if function == 'circle':
circle(*args)
if function == 'rectangle':
rectangle(*args)
if function == 'triangle':
triangle(*args)
circle(50,50,25,'green')
root.after_idle(go)
root.mainloop()
You had a few errors in your code in terms of getting the different parts of each line.

How to draw with mouse over webcam in Tkinter?

I have an app that displays the webcam using OpenCV then this is converted to Tkinter (for GUI purposes). I can implement a mouse drawing function but the image update loop keeps covering it. I have tried raise/lower on canvas and labels but to no avail. Anyone got any ideas on this please?
Update Loop:
def update(self):
isTrue, frame = self.vid.getFrame()
#Convert frame to TK and put on canvas
if isTrue:
self.photo = ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
self.image = self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
self.window.after(10, self.update)
Draw Function:
def paint(self, event):
white = (255, 255, 255)
x1, y1 = (event.x - 2), (event.y - 2)
x2, y2 = (event.x + 2), (event.y + 2)
self.canvas.create_oval(x1, y1, x2, y2, fill="white", outline="white")

PyGlet Shape Following The Mouse

So I try to make a "game" with PyGlet:
This is my code:
import pyglet
from pyglet import shapes
window = pyglet.window.Window(800, 600, "PyGlet Window")
circle = shapes.Circle(x = 100, y = 100, radius = 13, color=(255, 255, 255))
def callback(dt):
pass
pyglet.clock.schedule_interval(callback, 0.5)
#window.event
def on_draw():
window.clear()
circle.draw()
pyglet.app.run()
How to make the circle follow the mouse? Thanks!
Implement the on_mouse_motion event (see Working with the mouse) and change the position of the shape (see pyglet.shapes):
#window.event
def on_mouse_motion(x, y, dx, dy):
circle.x = x
circle.y = y

Pyglet text background is not transparent

I am using Pyglet to create a main menu for my python game. I want to draw text on top of a box that would act as its container. Whenever I render the text, instead of it having a transparent background, it draws whatever the glClearColor is set to. This also happens whenever I try and draw an image that has no background.
I am currently using these two lines for my textbox and the text.
self.text_box = pyglet.sprite.Sprite(pyglet.image.load('../Resources/Textures/Menu/text_box.png'),640-150,360-25, batch=self.menuBatch,group=boxGroup)
self.play_text = pyglet.text.Label("Play", font_name="Arial", font_size=32, x=640, y=360, anchor_x='center', anchor_y='center', color=(255,255,255,255), batch=self.menuBatch,group=textGroup)
Then I just call self.menuBatch.draw(). A picture of the problem I am having is:
For transparency effects to work, 'blend' should be enabled in OpenGL.
To enable blend:
glEnable(GL_BLEND) # transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # transparency
This complete snippet is working for me:
import pyglet
from pyglet.gl import *
# Pyglet Window stuff ---------------------------------------------------------
batch = pyglet.graphics.Batch() # holds all graphics
config = Config(sample_buffers=1, samples=4,depth_size=16, double_buffer=True, mouse_visible=False)
window = pyglet.window.Window(fullscreen=False, config=config)
glClearColor( 0, 100, 0, 255) # background color
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE)
glEnable(GL_BLEND) # transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # transparency
play_text = pyglet.text.Label("Play", font_name="Arial", font_size=32, x=240, y=140, anchor_x='center', anchor_y='center', color=(255,0,255,255), batch=batch,group=None)
text_box = pyglet.sprite.Sprite(pyglet.image.load('text_box.png'),240-150,160-25, batch=batch,group=None)
#window.event
def draw(dt):
window.clear()
batch.draw()
if __name__ == "__main__":
pyglet.clock.schedule_interval(draw, 1.0/60)
pyglet.app.run()
I have done something very similar to this before when making the GUI for one of my 3D programs. To make things very simple, I firstly turned off GL_DEPTH_TEST before the draw() call and then I re-enabled GL_DEPTH_TEST after the draw() call. I believe that the reason why the previous answer was working for them is because they didn't have GL_DEPTH_TEST enabled in the first place. I made a button class that you could maybe use as well (It's important to note that the argument "pos" is for the (x, y) coordinate of the lower left corner of the button and the argument "dim" is for the (width, height) dimensions of the button. The "texture_coords" argument refers to the texture coordinates of the background image of the button. If you want the full image, just leave it at the default. The rest is pretty self explanatory). So here's the code:
class Button:
def __init__(self, btn_img_file, text = "Button", pos = (0, 0), dim = (10, 10), text_color = (255, 255, 255), texture_coords = (0, 0, 1, 0, 1, 1, 0, 1)):
self.x, self.y = pos
self.w, self.h = dim
self.text = text
self.img = btn_img_file
self.tex = get_tex(self.img)
self.coords = texture_coords
self.color = text_color
self.batch = pyglet.graphics.Batch()
def on_click(self, mouse_x, mouse_y, function, *args, **kwargs):
x, y = self.x, self.y
X, Y = x + self.w, y + self.h
if mouse_x > x and mouse_x < X and mouse_y > y and mouse_y < Y:
function(*args, **kwargs)
def draw(self):
x, y = self.x, self.y
X, Y = x + self.w, y + self.h
font_size = (self.w // len(self.text)) - 5
label = pyglet.text.Label(self.text,
font_name = 'Times New Roman',
font_size = font_size,
x = x + (self.w // 2), y = y + (self.h // 2),
anchor_x = 'center', anchor_y = 'center',
color = (*self.color, 255))
self.batch.add(4, GL_QUADS, self.tex, ('v2f', (x, y, X, y, X, Y, x, Y)), ('t2f', self.coords))
glDisable(GL_DEPTH_TEST)
self.batch.draw()
label.draw()
glEnable(GL_DEPTH_TEST)
I hope this was helpful!
~ Rick Bowen

Categories

Resources