So i'm trying to make a function for displaying a circle, here's the function :
def drawCircle(x,y,size):
glColor3f(1,1,1)
glBegin(GL_TRIANGLE_STRIP)
glVertex2f(0,0)
glVertex2f(100,0)
glVertex2f(100,100)
glVertex2f(0,100)
But when i try it, nothing display. If you have the solution, please explain me in detail, I'm not a profesional in opengl or in pyopengl.
Here's the code :
import glfw
from OpenGL.GL import *
from OpenGL.GLUT import *
import numpy as np
import pyrr
from random import *
from math import *
buffer = bytearray(800 * 600 * 3)
display = (800,600)
if not glfw.init():
raise Exception("glfw can not be initialized!")
def CreateWindow(title="Baguette game",width=800,height=600):
display = (width,height)
window = glfw.create_window(width,height,title, None, None)
glfw.set_window_pos(window,400,200)
glfw.make_context_current(window)
return window
def DrawTriangle(pointA=[-0.5, -0.5, 0.0],pointB=[0.5, -0.5,0.0],
pointC=[-0.5, 0, 0.0],color=[1.0,1.0,1.0]):
vertices = [pointA[0], pointA[1], pointA[2],
pointB[0], pointB[1], pointB[2],
pointC[0], pointC[1], pointC[2]]
colors = [color[0], color[1], color[2],
color[0], color[1], color[2],
color[0], color[1], color[2] ]
v = np.array(vertices,dtype=np.float32)
c = np.array(colors, dtype=np.float32)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT,0,v)
glEnableClientState(GL_COLOR_ARRAY)
glColorPointer(3, GL_FLOAT,0,c)
glDrawArrays(GL_TRIANGLES,0,3)
def DrawRectangle(size=[0.5,0.5],position=[0,0],color=[1.0,1.0,1.0]):
DrawTriangle([position[0]-size[0]/2,position[1]+size[1]/2,0],
[position[0]+size[0]/2,position[1]+size[1]/2,0],
[position[0]+size[0]/2,position[1]-size[1]/2,0],
color)
DrawTriangle([position[0]-size[0]/2,position[1]-size[1]/2,0],
[position[0]+size[0]/2,position[1]-size[1]/2,0],
[position[0]-size[0]/2,position[1]+size[1]/2,0],
color)
def Pixel(x,y):
buffer_data = [randint(0,255), 0, 0] * (x * y)
buffer = (GLubyte * (x * y * 3))(*buffer_data)
glDrawPixels(x, y, GL_RGB, GL_UNSIGNED_BYTE, buffer)
def drawCircle(x,y,size):
glColor3f(1,1,1)
glBegin(GL_TRIANGLE_STRIP)
glVertex2f(0,0)
glVertex2f(100,0)
glVertex2f(100,100)
glVertex2f(0,100)
if __name__=="__main__":
window = CreateWindow()
initialPosition = (0,0,0)
z=1
while not glfw.window_should_close(window):
glfw.poll_events()
#glClear(GL_COLOR_BUFFER_BIT)
DrawRectangle([1,1],[-0.5,0],[1,0,0])
DrawRectangle([1,1],[0.5,0],[0,1,0])
glfw.swap_buffers(window)
glfw.terminate()
You don't use the x, y and size arguments at all. A glBegin/glEnd sequence must be delimited with glEnd. e.g.:
def drawCircle(cx, cy, radius):
glColor3f(1,1,1)
glBegin(GL_TRIANGLE_FAN)
glVertex2f(cx, cy)
for a in range(361):
x = cx + radius * cos(radians(a))
y = cy + radius * sin(radians(a))
glVertex2f(x, y)
glEnd()
For example draw a circle in the center of the viewport:
drawCircle(0, 0, 0.5)
I recommend reading a good OpenGL tutorial. e.g.: LearnOpenGL
Related
I'm running a code from github site and it has this error ( last lines) :
File "D:\Anaconda3\lib\site-packages\pyqtgraph\opengl\GLViewWidget.py", line 152, in viewMatrix
tr.translate(-center.x(), -center.y(), -center.z())
AttributeError: 'int' object has no attribute 'x'
I found that the error is related to pyqtgraph libraries and i didn't change their files
just install the last versions of PyOpenGl and PyQtGraph in spyder
can you please help me in this error?
We need more information,
The problem seems to be related to the fact that center is an int but you use it as an object with attributs
Can we see center's initialization ?
the GlViewWidget.py file :
from ..Qt import QtCore, QtGui, QtOpenGL, QT_LIB
from OpenGL.GL import *
import OpenGL.GL.framebufferobjects as glfbo
import numpy as np
from .. import Vector
from .. import functions as fn
##Vector = QtGui.QVector3D
ShareWidget = None
class GLViewWidget(QtOpenGL.QGLWidget):
"""
Basic widget for displaying 3D data
- Rotation/scale controls
- Axis/grid display
- Export options
High-DPI displays: Qt5 should automatically detect the correct resolution.
For Qt4, specify the ``devicePixelRatio`` argument when initializing the
widget (usually this value is 1-2).
"""
def __init__(self, parent=None, devicePixelRatio=None):
global ShareWidget
if ShareWidget is None:
## create a dummy widget to allow sharing objects (textures, shaders, etc) between views
ShareWidget = QtOpenGL.QGLWidget()
QtOpenGL.QGLWidget.__init__(self, parent, ShareWidget)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.opts = {
'center': Vector(0,0,0), ## will always appear at the center of the widget
'distance': 10.0, ## distance of camera from center
'fov': 60, ## horizontal field of view in degrees
'elevation': 30, ## camera's angle of elevation in degrees
'azimuth': 45, ## camera's azimuthal angle in degrees
## (rotation around z-axis 0 points along x-axis)
'viewport': None, ## glViewport params; None == whole widget
'devicePixelRatio': devicePixelRatio,
}
self.setBackgroundColor('k')
self.items = []
self.noRepeatKeys = [QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]
self.keysPressed = {}
self.keyTimer = QtCore.QTimer()
self.keyTimer.timeout.connect(self.evalKeyState)
self.makeCurrent()
def addItem(self, item):
self.items.append(item)
if hasattr(item, 'initializeGL'):
self.makeCurrent()
try:
item.initializeGL()
except:
self.checkOpenGLVersion('Error while adding item %s to GLViewWidget.' % str(item))
item._setView(self)
#print "set view", item, self, item.view()
self.update()
def removeItem(self, item):
self.items.remove(item)
item._setView(None)
self.update()
def initializeGL(self):
self.resizeGL(self.width(), self.height())
def setBackgroundColor(self, *args, **kwds):
"""
Set the background color of the widget. Accepts the same arguments as
pg.mkColor() and pg.glColor().
"""
self.opts['bgcolor'] = fn.glColor(*args, **kwds)
self.update()
def getViewport(self):
vp = self.opts['viewport']
dpr = self.devicePixelRatio()
if vp is None:
return (0, 0, int(self.width() * dpr), int(self.height() * dpr))
else:
return tuple([int(x * dpr) for x in vp])
def devicePixelRatio(self):
dpr = self.opts['devicePixelRatio']
if dpr is not None:
return dpr
if hasattr(QtOpenGL.QGLWidget, 'devicePixelRatio'):
return QtOpenGL.QGLWidget.devicePixelRatio(self)
else:
return 1.0
def resizeGL(self, w, h):
pass
#glViewport(*self.getViewport())
#self.update()
def setProjection(self, region=None):
m = self.projectionMatrix(region)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
a = np.array(m.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
def projectionMatrix(self, region=None):
if region is None:
dpr = self.devicePixelRatio()
region = (0, 0, self.width() * dpr, self.height() * dpr)
x0, y0, w, h = self.getViewport()
dist = self.opts['distance']
fov = self.opts['fov']
nearClip = dist * 0.001
farClip = dist * 1000.
r = nearClip * np.tan(fov * 0.5 * np.pi / 180.)
t = r * h / w
## Note that X0 and width in these equations must be the values used in viewport
left = r * ((region[0]-x0) * (2.0/w) - 1)
right = r * ((region[0]+region[2]-x0) * (2.0/w) - 1)
bottom = t * ((region[1]-y0) * (2.0/h) - 1)
top = t * ((region[1]+region[3]-y0) * (2.0/h) - 1)
tr = QtGui.QMatrix4x4()
tr.frustum(left, right, bottom, top, nearClip, farClip)
return tr
def setModelview(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
m = self.viewMatrix()
a = np.array(m.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
def viewMatrix(self):
tr = QtGui.QMatrix4x4()
tr.translate( 0.0, 0.0, -self.opts['distance'])
tr.rotate(self.opts['elevation']-90, 1, 0, 0)
tr.rotate(self.opts['azimuth']+90, 0, 0, -1)
center = self.opts['center']
tr.translate(-center.x(), -center.y(), -center.z())
return tr
def itemsAt(self, region=None):
"""
Return a list of the items displayed in the region (x, y, w, h)
relative to the widget.
"""
region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
#buf = np.zeros(100000, dtype=np.uint)
buf = glSelectBuffer(100000)
try:
glRenderMode(GL_SELECT)
glInitNames()
glPushName(0)
self._itemNames = {}
self.paintGL(region=region, useItemNames=True)
finally:
hits = glRenderMode(GL_RENDER)
items = [(h.near, h.names[0]) for h in hits]
items.sort(key=lambda i: i[0])
return [self._itemNames[i[1]] for i in items]
def paintGL(self, region=None, viewport=None, useItemNames=False):
"""
viewport specifies the arguments to glViewport. If None, then we use self.opts['viewport']
region specifies the sub-region of self.opts['viewport'] that should be rendered.
Note that we may use viewport != self.opts['viewport'] when exporting.
"""
if viewport is None:
glViewport(*self.getViewport())
else:
glViewport(*viewport)
self.setProjection(region=region)
self.setModelview()
bgcolor = self.opts['bgcolor']
glClearColor(*bgcolor)
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
self.drawItemTree(useItemNames=useItemNames)
def drawItemTree(self, item=None, useItemNames=False):
if item is None:
items = [x for x in self.items if x.parentItem() is None]
else:
items = item.childItems()
items.append(item)
items.sort(key=lambda a: a.depthValue())
for i in items:
if not i.visible():
continue
if i is item:
try:
glPushAttrib(GL_ALL_ATTRIB_BITS)
if useItemNames:
glLoadName(i._id)
self._itemNames[i._id] = i
i.paint()
except:
from .. import debug
debug.printExc()
msg = "Error while drawing item %s." % str(item)
ver = glGetString(GL_VERSION)
if ver is not None:
ver = ver.split()[0]
if int(ver.split(b'.')[0]) < 2:
print(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
else:
print(msg)
finally:
glPopAttrib()
else:
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
try:
tr = i.transform()
a = np.array(tr.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
self.drawItemTree(i, useItemNames=useItemNames)
finally:
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
def setCameraPosition(self, pos=None, distance=None, elevation=None, azimuth=None):
if pos is not None:
self.opts['center'] = pos
if distance is not None:
self.opts['distance'] = distance
if elevation is not None:
self.opts['elevation'] = elevation
if azimuth is not None:
self.opts['azimuth'] = azimuth
self.update()
def cameraPosition(self):
"""Return current position of camera based on center, dist, elevation, and azimuth"""
center = self.opts['center']
dist = self.opts['distance']
elev = self.opts['elevation'] * np.pi/180.
azim = self.opts['azimuth'] * np.pi/180.
pos = Vector(
center.x() + dist * np.cos(elev) * np.cos(azim),
center.y() + dist * np.cos(elev) * np.sin(azim),
center.z() + dist * np.sin(elev)
)
return pos
def orbit(self, azim, elev):
"""Orbits the camera around the center position. *azim* and *elev* are given in degrees."""
self.opts['azimuth'] += azim
self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90)
self.update()
def pan(self, dx, dy, dz, relative='global'):
"""
Moves the center (look-at) position while holding the camera in place.
============== =======================================================
**Arguments:**
*dx* Distance to pan in x direction
*dy* Distance to pan in y direction
*dx* Distance to pan in z direction
*relative* String that determines the direction of dx,dy,dz.
If "global", then the global coordinate system is used.
If "view", then the z axis is aligned with the view
direction, and x and y axes are inthe plane of the
view: +x points right, +y points up.
If "view-upright", then x is in the global xy plane and
points to the right side of the view, y is in the
global xy plane and orthogonal to x, and z points in
the global z direction.
============== =======================================================
Distances are scaled roughly such that a value of 1.0 moves
by one pixel on screen.
Prior to version 0.11, *relative* was expected to be either True (x-aligned) or
False (global). These values are deprecated but still recognized.
"""
# for backward compatibility:
relative = {True: "view-upright", False: "global"}.get(relative, relative)
if relative == 'global':
self.opts['center'] += QtGui.QVector3D(dx, dy, dz)
elif relative == 'view-upright':
cPos = self.cameraPosition()
cVec = self.opts['center'] - cPos
dist = cVec.length() ## distance from camera to center
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point
xScale = xDist / self.width()
zVec = QtGui.QVector3D(0,0,1)
xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized()
yVec = QtGui.QVector3D.crossProduct(xVec, zVec).normalized()
self.opts['center'] = self.opts['center'] + xVec * xScale * dx + yVec * xScale * dy + zVec * xScale * dz
elif relative == 'view':
# pan in plane of camera
elev = np.radians(self.opts['elevation'])
azim = np.radians(self.opts['azimuth'])
fov = np.radians(self.opts['fov'])
dist = (self.opts['center'] - self.cameraPosition()).length()
fov_factor = np.tan(fov / 2) * 2
scale_factor = dist * fov_factor / self.width()
z = scale_factor * np.cos(elev) * dy
x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy)
y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy)
self.opts['center'] += QtGui.QVector3D(x, -y, z)
else:
raise ValueError("relative argument must be global, view, or view-upright")
self.update()
def pixelSize(self, pos):
"""
Return the approximate size of a screen pixel at the location pos
Pos may be a Vector or an (N,3) array of locations
"""
cam = self.cameraPosition()
if isinstance(pos, np.ndarray):
cam = np.array(cam).reshape((1,)*(pos.ndim-1)+(3,))
dist = ((pos-cam)**2).sum(axis=-1)**0.5
else:
dist = (pos-cam).length()
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.)
return xDist / self.width()
def mousePressEvent(self, ev):
self.mousePos = ev.pos()
def mouseMoveEvent(self, ev):
diff = ev.pos() - self.mousePos
self.mousePos = ev.pos()
if ev.buttons() == QtCore.Qt.LeftButton:
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.pan(diff.x(), diff.y(), 0, relative='view')
else:
self.orbit(-diff.x(), diff.y())
elif ev.buttons() == QtCore.Qt.MidButton:
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.pan(diff.x(), 0, diff.y(), relative='view-upright')
else:
self.pan(diff.x(), diff.y(), 0, relative='view-upright')
def mouseReleaseEvent(self, ev):
pass
# Example item selection code:
#region = (ev.pos().x()-5, ev.pos().y()-5, 10, 10)
#print(self.itemsAt(region))
## debugging code: draw the picking region
#glViewport(*self.getViewport())
#glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
#region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
#self.paintGL(region=region)
#self.swapBuffers()
def wheelEvent(self, ev):
delta = 0
if QT_LIB in ['PyQt4', 'PySide']:
delta = ev.delta()
else:
delta = ev.angleDelta().x()
if delta == 0:
delta = ev.angleDelta().y()
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.opts['fov'] *= 0.999**delta
else:
self.opts['distance'] *= 0.999**delta
self.update()
def keyPressEvent(self, ev):
if ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
self.keysPressed[ev.key()] = 1
self.evalKeyState()
def keyReleaseEvent(self, ev):
if ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
try:
del self.keysPressed[ev.key()]
except:
self.keysPressed = {}
self.evalKeyState()
def evalKeyState(self):
speed = 2.0
if len(self.keysPressed) > 0:
for key in self.keysPressed:
if key == QtCore.Qt.Key_Right:
self.orbit(azim=-speed, elev=0)
elif key == QtCore.Qt.Key_Left:
self.orbit(azim=speed, elev=0)
elif key == QtCore.Qt.Key_Up:
self.orbit(azim=0, elev=-speed)
elif key == QtCore.Qt.Key_Down:
self.orbit(azim=0, elev=speed)
elif key == QtCore.Qt.Key_PageUp:
pass
elif key == QtCore.Qt.Key_PageDown:
pass
self.keyTimer.start(16)
else:
self.keyTimer.stop()
def checkOpenGLVersion(self, msg):
## Only to be called from within exception handler.
ver = glGetString(GL_VERSION).split()[0]
if int(ver.split(b'.')[0]) < 2:
from .. import debug
debug.printExc()
raise Exception(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
else:
raise
def readQImage(self):
"""
Read the current buffer pixels out as a QImage.
"""
w = self.width()
h = self.height()
self.repaint()
pixels = np.empty((h, w, 4), dtype=np.ubyte)
pixels[:] = 128
pixels[...,0] = 50
pixels[...,3] = 255
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels)
# swap B,R channels for Qt
tmp = pixels[...,0].copy()
pixels[...,0] = pixels[...,2]
pixels[...,2] = tmp
pixels = pixels[::-1] # flip vertical
img = fn.makeQImage(pixels, transpose=False)
return img
def renderToArray(self, size, format=GL_BGRA, type=GL_UNSIGNED_BYTE, textureSize=1024, padding=256):
w,h = map(int, size)
self.makeCurrent()
tex = None
fb = None
try:
output = np.empty((w, h, 4), dtype=np.ubyte)
fb = glfbo.glGenFramebuffers(1)
glfbo.glBindFramebuffer(glfbo.GL_FRAMEBUFFER, fb )
glEnable(GL_TEXTURE_2D)
tex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex)
texwidth = textureSize
data = np.zeros((texwidth,texwidth,4), dtype=np.ubyte)
## Test texture dimensions first
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, texwidth, texwidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
if glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH) == 0:
raise Exception("OpenGL failed to create 2D texture (%dx%d); too large for this hardware." % shape[:2])
## create teture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texwidth, texwidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.transpose((1,0,2)))
self.opts['viewport'] = (0, 0, w, h) # viewport is the complete image; this ensures that paintGL(region=...)
# is interpreted correctly.
p2 = 2 * padding
for x in range(-padding, w-padding, texwidth-p2):
for y in range(-padding, h-padding, texwidth-p2):
x2 = min(x+texwidth, w+padding)
y2 = min(y+texwidth, h+padding)
w2 = x2-x
h2 = y2-y
## render to texture
glfbo.glFramebufferTexture2D(glfbo.GL_FRAMEBUFFER, glfbo.GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0)
self.paintGL(region=(x, h-y-h2, w2, h2), viewport=(0, 0, w2, h2)) # only render sub-region
glBindTexture(GL_TEXTURE_2D, tex) # fixes issue #366
## read texture back to array
data = glGetTexImage(GL_TEXTURE_2D, 0, format, type)
data = np.fromstring(data, dtype=np.ubyte).reshape(texwidth,texwidth,4).transpose(1,0,2)[:, ::-1]
output[x+padding:x2-padding, y+padding:y2-padding] = data[padding:w2-padding, -(h2-padding):-padding]
finally:
self.opts['viewport'] = None
glfbo.glBindFramebuffer(glfbo.GL_FRAMEBUFFER, 0)
glBindTexture(GL_TEXTURE_2D, 0)
if tex is not None:
glDeleteTextures([tex])
if fb is not None:
glfbo.glDeleteFramebuffers([fb])
return output
Somewhere in your code you are setting the center to be an int. setCameraPosition, maybe? It needs to be a Vector object, instead.
I'm trying to draw a circle with pyglet. But it is not visible when I use the draw() function instad of the app.run() loop. Any suggestions what I can do? thanks
from math import *
from pyglet.gl import *
window = pyglet.window.Window()
def makeCircle(x_pos, y_pos, radius, numPoints):
verts = []
glClear(pyglet.gl.GL_COLOR_BUFFER_BIT)
glColor3f(1,1,0)
for i in range(numPoints):
angle = radians(float(i)/numPoints * 360.0)
x = radius *cos(angle) + x_pos
y = radius *sin(angle) + y_pos
verts += [x,y]
circle = pyglet.graphics.vertex_list(numPoints, ('v2f', verts))
circle.draw(GL_LINE_LOOP)
input()
makeCircle(5,5, 100, 10)
You've to call window.flip() to update the window.
Since you don't have set a projection matrix, the geometry has to be draw in normalized device coordinates, which are in range [-1, 1] for all 3 components (x, y, z). Note, pyglet set a projection matrix by default when the application is started by pyglet.app.run().
Call window.flip() and change the geometry:
from math import *
from pyglet.gl import *
window = pyglet.window.Window()
def makeCircle(x_pos, y_pos, radius, numPoints):
verts = []
glClear(pyglet.gl.GL_COLOR_BUFFER_BIT)
glColor3f(1,1,0)
for i in range(numPoints):
angle = radians(float(i)/numPoints * 360.0)
x = radius *cos(angle) + x_pos
y = radius *sin(angle) + y_pos
verts += [x,y]
circle = pyglet.graphics.vertex_list(numPoints, ('v2f', verts))
circle.draw(GL_LINE_LOOP)
window.flip() # <--------
input()
makeCircle(0, 0, 0.5, 10) # <--------
Alternatively you can set an orthographic projection on your own, by glOrtho. e.g.:
from math import *
from pyglet.gl import *
window = pyglet.window.Window()
def makeCircle(x_pos, y_pos, radius, numPoints):
verts = []
glMatrixMode(GL_PROJECTION)
glOrtho(0, 640, 0, 480, -1, 1)
glMatrixMode(GL_MODELVIEW)
glClear(pyglet.gl.GL_COLOR_BUFFER_BIT)
glColor3f(1,1,0)
for i in range(numPoints):
angle = radians(float(i)/numPoints * 360.0)
x = radius *cos(angle) + x_pos
y = radius *sin(angle) + y_pos
verts += [x,y]
circle = pyglet.graphics.vertex_list(numPoints, ('v2f', verts))
circle.draw(GL_LINE_LOOP)
text = 'This is a test but it is not visible'
label = pyglet.text.Label(text, font_size=36,
x=10, y=10, anchor_x='left', anchor_y='bottom',
color=(255, 123, 255, 255))
label.draw()
window.flip()
input()
makeCircle(5,5, 100, 10)
I am trying to figure out the coordinates of the vertices of two rectangles in a pygame window that is using OpenGL to create the 3D objects.
import pygame
from pygame.locals import *
import random
from OpenGL.GL import *
from OpenGL.GLU import *
rect1 = [(-5.125,0,-40),(-3.125,0,-40),(-3.125,5,-40),(-5.125,5,-40),]
rect2 = [(3.125,0,-40),(5.125,0,-40),(5.125,5,-40),(3.125,5,-40)]
edges = ((0,1),(1,2),(2,3),(3,0))
#This draws the rectangles edges
def Target():
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(rect1[vertex])
glEnd()
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(rect2[vertex])
glEnd()
def main():
try:
pygame.init()
display = (320,240)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
gluPerspective(45, (display[0]/display[1]), .1, 1000)
while True:
#iterates through events to check for quits
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
Target()
pygame.display.flip()
pygame.time.wait(100)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
except Exception as e:
print (e)
main()
How do I grab the coordinates on the pygame window(320,240) of the object?
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. It transforms from eye space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) by dividing with the w component of the clip coordinates. The NDC are in range (-1,-1,-1) to (1,1,1).
At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport. The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
Perspective Projection Matrix:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
where :
aspect = w / h
tanFov = tan( fov_y / 2 );
2 * n / (r-l) = 1 / (tanFov * aspect)
2 * n / (t-b) = 1 / tanFov
Since the projection matrix is defined by the field of view and the aspect ratio it is possible to recover the viewport position with the field of view and the aspect ratio. Provided that it is a symmetrical perspective projection, where the field of view is not dispalced (as in your case).
First you have to transform the mose position to normalized device coordianates:
w = with of the viewport
h = height of the viewport
x = X position of the mouse
y = Y position ot the mouse
ndc_x = 2.0 * x/w - 1.0;
ndc_y = 1.0 - 2.0 * y/h; // invert Y axis
Then you have to converte the normalized device coordinates to view coordinates:
z = z coodinate of the geometry in view space
viewPos.x = -z * ndc_x * aspect * tanFov;
viewPos.y = -z * ndc_y * tanFov;
If you want to check if the mouse hovers over your rectangles, then the code may look like this:
mpos = pygame.mouse.get_pos()
z = 40
ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
tanFov = math.tan( fov_y * 0.5 * math.pi / 180 )
aspect = width / height
viewPos = [z * ndc[0] * aspect * tanFov, z * ndc[1] * tanFov ]
onRect1 = 1 if (viewPos[0]>=rect1[0][0] and viewPos[0]<=rect1[1][0] and viewPos[1]>=rect1[0][1] and viewPos[1]<=rect1[2][1] ) else 0
onRect2 = 1 if (viewPos[0]>=rect2[0][0] and viewPos[0]<=rect2[1][0] and viewPos[1]>=rect2[0][1] and viewPos[1]<=rect2[2][1] ) else 0
See further:
How to recover view space position given view space depth value and ndc xy
Is it possble get which surface of cube will be click in OpenGL?
OpenGL - Mouse coordinates to Space coordinates
In the following I added the algorithm to your example. If the mouse hovers over an rectangle, then the rectangle is colored in red.
import pygame
from pygame.locals import *
import random
from OpenGL.GL import *
from OpenGL.GLU import *
import math
rect1 = [(-5.125,0,-40),(-3.125,0,-40),(-3.125,5,-40),(-5.125,5,-40),]
rect2 = [(3.125,0,-40),(5.125,0,-40),(5.125,5,-40),(3.125,5,-40)]
edges = ((0,1),(1,2),(2,3),(3,0))
fov_y = 45
width = 320
height = 200
#This draws the rectangles edges
def Target():
mpos = pygame.mouse.get_pos()
z = 40
ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
tanFov = math.tan( fov_y * 0.5 * math.pi / 180 )
aspect = width / height
viewPos = [z * ndc[0] * aspect * tanFov, z * ndc[1] * tanFov ]
onRect1 = 1 if (viewPos[0]>=rect1[0][0] and viewPos[0]<=rect1[1][0] and viewPos[1]>=rect1[0][1] and viewPos[1]<=rect1[2][1] ) else 0
onRect2 = 1 if (viewPos[0]>=rect2[0][0] and viewPos[0]<=rect2[1][0] and viewPos[1]>=rect2[0][1] and viewPos[1]<=rect2[2][1] ) else 0
glColor3f( 1, 1-onRect1, 1-onRect1 )
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(rect1[vertex])
glEnd()
glColor3f( 1, 1-onRect2, 1-onRect2 )
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(rect2[vertex])
glEnd()
def main():
try:
pygame.init()
display = (width,height)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
glMatrixMode(GL_PROJECTION)
gluPerspective(fov_y, (display[0]/display[1]), .1, 1000)
glMatrixMode(GL_MODELVIEW)
while True:
#iterates through events to check for quits
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
Target()
pygame.display.flip()
pygame.time.wait(100)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
except Exception as e:
print (e)
main()
Extension to the answer
Of course you can also do it the other way around. You can transform the corner points of the rectangle to normalized device coordinates and compare them to the mouse position, in normalized device coordinates.
For this you have to read the projection matrix by glGetFloatv(GL_PROJECTION_MATRIX):
prjMat = (GLfloat * 16)()
glGetFloatv(GL_PROJECTION_MATRIX, prjMat)
And you need a function which transform a 3 dimensional cartesian vector by a projection matrix. This is done by multiplying the vector by the projection matrix, which gives homogeneous clip space coordinates. The normalized device coordinates are calculated by dividing the x, y, and z component by the w component.
def TransformVec3(vecA,mat44):
vecB = [0, 0, 0, 0]
for i0 in range(0, 4):
vecB[i0] = vecA[0] * mat44[0*4+i0] + vecA[1] * mat44[1*4+i0] + vecA[2] * mat44[2*4+i0] + mat44[3*4+i0]
return [vecB[0]/vecB[3], vecB[1]/vecB[3], vecB[2]/vecB[3]]
The following function tests if the mouse position is in an rectangle defined by a lower left and a upper right point (the corner points have to be in view space coordinates):
def TestRec(prjMat, mpos, ll, tr):
ll_ndc = TransformVec3(ll, prjMat)
tr_ndc = TransformVec3(tr, prjMat)
ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
inRect = 1 if (ndc[0]>=ll_ndc[0] and ndc[0]<=tr_ndc[0] and ndc[1]>=ll_ndc[1] and ndc[1]<=tr_ndc[1] ) else 0
return inRect
Again I added the algorithm to your example. If the mouse hovers over an rectangle, then the rectangle is colored in red.
import pygame
from pygame.locals import *
import random
from OpenGL.GL import *
from OpenGL.GLU import *
import math
rect1 = [(-5.125,0,-40),(-3.125,0,-40),(-3.125,5,-40),(-5.125,5,-40),]
rect2 = [(3.125,0,-40),(5.125,0,-40),(5.125,5,-40),(3.125,5,-40)]
edges = ((0,1),(1,2),(2,3),(3,0))
fov_y = 45
width = 320
height = 200
def TransformVec3(vecA,mat44):
vecB = [0, 0, 0, 0]
for i0 in range(0, 4):
vecB[i0] = vecA[0] * mat44[0*4+i0] + vecA[1] * mat44[1*4+i0] + vecA[2] * mat44[2*4+i0] + mat44[3*4+i0]
return [vecB[0]/vecB[3], vecB[1]/vecB[3], vecB[2]/vecB[3]]
def TestRec(prjMat, mpos, ll, tr):
ll_ndc = TransformVec3(ll, prjMat)
tr_ndc = TransformVec3(tr, prjMat)
ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
inRect = 1 if (ndc[0]>=ll_ndc[0] and ndc[0]<=tr_ndc[0] and ndc[1]>=ll_ndc[1] and ndc[1]<=tr_ndc[1] ) else 0
return inRect
#This draws the rectangles edges
def Target():
prjMat = (GLfloat * 16)()
glGetFloatv(GL_PROJECTION_MATRIX, prjMat)
mpos = pygame.mouse.get_pos()
onRect1 = TestRec(prjMat, mpos, rect1[0], rect1[2])
onRect2 = TestRec(prjMat, mpos, rect2[0], rect2[2])
glColor3f( 1, 1-onRect1, 1-onRect1 )
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(rect1[vertex])
glEnd()
glColor3f( 1, 1-onRect2, 1-onRect2 )
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(rect2[vertex])
glEnd()
def main():
try:
pygame.init()
display = (width,height)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
glMatrixMode(GL_PROJECTION)
gluPerspective(fov_y, (display[0]/display[1]), .1, 1000)
glMatrixMode(GL_MODELVIEW)
while True:
#iterates through events to check for quits
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
Target()
pygame.display.flip()
pygame.time.wait(100)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
except Exception as e:
print (e)
main()
I am working on creating a flashlight app in Python using Numba for my kernel and OpenGL (code below) . It is very close to finished but when I run it I run into an error with glEnd error 1280. The terminal output for when the code is run is also below. I can't figure out what exactly is causing the issue and helping narrow it down would be very helpful.
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL.ARB.vertex_buffer_object import *
from OpenGL.GL.ARB.pixel_buffer_object import *
import numpy as np
import sys
import pycuda.gl as cuda_gl
import pycuda.driver as cuda_driver
import math
from numba import jit, cuda as nbcuda
W = 600
H = 600
loc = np.array([W/2, H/2], dtype = 'float32')
dragMode = False
TX = 32
TY = 32
pbo, tex, pycuda_pbo, distanceKernel = [None] * 4
class ExternalMemory(object):
"""
Provide an externally managed memory.
Interface requirement: __cuda_memory__, device_ctypes_pointer, _cuda_memize_
"""
__cuda_memory__ = True
def __init__(self, ptr, size):
self.device_ctypes_pointer = ctypes.c_void_p(ptr)
self._cuda_memsize_ = size
def render():
global pycuda_pbo, pbo
assert pbo is not None
pycuda_pbo.unregister()
glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, long(pbo))
pycuda_pbo = cuda_gl.BufferObject(long(pbo))
pbo_mapping = pycuda_pbo.map()
source_ptr = ExternalMemory(pbo_mapping.device_ptr(), W*H * 4)
d_out = nbcuda.devicearray.DeviceNDArray(shape = W*H * 4,
strides = (1,),
dtype = np.dtype('uint8'),
gpu_data = source_ptr)
blockSize = (TX, TY)
gridSize = ((W + TX - 1)/TX, (H + TY - 1)/TY)
distanceKernel[gridSize, blockSize](d_out, W, H, loc)
cuda_driver.Context.synchronize()
pbo_mapping.unmap()
glBindTexture(GL_TEXTURE_2D, tex)
def drawTexture():
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, H, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
glEnable(GL_TEXTURE_2D)
glBegin(GL_TEXTURE_2D)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0); glVertex2f(0,0)
glTexCoord2f(0.0, 1.0); glVertex2f(0,H)
glTexCoord2f(1.0, 1.0); glVertex2f(W,H)
glTexCoord2f(1.0, 0.0); glVertex2f(W,0)
glEnd()
glDisable(GL_TEXTURE_2D)
def display():
render()
drawTexture()
glutSwapBuffers()
def create_PBO():
global pbo, pycuda_pbo
data = np.zeros((W*H,4), dtype = 'uint8')
pbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, pbo)
glBufferData(GL_ARRAY_BUFFER, data, GL_DYNAMIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
pycuda_pbo = cuda_gl.BufferObject(long(pbo))
def create_texture():
global tex
tex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
def exitfunc():
glBindBuffer(GL_ARRAY_BUFFER, long(pbo))
glDeleteBuffers(1, long(pbo));
glBindBuffer(GL_ARRAY_BUFFER, 0)
pbo = None
glDeleteTextures(tex);
tex = None
def keyboard(key, x, y):
if key == '\033': #\033 is escape key
exit()
elif key == 'a':
dragMode = not dragMode
elif key == 27:
exit()
glutPostRedisplay()
def mouseMove(x, y):
if dragMode == True:
loc[0] = x
loc[1] = y
glutPostRedisplay()
def mouseDrag(x, y):
if dragMode == False:
loc[0] = x
loc[1] = y
glutPostRedisplay()
def handleSpecialKeypress(key, x, y):
if key == GLUT_KEY_LEFT:
loc[0] -= DELTA
if key == GLUT_KEY_RIGHT:
loc[0] += DELTA
if key == GLUT_KEY_UP:
loc[1] -= DELTA
if key == GLUT_KEY_DOWN:
loc[1] += DELTA
glutPostRedisplay()
def printInstructions():
print "flashlight instructions"
print "a: toggle mouse tracking mode"
print "arrow keys: move ref location"
print "esc: close graphics window"
def main():
global cuda_gl, cuda_driver, distanceKernel
printInstructions();
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
glutCreateWindow("flashlight: distance image display app")
gluOrtho2D(0, W, H, 0)
glutDisplayFunc(display)
glutKeyboardFunc(keyboard)
glutSpecialFunc(handleSpecialKeypress)
glutPassiveMotionFunc(mouseMove)
glutMotionFunc(mouseDrag)
create_texture()
#Sets up GL interop
import pycuda.gl.autoinit
import pycuda.gl
cuda_gl = pycuda.gl
cuda_driver = pycuda.driver
# force compilation here
#nbcuda.jit(device = True)
def clip(n):
if n > 255:
n = 255
elif n < 0:
n = 0
return n
#nbcuda.jit("(uint8[::1], int32, int32, float32[::1])")
def distanceKernel(d_out, w, h, pos):
c = nbcuda.blockIdx.x*nbcuda.blockDim.x + nbcuda.threadIdx.x
r = nbcuda.blockIdx.y*nbcuda.blockDim.y + nbcuda.threadIdx.y
i = (r*w + c) * 4
if c >= w or r >= h:
return
d = math.sqrt((c - pos[0]) * (c - pos[0]) + (r - pos[1]) * (r - pos[1]))
intensity = clip(255 - d)
d_out[i] = intensity
d_out[i+1] = intensity
d_out[i+2] = 0
d_out[i+3] = 255
create_PBO()
glutMainLoop()
atexit(exitfunc)
main()
This is what the terminal spits out as OpenGL runs
flashlight instructions
a: toggle mouse tracking mode
arrow keys: move ref location
esc: close graphics window
cuInit
cuDeviceGetCount
cuDeviceGetCount
cuDeviceGet
cuGLCtxCreate
cuCtxGetDevice
cuGLRegisterBufferObject
cuGLUnregisterBufferObject
cuGLRegisterBufferObject
cuGLMapBufferObject
cuCtxSynchronize
cuGLUnmapBufferObject
Traceback (most recent call last):
File "_ctypes/callbacks.c", line 314, in 'calling callback function'
File "stackcode.py", line 80, in display
drawTexture()
File "stackcode.py", line 75, in drawTexture
glEnd()
File "latebind.pyx", line 44, in OpenGL_accelerate.latebind.Curry.__call__ (src/latebind.c:1201)
File "/home/uchytilc/anaconda2/lib/python2.7/site-packages/OpenGL/GL/exceptional.py", line 46, in glEnd
return baseFunction( )
File "/home/uchytilc/anaconda2/lib/python2.7/site-packages/OpenGL/platform/baseplatform.py", line 402, in __call__
return self( *args, **named )
File "errorchecker.pyx", line 53, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError (src/errorchecker.c:1218)
OpenGL.error.GLError: GLError(
err = 1280,
description = 'invalid enumerant',
baseOperation = glEnd,
cArguments = ()
)
cuCtxPopCurrent
cuCtxPushCurrent
cuGLUnregisterBufferObject
cuGLUnregisterBufferObject failed with code 1
PyCUDA WARNING: a clean-up operation failed (dead context maybe?)
cuGLUnregisterBufferObject failed: invalid argument
cuCtxPopCurrent
cuCtxPushCurrent
cuCtxDetach
The problem might come from this line:
glBegin(GL_TEXTURE_2D)
glBegin does not allow for this parameter. It only accepts primitive types (which TEXTURE_2D is not). Have a look at the documentation about supported values.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
Is there a library to do pretty on screen display with Python (mainly on Linux but preferably available on other OS too) ? I know there is python-osd but it uses libxosd which looks quite old. I would not call it pretty.
Maybe a Python binding for libaosd. But I did not find any.
Actually, xosd isn't all that old; I went to university with the original author (Andre Renaud, who is a superlative programmer). It is quite low level, but pretty simple - xosd.c is only 1365 lines long. It wouldn't be hard to tweak it to display pretty much anything you want.
Using PyGTK on X it's possible to scrape the screen background and composite the image with a standard Pango layout.
I have some code that does this at http://svn.sacredchao.net/svn/quodlibet/trunk/plugins/events/animosd.py. It's a bit ugly and long, but mostly straightforward.
Building upon the answer from #user79758, the animosd.py link in that post is no longer available, but this one still is: http://hefesto.intra.ial.sp.gov.br/share/pyshared/quodlibet/plugins/events/animosd.py (archive).
Regardless, it was a part of quodlibet music player, which still exists in Ubuntu 18.04; I installed it with
sudo apt install quodlibet
Then, it's not exactly trivial to get a minimal example; mine is included below as animosd_test.py; simply run it with:
python2 animosd_test.py
You'll get this:
animosd_test.py:
#!/usr/bin/env python2
#from quodlibet.ext.events.animosd.osdwindow import OSDWindow
import sys
import os
from collections import namedtuple
from quodlibet.packages.senf import environ, argv as sys_argv
from quodlibet.cli import process_arguments, exit_
import quodlibet
try:
# we want basic commands not to import gtk (doubles process time)
assert "gi.repository.Gtk" not in sys.modules
sys.modules["gi.repository.Gtk"] = None
startup_actions, cmds_todo = process_arguments(sys_argv)
finally:
sys.modules.pop("gi.repository.Gtk", None)
quodlibet.init()
#from quodlibet.ext.events import animosd
from quodlibet.ext.events.animosd import osdwindow
from quodlibet.ext.events.animosd.config import get_config
from quodlibet.ext.events.animosd.osdwindow import OSDWindow
from quodlibet.util import cached_property, print_exc
#~ window = OSDWindow(self.Conf, song)
#~ window.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
#~ window.connect('button-press-event', self.__buttonpress)
#~ window.connect('fade-finished', self.__fade_finished)
#~ self.__current_window = window
#~ window.set_opacity(0.0)
#~ window.show()
#~ window.fade_in()
from gi.repository import Gtk, GObject, GLib, Pango, PangoCairo, Gdk
from quodlibet import qltk
import cairo
from math import pi
class OSDWindowMin(Gtk.Window):
__gsignals__ = {
'fade-finished': (GObject.SignalFlags.RUN_LAST, None, (bool,)),
}
POS_X = 0.5
"""position of window 0--1 horizontal"""
MARGIN = 50
"""never any closer to the screen edge than this"""
BORDER = 20
"""text/cover this far apart, from edge"""
FADETIME = 0.3
"""take this many seconds to fade in or out"""
MS = 40
"""wait this many milliseconds between steps"""
#cached_property
def Conf(self):
return get_config('animosd')
#def __init__(self, conf, song):
def __init__(self):
Gtk.Window.__init__(self, Gtk.WindowType.POPUP)
self.set_type_hint(Gdk.WindowTypeHint.NOTIFICATION)
screen = self.get_screen()
rgba = screen.get_rgba_visual()
if rgba is not None:
self.set_visual(rgba)
#self.conf = conf
self.conf = self.Conf
self.iteration_source = None
self.fading_in = False
self.fade_start_time = 0
mgeo = screen.get_monitor_geometry(self.conf.monitor)
textwidth = mgeo.width - 2 * (self.BORDER + self.MARGIN)
scale_factor = self.get_scale_factor()
#cover_pixbuf = app.cover_manager.get_pixbuf(
# song, conf.coversize * scale_factor, conf.coversize * scale_factor)
coverheight = 0
coverwidth = 0
#~ if cover_pixbuf:
#~ self.cover_surface = get_surface_for_pixbuf(self, cover_pixbuf)
#~ coverwidth = cover_pixbuf.get_width() // scale_factor
#~ coverheight = cover_pixbuf.get_height() // scale_factor
#~ textwidth -= coverwidth + self.BORDER
#~ else:
#~ self.cover_surface = None
self.cover_surface = None
layout = self.create_pango_layout('')
layout.set_alignment((Pango.Alignment.LEFT, Pango.Alignment.CENTER,
Pango.Alignment.RIGHT)[self.conf.align])
layout.set_spacing(Pango.SCALE * 7)
layout.set_font_description(Pango.FontDescription(self.conf.font))
#try:
# layout.set_markup(pattern.XMLFromMarkupPattern(conf.string) % song)
#except pattern.error:
# layout.set_markup("")
layout.set_markup("AAAA")
layout.set_width(Pango.SCALE * textwidth)
layoutsize = layout.get_pixel_size()
if layoutsize[0] < textwidth:
layout.set_width(Pango.SCALE * layoutsize[0])
layoutsize = layout.get_pixel_size()
self.title_layout = layout
winw = layoutsize[0] + 2 * self.BORDER
if coverwidth:
winw += coverwidth + self.BORDER
winh = max(coverheight, layoutsize[1]) + 2 * self.BORDER
self.set_default_size(winw, winh)
rect = namedtuple("Rect", ["x", "y", "width", "height"])
rect.x = self.BORDER
rect.y = (winh - coverheight) // 2
rect.width = coverwidth
rect.height = coverheight
self.cover_rectangle = rect
winx = int((mgeo.width - winw) * self.POS_X)
winx = max(self.MARGIN, min(mgeo.width - self.MARGIN - winw, winx))
winy = int((mgeo.height - winh) * self.conf.pos_y)
winy = max(self.MARGIN, min(mgeo.height - self.MARGIN - winh, winy))
self.move(winx + mgeo.x, winy + mgeo.y)
def do_draw(self, cr):
if self.is_composited():
self.draw_title_info(cr)
else:
# manual transparency rendering follows
walloc = self.get_allocation()
wpos = self.get_position()
if not getattr(self, "_bg_sf", None):
# copy the root surface into a temp image surface
root_win = self.get_root_window()
bg_sf = cairo.ImageSurface(cairo.FORMAT_ARGB32,
walloc.width, walloc.height)
pb = Gdk.pixbuf_get_from_window(
root_win, wpos[0], wpos[1], walloc.width, walloc.height)
bg_cr = cairo.Context(bg_sf)
Gdk.cairo_set_source_pixbuf(bg_cr, pb, 0, 0)
bg_cr.paint()
self._bg_sf = bg_sf
if not getattr(self, "_fg_sf", None):
# draw the window content in another temp surface
fg_sf = cairo.ImageSurface(cairo.FORMAT_ARGB32,
walloc.width, walloc.height)
fg_cr = cairo.Context(fg_sf)
fg_cr.set_source_surface(fg_sf)
self.draw_title_info(fg_cr)
self._fg_sf = fg_sf
# first draw the background so we have 'transparancy'
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.set_source_surface(self._bg_sf)
cr.paint()
# then draw the window content with the right opacity
cr.set_operator(cairo.OPERATOR_OVER)
cr.set_source_surface(self._fg_sf)
cr.paint_with_alpha(self.get_opacity())
#staticmethod
def rounded_rectangle(cr, x, y, radius, width, height):
cr.move_to(x + radius, y)
cr.line_to(x + width - radius, y)
cr.arc(x + width - radius, y + radius, radius,
- 90.0 * pi / 180.0, 0.0 * pi / 180.0)
cr.line_to(x + width, y + height - radius)
cr.arc(x + width - radius, y + height - radius, radius,
0.0 * pi / 180.0, 90.0 * pi / 180.0)
cr.line_to(x + radius, y + height)
cr.arc(x + radius, y + height - radius, radius,
90.0 * pi / 180.0, 180.0 * pi / 180.0)
cr.line_to(x, y + radius)
cr.arc(x + radius, y + radius, radius,
180.0 * pi / 180.0, 270.0 * pi / 180.0)
cr.close_path()
#property
def corners_factor(self):
if self.conf.corners != 0:
return 0.14
return 0.0
def draw_conf_rect(self, cr, x, y, width, height, radius):
if self.conf.corners != 0:
self.rounded_rectangle(cr, x, y, radius, width, height)
else:
cr.rectangle(x, y, width, height)
def draw_title_info(self, cr):
cr.save()
do_shadow = (self.conf.shadow[0] != -1.0)
do_outline = (self.conf.outline[0] != -1.0)
self.set_name("osd_bubble")
qltk.add_css(self, """
#osd_bubble {
background-color:rgba(0,0,0,0);
}
""")
cr.set_operator(cairo.OPERATOR_OVER)
cr.set_source_rgba(*self.conf.fill)
radius = min(25, self.corners_factor * min(*self.get_size()))
self.draw_conf_rect(cr, 0, 0, self.get_size()[0],
self.get_size()[1], radius)
cr.fill()
# draw border
if do_outline:
# Make border darker and more translucent than the fill
f = self.conf.fill
rgba = (f[0] / 1.25, f[1] / 1.25, f[2] / 1.25, f[3] / 2.0)
cr.set_source_rgba(*rgba)
self.draw_conf_rect(cr,
1, 1,
self.get_size()[0] - 2, self.get_size()[1] - 2,
radius)
cr.set_line_width(2.0)
cr.stroke()
textx = self.BORDER
if self.cover_surface is not None:
rect = self.cover_rectangle
textx += rect.width + self.BORDER
surface = self.cover_surface
transmat = cairo.Matrix()
if do_shadow:
cr.set_source_rgba(*self.conf.shadow)
self.draw_conf_rect(cr,
rect.x + 2, rect.y + 2,
rect.width, rect.height,
0.6 * self.corners_factor * rect.width)
cr.fill()
if do_outline:
cr.set_source_rgba(*self.conf.outline)
self.draw_conf_rect(cr,
rect.x, rect.y,
rect.width, rect.height,
0.6 * self.corners_factor * rect.width)
cr.stroke()
cr.set_source_surface(surface, 0, 0)
width, height = get_surface_extents(surface)[2:]
transmat.scale(width / float(rect.width),
height / float(rect.height))
transmat.translate(-rect.x, -rect.y)
cr.get_source().set_matrix(transmat)
self.draw_conf_rect(cr,
rect.x, rect.y,
rect.width, rect.height,
0.6 * self.corners_factor * rect.width)
cr.fill()
PangoCairo.update_layout(cr, self.title_layout)
height = self.title_layout.get_pixel_size()[1]
texty = (self.get_size()[1] - height) // 2
if do_shadow:
cr.set_source_rgba(*self.conf.shadow)
cr.move_to(textx + 2, texty + 2)
PangoCairo.show_layout(cr, self.title_layout)
if do_outline:
cr.set_source_rgba(*self.conf.outline)
cr.move_to(textx, texty)
PangoCairo.layout_path(cr, self.title_layout)
cr.stroke()
cr.set_source_rgb(*self.conf.text[:3])
cr.move_to(textx, texty)
PangoCairo.show_layout(cr, self.title_layout)
cr.restore()
def fade_in(self):
self.do_fade_inout(True)
def fade_out(self):
self.do_fade_inout(False)
def do_fade_inout(self, fadein):
fadein = bool(fadein)
self.fading_in = fadein
now = GObject.get_current_time()
fraction = self.get_opacity()
if not fadein:
fraction = 1.0 - fraction
self.fade_start_time = now - fraction * self.FADETIME
if self.iteration_source is None:
self.iteration_source = GLib.timeout_add(self.MS,
self.fade_iteration_callback)
def fade_iteration_callback(self):
delta = GObject.get_current_time() - self.fade_start_time
fraction = delta / self.FADETIME
if self.fading_in:
self.set_opacity(fraction)
else:
self.set_opacity(1.0 - fraction)
if not self.is_composited():
self.queue_draw()
if fraction >= 1.0:
self.iteration_source = None
self.emit('fade-finished', self.fading_in)
return False
return True
def __buttonpress(window, event):
window.hide()
#if self.__current_window is window:
# self.__current_window = None
window.destroy()
#def __fade_finished(window, fade_in):
# if fade_in:
# GLib.timeout_add(self.Conf.delay, self.start_fade_out, window)
# else:
# window.hide()
# if self.__current_window is window:
# self.__current_window = None
# # Delay destroy - apparently the hide does not quite register if
# # the destroy is done immediately. The compiz animation plugin
# # then sometimes triggers and causes undesirable effects while the
# # popup should already be invisible.
# GLib.timeout_add(1000, window.destroy)
window = OSDWindowMin()
window.connect("destroy", Gtk.main_quit) # must be present, else program does not exit when on-screen display window is clicked!
window.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
window.connect('button-press-event', __buttonpress)
#window.connect('fade-finished', self.__fade_finished)
#window.show_all#()
window.set_opacity(0.0)
window.show()
window.fade_in()
Gtk.main()