Python 3.x Qt/openGL纹理无法正常工作

Python 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

在使用openGL纹理时,我在python3/qt5/openGL中遇到了奇怪的行为

在两个单独的窗口中打开两个QopenglWidget,会导致两个小部件之间的openGL纹理混合-在第一个小部件中更改纹理,在第二个小部件中更改纹理。虽然它们应该有不同的openGL上下文

提供了演示此问题的代码(请参见下文)-请在建议解决方案之前先试用。代码简要地演示了两种情况:

1) 创建共享同一父窗口小部件的两个QopenglWidget

  • 工作正常-无“纹理混合”
2) 创建两个没有父对象的QopenglWidget

  • 窗口小部件作为单个窗口打开
  • Qt/OpenGL系统弄乱了OpenGL纹理:在第一个窗口中更改纹理,会意外更改第二个窗口的纹理! (请记住调整/单击第二个窗口以查看此信息)
为了修复案例(2),我还尝试强制这两个小部件共享相同的OpenGLContext。。但是没有成功

他说:

也可以创建额外的QOpenGLContext实例,这些实例与QOpenGLWidget的上下文共享纹理等资源只需“在调用QOpenGLContext::create()之前,将从context()返回的指针传递给QOpenGLContext::setShareContext()”

是的,我可以实例化一个自定义QOpenGLContext进行共享,但是没有办法强制QOpenGLWidget使用该自定义上下文:QOpenGLWidget会在某个地方(哪里?)自动实例化QOpenGLContext。。我只能在initializeGL(使用“self.context()”)中访问它,但此时上下文的“.create()”方法已被调用。。因此,没有办法在任何地方填充我的自定义QOpenGLContext

我还尝试了以下方法:创建一个类变量

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()
    ...