2017-01-17 234 views
1

在使用openGL紋理時,我遇到了python3/qt5/openGL的奇怪行爲。Qt/openGL紋理無法正常工作

在兩個單獨的窗口中打開兩個QOpenGLWidgets,導致兩個Widget之間的OpenGL紋理混合 - 更改第一個Widget中的紋理,導致第二個Widget中的紋理更改。雖然他們應該有不同的openGL上下文。

提供演示此問題的代碼(請參閱下文) - 請在提出解決方案之前先嚐試一下。代碼緊湊演示了兩種情況:

1)創建共享相同的父窗口部件

  • 廠確定兩個的QOpenGLWidgets - 沒有「紋理混合」

2)創建兩個QOpenGLWidgets沒有父母

  • 小工具作爲開放各個窗口
  • 的QT/OpenGL的系統弄亂OpenGL紋理:在第一個窗口中改變紋理,意外改變第二個窗口的紋理! (記住要調整/點擊第二個窗口中看到這一點)

爲了修復情況(2),我還試圖迫使這兩個部件共享相同的OpenGLContext ..但沒有成功。

http://doc.qt.io/qt-5/qopenglwidget.html

說以下內容:

創建額外QOpenGLContext情況下想與QOpenGLWidget的背景紋理共享資源也是可能的。調用QOpenGLContext ::創建()

是前「簡單的」通)從上下文()以QOpenGLContext :: setShareContext(返回的指針,我可以實例化一個自定義QOpenGLContext共享,但沒有辦法強迫使用該自定義上下文的QOpenGLWidget:QOpenGLWidget自動實例化QOpenGLContext某處(其中?)..我只能在initializeGL(使用「self.context()」)訪問它,但在此時上下文的「.create()」方法已經被稱爲..! ..因此沒有辦法填充我的自定義QOpenGLContext任何地方。

我也試過如下:我們創建一個類變量

glcontext=None 

在widget的(請參閱第一下面的完整的示例)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了。

這是一團糟!幫助高度讚賞!

相關:

Is it possible to use the same OpenGL context between top-level windows in Qt?

How to share OpenGL context or data?

演示程序:

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

回答

2

我發現固定示例的方式,但我不知道它是否是「正確的「解決方案,因爲我幾乎不知道OpenGL(也許有幫助)。

反正,我所做的就是這樣的:

@QtCore.pyqtSlot(object) 
def frameReceived(self, frame): 
    self.makeCurrent() 
    ... 

根據該文檔爲makeCurrent

這是沒有必要調用在大多數情況下此功能,因爲它是 自動調用在調用paintGL()之前。

所以,問題似乎是:什麼是「其他情況」提到這裏,是什麼讓他們不同...

+0

感謝「採樣」!它上帝****工程..! –