2017-01-17 21 views
1

Я встречаю странное поведение с python3/qt5/openGL при использовании текстур openGL.Тексты Qt/openGL не работают правильно

Открытие двух QOpenGLWidgets в двух отдельных окнах приводит к смешению текстуры openGL между двумя виджетами - изменение текстуры в первом виджетах приводит к изменению текстуры во втором виджетах. Хотя они должны иметь разные контексты openGL ..!

Предоставлен код, демонстрирующий эту проблему (см. Ниже) - попробуйте сначала, прежде чем предлагать решение. Код демонстрирует, компактно два случая:

1) Создание двух QOpenGLWidgets, которые разделяют один и тот же родительский виджет

  • Работы OK - нет "текстуры смешивания"

2) Создание двух QOpenGLWidgets с не родители

  • Widgets открыт как отдельные окна
  • Параметр/система OpenGL Qt запутывает текстуры OpenGL: изменение текстуры в первом окне неожиданно изменяет текстуру второго окна! (Не забудьте изменить размер/щелкните второе окно, чтобы увидеть это)

Для того чтобы зафиксировать случай (2), я также пытался заставить два виджета, чтобы одни и те же OpenGLContext .. но без успеха.

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

говорит следующее:

Создание дополнительных экземпляров QOpenGLContext, которые совместно используют ресурсы, такие как текстуры с контекстом QOpenGLWidget является также возможным. «Просто» передать указатель, возвращенный из контекста() в QOpenGLContext :: setShareContext() перед вызовом QOpenGLContext :: создать()

Да, я могу создать экземпляр пользовательского 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 больше нет.

Это беспорядок! Помогите оценить!

Похожие:

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

Спасибо за «выборки»! Это бог **** работает ..! –

 Смежные вопросы

  • Нет связанных вопросов^_^