Python 3.x Qt/openGL纹理无法正常工作
在使用openGL纹理时,我在python3/qt5/openGL中遇到了奇怪的行为 在两个单独的窗口中打开两个QopenglWidget,会导致两个小部件之间的openGL纹理混合-在第一个小部件中更改纹理,在第二个小部件中更改纹理。虽然它们应该有不同的openGL上下文 提供了演示此问题的代码(请参见下文)-请在建议解决方案之前先试用。代码简要地演示了两种情况: 1) 创建共享同一父窗口小部件的两个QopenglWidgetPython 3.x Qt/openGL纹理无法正常工作,python-3.x,opengl,qt5,pyqt5,pyopengl,Python 3.x,Opengl,Qt5,Pyqt5,Pyopengl,在使用openGL纹理时,我在python3/qt5/openGL中遇到了奇怪的行为 在两个单独的窗口中打开两个QopenglWidget,会导致两个小部件之间的openGL纹理混合-在第一个小部件中更改纹理,在第二个小部件中更改纹理。虽然它们应该有不同的openGL上下文 提供了演示此问题的代码(请参见下文)-请在建议解决方案之前先试用。代码简要地演示了两种情况: 1) 创建共享同一父窗口小部件的两个QopenglWidget 工作正常-无“纹理混合” 2) 创建两个没有父对象的Qopen
- 工作正常-无“纹理混合”
- 窗口小部件作为单个窗口打开
- Qt/OpenGL系统弄乱了OpenGL纹理:在第一个窗口中更改纹理,会意外更改第二个窗口的纹理! (请记住调整/单击第二个窗口以查看此信息)
glcontext=None
在小部件(请首先查看下面的完整示例)的initializeGL方法中,我正在尝试这样做:
if (VideoImageWidget.glcontext==None):
print("VideoImageWidget: initializeGL: creating context for sharing")
VideoImageWidget.glcontext=QtGui.QOpenGLContext()
ok=VideoImageWidget.glcontext.create()
print("VideoImageWidget: initializeGL: created context for sharing",VideoImageWidget.glcontext,ok)
context=self.context()
print("VideoImageWidget: initializeGL: automatically created context:",context)
context.setShareContext(VideoImageWidget.glcontext)
ok=context.create() # must call this ..
print("VideoImageWidget: initializeGL: recreated my context:",ok)
但这是行不通的。。VideoImageWidget不再显示图像
真是一团糟!非常感谢您的帮助
相关的:
演示程序:
import sys
import time
from PyQt5 import QtWidgets, QtCore, QtGui # Qt5
from OpenGL.GL import *
from PIL import Image
"""
Demonstrating a bug (?) in QOpenGLWidget / Qt OpenGL insfrastructure :
You need:
* to have two tiff images ("base.tif" and "2.tif") in the same directory
* to remove/install some libraries:
sudo apt-get install python3-pyqt5 pip3
sudo apt-get remove python3-opengl # we want the most recent version of the opengl bindings
sudo pip3 install PyOpenGL PyOpenGL_accelerate
sudo pip3 install imutils
Usage:
* look for the tag "TOGGLE HERE" below to switch between nested QWidgets / individual windows
* run program with
python3 context_test.py
What's going on here?
* Press the button a few times : a new image appears to the first widget
* The image should appear only to the first widget
* .. but it appears in both widgets if we set "nested=False", i.e. when the widgets constitute individual windows
* .. confirm this by clicking / resizing the second window after clicking the button a few times
Why this happens?
* Qt creates some sort of "top-level" (?) opengl context that is referring to the same texture ids = bug ?
This code is licensed under the do-with-it-whatever-you-want license, written by Sampsa Riikonen, 2017
"""
def getImg(fname):
im =QtGui.QImage(fname)
im =im.convertToFormat(QtGui.QImage.Format_RGB888)
ix =im.width()
iy =im.height()
ptr=im.bits()
ptr.setsize(im.byteCount())
return ptr.asstring(), ix, iy
class VideoImageWidget(QtWidgets.QOpenGLWidget): # http://doc.qt.io/qt-5/qopenglwidget.html # Qt5
def __init__(self,parent=None):
super().__init__(parent=parent)
self.parent=parent
self.baseimage, self.ix, self.iy =getImg("base.tif")
self.gl_format=GL_RGB
self.ratio =1
self.picratio =1
def changeTexture(self,image,ix,iy):
glBindTexture(GL_TEXTURE_2D, self.tex) # this is the texture we will manipulate
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, self.gl_format, ix, iy, 0, self.gl_format, GL_UNSIGNED_BYTE, image) # load bitmap to texture
self.picratio=self.iy/self.ix
def resetTexture(self):
self.changeTexture(self.baseimage,self.ix,self.iy)
def initializeGL(self):
# "This function should set up any required OpenGL resources and state"
glEnable(GL_TEXTURE_2D)
self.tex = glGenTextures(1) # create a new texture
# https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenTextures.xml
# "it is guaranteed that none of the returned names was in use immediately before the call"
print("VideoImageWidget: glGenTextures returned:",self.tex)
self.resetTexture()
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Clear The Screen And The Depth Buffer
glLoadIdentity() # Reset The View
glBindTexture(GL_TEXTURE_2D, self.tex) # this is the texture we will manipulate
glBegin(GL_QUADS)
dz=0
r=self.ratio/self.picratio # screen h/w // picture h/w
if (r<1): # screen wider than image
dy=1
dx=r
elif (r>1): # screen taller than image
dx=1
dy=1/r
else:
dx=1
dy=1
glTexCoord2f(0.0, 0.0); glVertex3f(-dx, dy, dz)
glTexCoord2f(1.0, 0.0); glVertex3f( dx, dy, dz)
glTexCoord2f(1.0, 1.0); glVertex3f( dx,-dy, dz)
glTexCoord2f(0.0, 1.0); glVertex3f(-dx,-dy, dz)
glEnd()
def resizeGL(self, width, height):
"""Called upon window resizing: reinitialize the viewport.
"""
glViewport(0, 0, width, height)
glLoadIdentity()
glOrtho(-1, 1, 1, -1, -1, 1)
self.ratio=height/width
@QtCore.pyqtSlot(object)
def frameReceived(self,frame):
buf =frame[0]
width =frame[1]
height=frame[2]
print("VideoImageWidget updating with frame",width,height)
self.changeTexture(buf,width,height)
self.update()
class MyGui(QtWidgets.QMainWindow):
f1 = QtCore.pyqtSignal(object)
f2 = QtCore.pyqtSignal(object)
def __init__(self,parent=None):
super().__init__(parent)
self.cw=QtWidgets.QWidget(self)
self.setCentralWidget(self.cw)
self.lay = QtWidgets.QVBoxLayout(self.cw)
self.b = QtWidgets.QPushButton("Send frame",self.cw)
# *** TOGGLE HERE ***
# nested=True # *** widgets sitting in the QMainWindow
nested=False # *** individual windows
self.lay.addWidget(self.b)
if (nested):
self.v1 = VideoImageWidget(parent=self.cw)
self.v2 = VideoImageWidget(parent=self.cw)
self.lay.addWidget(self.v1)
self.lay.addWidget(self.v2)
else:
self.v1 = VideoImageWidget(parent=None)
self.v2 = VideoImageWidget(parent=None)
self.v1.show()
self.v2.show()
self.b.clicked. connect(self.clicked)
self.f1. connect(self.v1.frameReceived)
self.newimage, self.ix, self.iy =getImg("2.tif")
@QtCore.pyqtSlot()
def clicked(self):
print("emitting frame")
self.f1.emit([self.newimage, self.ix, self.iy]) # update _only_ the first VideoImageWidget
if (__name__=="__main__"):
app=QtWidgets.QApplication([])
# *** Set this to apply context sharing ***
# app.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
"""
.. but that does not work for textures
See the last comment on this post:
https://stackoverflow.com/questions/29838356/is-it-possible-to-use-the-same-opengl-context-between-top-level-windows-in-qt
"I've realised that this does not solve the problem in the question, as it does not actually share the context, but enables sharing of a subset of resources .."
That is said also in the qt docs, but in an extremely implicit way..
http://doc.qt.io/qt-5/qopenglwidget.html
"Creating extra QOpenGLContext instances that share resources like textures .. "
"""
print("OpenGL context sharing status:",app.testAttribute(QtCore.Qt.AA_ShareOpenGLContexts))
mg=MyGui()
mg.show()
app.exec_()
导入系统
导入时间
从PyQt5导入QtWidgets、QtCore、QtGui#Qt5
从OpenGL.GL导入*
从PIL导入图像
"""
演示QOpenGLWidget/Qt OpenGL insfrastructure中的错误(?):
你需要:
*在同一目录中有两个tiff图像(“base.tif”和“2.tif”)
*要删除/安装某些库,请执行以下操作:
sudo-apt-get安装python3-pyqt5-pip3
sudo apt get remove python3 opengl#我们需要最新版本的opengl绑定
sudopip3安装PyOpenGL PyOpenGL\u加速
sudopip3安装imutils
用法:
*寻找下面的标签“TOGGLE HERE”,在嵌套的QWidgets/单个窗口之间切换
*使用运行程序
python3 context_test.py
这是怎么回事?
*按下按钮几次:第一个小部件将显示一个新图像
*图像应该只出现在第一个小部件上
*…但如果我们设置“nested=False”,即当小部件构成单个窗口时,它会出现在两个小部件中
*..点击按钮几次后,通过点击/调整第二个窗口的大小来确认这一点
为什么会发生这种情况?
*Qt创建某种“顶级”(?)opengl上下文,它引用相同的纹理ID=bug?
此代码根据Sampsa Riikonen于2017年编写的“随你所愿随你所愿”许可证进行许可
"""
def getImg(fname):
im=QtGui.QImage(fname)
im=im.convertToFormat(QtGui.QImage.Format_RGB888)
ix=im.宽度()
iy=im.高度()
ptr=im.bits()
ptr.setsize(im.byteCount())
返回ptr.asstring(),ix,iy
类VideoImageWidget(QtWidgets.QOpenGLWidget):#http://doc.qt.io/qt-5/qopenglwidget.html #Qt5
def uuu init uuu(self,parent=None):
super()。\uuuuu init\uuuuuu(父=父)
self.parent=parent
self.baseimage、self.ix、self.iy=getImg(“base.tif”)
self.gl\u format=gl\u RGB
自身比率=1
自拍照比率=1
def changeTexture(自我、图像、ix、iy):
glBindTexture(GL_TEXTURE_2D,self.tex)#这是我们将要处理的纹理
glTexParameteri(GL_纹理_2D、GL_纹理_贴图过滤器、GL_线性)
glTexParameteri(GL\u纹理\u 2D、GL\u纹理\u最小\u过滤器、GL\u线性)
glTexImage2D(GL_纹理_2D,0,self.GL_格式,ix,iy,0,self.GL_格式,GL_无符号字节,图像)#将位图加载到纹理
self.picratio=self.iy/self.ix
def重置纹理(自):
self.changeTexture(self.baseimage、self.ix、self.iy)
def初始化EGL(自身):
#“此函数应设置任何必需的OpenGL资源和状态”
glEnable(GL_纹理_2D)
self.tex=glGenTextures(1)#创建新纹理
# https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenTextures.xml
#“保证在调用之前没有使用任何返回的名称”
打印(“VideoImageWidget:glGenTextures返回:”,self.tex)
self.resetTexture()
def paintGL(自我):
glClear(GL_颜色_缓冲区_位| GL_深度_缓冲区_位)#清除屏幕和深度缓冲区
glLoadIdentity()#重置视图
glBindTexture(GL_TEXTURE_2D,self.tex)#这是我们将要处理的纹理
glBegin(GLU四边形)
dz=0
r=self.ratio/self.picratio#屏幕高宽//图片高宽
如果(r1):#屏幕比图像高
dx=1
dy=1/r
其他:
dx=1
dy=1
glTexCoord2f(0.0,0.0);glVertex3f(-dx,dy,dz)
glTexCoord2f(1.0,0.0);glVertex3f(dx、dy、dz)
glTexCoord2f(1.0,1.0);glVertex3f(dx,-dy,dz)
glTexCoord2f(0.0,1.0);glVertex3f(-dx,-dy,dz)
格伦德()
def resizeGL(自身、宽度、高度):
“”“调整窗口大小时调用:重新初始化视口。”。
"""
glViewport(0,0,宽度,高度)
@QtCore.pyqtSlot(object)
def frameReceived(self, frame):
self.makeCurrent()
...