2017-10-19 47 views
2

我在Mac上使用PyOpenGL。默认情况下,使用OpenGL 2.1。但是,根据我的研究和OpenGL Extension Viewer,我应该可以使用OpenGL 4.1。 我试图将一个QGLFormat传递给我的QGLWidget,正如我在这里看到的许多线程所做的那样,但上下文版本不会改变。如果我试图强制它,它会告诉我上下文无效。 我一直在尝试了很多 - 这里是一个非常简单的例子,说明我的问题:如何在MacOS上使用QGLFormat更改OpenGL版本?

from PyQt4 import QtGui, QtCore, QtOpenGL 
import sys 

if __name__ == '__main__': 

    app = QtGui.QApplication(sys.argv) 

    glFormat = QtOpenGL.QGLFormat() 
    glFormat.setVersion(4,1) 
    glFormat.setProfile(QtOpenGL.QGLFormat.CoreProfile) 
    glFormat.setSampleBuffers(True) 
    glFormat.setDefaultFormat(glFormat) 
    print("Format version: " + str(glFormat.majorVersion())) 
    myW = QtOpenGL.QGLWidget(glFormat) 
    print ("Widget context valid: " + str(myW.context().isValid())) 
    print ("Widget format version: " + str(myW.format().majorVersion())) 
    print ("Widget context format version: " + str(myW.context().format().majorVersion())) 
    myW.context().setFormat(glFormat) 
    print ("Forced format valid: " + str(myW.context().isValid())) 
    print ("Forced format version: " + str(myW.format().majorVersion())) 
    myW.show() 
    sys.exit(app.exec_()) 

这里是输出:

Format version: 4 
Widget context valid: True 
Widget format version: 1 
Widget context format version: 1 
Forced format valid: False 
Forced format version: 4 

任何想法,我的问题所在?难道我做错了什么? 我正在使用macOS Sierra版本10.12.6。 我使用Qt版本4.8.7,sip版本4.19.3和pyqt版本4.12,以防相关。 这是what OpenGL Extension Viewer tells me

任何帮助将不胜感激!

编辑

我发现什么看起来像一个类似的问题与C++在这里。我不知道如何翻译解决方案到PyQt,但我会研究这个: Changing the OpenGL Context Version for QGLWidgets in Qt 4.8.6 on OS X

编辑3

尝试(第二版)由@ekhumoro提供的补丁,当我得到以下输出:

patching file qpy/QtOpenGL/core_profile_attributes.mm 
patching file qpy/QtOpenGL/qpyopengl.pro 
patching file sip/QtOpenGL/qgl.sip 
patch unexpectedly ends in middle of line 
Hunk #3 FAILED at 493. 
1 out of 3 hunks FAILED -- saving rejects to file sip/QtOpenGL/qgl.sip.rej 

qpyopengl.pro现在可以正确地打上补丁,但它qgl.sip仍然失败。

更新

这里是拒绝文件为qgl.sip内容:

*************** 
*** 478,481 **** 

    %ModuleHeaderCode 
    #include <qpyopengl_api.h> 

--- 493,498 ---- 

    %ModuleHeaderCode 
    #include <qpyopengl_api.h> 
+ typedef struct GDevice** GDHandle; 
+ void* select_3_2_mac_visual(); 

出于好奇,我跑configure-ng.py并试图编译。我收到以下错误:

[...]/PyQt4_gpl_mac-4.12.1/sip/QtOpenGL/qgl.sip:269:5: error: 'virtual' can only appear on non-static member functions 
    virtual void* chooseMacVisual(GDHandle handle) 
    ^
[...]/PyQt4_gpl_mac-4.12.1/sip/QtOpenGL/qgl.sip:271:16: error: use of undeclared identifier 'select_3_2_mac_visual' 
     return select_3_2_mac_visual(); 
      ^
[...]/PyQt4_gpl_mac-4.12.1/sip/QtOpenGL/qgl.sip:269:44: warning: unused parameter 'handle' [-Wunused-parameter] 
    virtual void* chooseMacVisual(GDHandle handle) 
+0

你需要创建在OSX上_forward-compatible_上下文。 – derhass

+0

@derhass:你能更具体吗? – Elensil

回答

1

对于什么是值得的,在我的Windows 10笔记本上运行你的代码提供了以下的输出:

Format version: 4 
Widget context valid: True 
Widget format version: 4 
Widget context format version: 4 
Forced format valid: False 
Forced format version: 4 

因此,我怀疑你的问题可能不会实际上是一个特定于平台的平台,而是与您的myW.context().setFormat(glFormat)通话。有趣的是,我看了一下PyQt documentation for QGLWidget,它似乎缺少了setFormat(),这非常无益!

展望而不是在相应的C++文档,它看起来像setFormat()实际上可以从要么QGLWidget itself叫,否则孩子QGLContext对象。由于您从QGLContext调用它,事实证明上下文是重置,使其无效。解决方案是在设置格式后调用myW.context().create(),从而使用新参数创建一个有效的格式对象!这一修改是这样的:

from PyQt4 import QtGui, QtCore, QtOpenGL 
import sys 

if __name__ == '__main__': 

    app = QtGui.QApplication(sys.argv) 

    glFormat = QtOpenGL.QGLFormat() 
    glFormat.setVersion(4,1) 
    glFormat.setProfile(QtOpenGL.QGLFormat.CoreProfile) 
    glFormat.setSampleBuffers(True) 
    glFormat.setDefaultFormat(glFormat) 
    print("Format version: " + str(glFormat.majorVersion())) 
    myW = QtOpenGL.QGLWidget(glFormat) 
    print ("Widget context valid: " + str(myW.context().isValid())) 
    print ("Widget format version: " + str(myW.format().majorVersion())) 
    print ("Widget context format version: " + str(myW.context().format().majorVersion())) 
    myW.context().setFormat(glFormat) 
    myW.context().create() 
    print ("Forced format valid: " + str(myW.context().isValid())) 
    print ("Forced format version: " + str(myW.format().majorVersion())) 
    myW.show() 
    sys.exit(app.exec_()) 

另一种方法是只是调用setFormat()QGLWidget水平,这会自动创建一个新的上下文,造成这样的:

from PyQt4 import QtGui, QtCore, QtOpenGL 
import sys 

if __name__ == '__main__': 

    app = QtGui.QApplication(sys.argv) 

    glFormat = QtOpenGL.QGLFormat() 
    glFormat.setVersion(4,1) 
    glFormat.setProfile(QtOpenGL.QGLFormat.CoreProfile) 
    glFormat.setSampleBuffers(True) 
    glFormat.setDefaultFormat(glFormat) 
    print("Format version: " + str(glFormat.majorVersion())) 
    myW = QtOpenGL.QGLWidget(glFormat) 
    print ("Widget context valid: " + str(myW.context().isValid())) 
    print ("Widget format version: " + str(myW.format().majorVersion())) 
    print ("Widget context format version: " + str(myW.context().format().majorVersion())) 
    myW.setFormat(glFormat) 
    print ("Forced format valid: " + str(myW.context().isValid())) 
    print ("Forced format version: " + str(myW.format().majorVersion())) 
    myW.show() 
    sys.exit(app.exec_()) 

话虽这么说,所有这些都是猜测,因为我没有可以测试此代码的Mac。让我知道这是否解决了你的问题,并希望它有帮助!

+1

谢谢!至少,我知道为什么上下文变得无效,多亏了你。实际上,我已经尝试在窗口小部件级别调用setFormat(),但它不会改变任何内容:也就是说,上下文是有效的,但版本保持为1.这就是为什么我直接尝试上下文。 :( 我也尝试了你的第一个建议,但我得到了同样的结果:我最终得到了有效的上下文,但仍然是错误的OpenGL版本。 – Elensil

+0

@Elensil看看你在问题编辑中添加的链接。如果升级到PyQt5不是一个选项(这可能会解决您的问题),它看起来像你需要以某种方式翻译该C++选项。有很多选项(ctypes,cython或C扩展),但最简单的可能是首先使用ctypes。希望增加一个赏金将带来一些关注这个问题,因为我不能真正测试任何东西,没有一个Mac哈哈...... – CodeSurgeon

+0

我试着用PyQt5,它修复了这个问题。这是一个备份解决方案,但将我的应用程序升级到PyQt5将需要大量工作。 主要是因为混合中引入了一些遗留的OpenGL,当我切换到PyQt5时不再有效。 我不是最初的开发者,而且我对OpenGL及其不同版本的知识有限。另一方面,我想为我的用户安装一些易于安装的东西。下载和运行补丁程序可能会阻碍不止一些补丁程序。 :( – Elensil

1

要回答关于翻译的Mac专用的解决方案成Python的具体问题:这是不可能做到直接,因为PyQt的绑定不裹虚方法chooseMacVisual。没有这个,就没有办法重新实现它,因为Qt无法看到PyQt没有预先定义为虚拟的任何Python方法。

因此,唯一可行的方法是修补PyQt4并重新编译它。幸运的是,有人已经做到了这一点,并在这里提供的代码:

然而,构建指令不是完全清楚,以及旨在PyQt的-4.9.4。所以我准备了一个比较容易处理的差异。我没有试图编译它,因为我无法访问Mac系统。

要应用修补程序,请将latest PyQt4 OSX sources和cd解压到目录中。然后复制DIFF下方并保存为macgl.diff,并运行此命令:

patch -Np1 -i macgl.diff 

然后,您可以编译使用说明您的修补PyQt4的如下所示:

更新

我以前认为差异对PyQt-4.9.4将适用于所有新版PyQt4版本,但我错了。我试图用下面修改过的差异来修正这个问题,它已被修改为PyQt-4.12.1 mac source code

这里是DIFF:

diff -Naur c/qpy/QtOpenGL/core_profile_attributes.mm d/qpy/QtOpenGL/core_profile_attributes.mm 
--- c/qpy/QtOpenGL/core_profile_attributes.mm 1970-01-01 01:00:00.000000000 +0100 
+++ d/qpy/QtOpenGL/core_profile_attributes.mm 2017-10-22 20:11:53.000000000 +0100 
@@ -0,0 +1,22 @@ 
+#include <QGLContext> 
+ 
+ 
+void* select_3_2_mac_visual() 
+{ 
+ static const int Max = 40; 
+ NSOpenGLPixelFormatAttribute attribs[Max]; 
+ int cnt = 0; 
+ 
+ attribs[cnt++] = NSOpenGLPFAOpenGLProfile; 
+ attribs[cnt++] = NSOpenGLProfileVersion3_2Core; 
+ 
+ attribs[cnt++] = NSOpenGLPFADoubleBuffer; 
+ 
+ attribs[cnt++] = NSOpenGLPFADepthSize; 
+ attribs[cnt++] = (NSOpenGLPixelFormatAttribute)16; 
+ 
+ attribs[cnt] = 0; 
+ Q_ASSERT(cnt < Max); 
+ 
+ return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; 
+} 
diff -Naur c/qpy/QtOpenGL/qpyopengl.pro d/qpy/QtOpenGL/qpyopengl.pro 
--- c/qpy/QtOpenGL/qpyopengl.pro 2017-06-30 09:47:26.000000000 +0100 
+++ d/qpy/QtOpenGL/qpyopengl.pro 2017-10-31 17:07:37.694805459 +0000 
@@ -24,6 +24,11 @@ 
TARGET  = qpyopengl 
TEMPLATE = lib 

+mac { 
+  OBJECTIVE_SOURCES += core_profile_attributes.mm 
+  LIBS += -framework Foundation -framework Cocoa 
+ } 
+ 
SOURCES = \ 
      qpyopengl_attribute_array.cpp \ 
      qpyopengl_uniform_value_array.cpp 
diff -Naur c/sip/QtOpenGL/qgl.sip d/sip/QtOpenGL/qgl.sip 
--- c/sip/QtOpenGL/qgl.sip 2017-06-30 09:47:26.000000000 +0100 
+++ d/sip/QtOpenGL/qgl.sip 2017-10-31 17:30:26.025295693 +0000 
@@ -20,6 +20,14 @@ 
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 


+%ModuleCode 
+#include <qgl.h> 
+ 
+typedef struct GDevice** GDHandle; 
+void* select_3_2_mac_visual(); 
+ 
+%End 
+ 
namespace QGL 
{ 
%TypeHeaderCode 
@@ -257,6 +265,13 @@ 
%End 
%End 

+%TypeCode 
+ virtual void* chooseMacVisual(GDHandle handle) 
+ { 
+  return select_3_2_mac_visual(); 
+ } 
+%End 
+ 
public: 
    void deleteTexture(GLuint tx_id); 
    static void setTextureCacheLimit(int size); 
@@ -478,4 +493,6 @@ 

%ModuleHeaderCode 
#include <qpyopengl_api.h> 
+typedef struct GDevice** GDHandle; 
+void* select_3_2_mac_visual(); 
%End 
+0

这看起来不错,谢谢你的彻底答案!我无法对此进行测试,因此希望OP能够回复并验证它是否适用于较新版本的PyQt4。 – CodeSurgeon

+1

@CodeSurgeon。看起来我们都在这个黑暗中,但希望这个补丁仍然会工作... – ekhumoro

+0

@ekhumoro 感谢您的答案,这看起来很有希望! 我已经离开了一段时间,刚刚回到这个问题。 要清楚,你的补丁完全取代你提供的coregl-python链接,对吗? 我试过了你的指示,但我收到一条错误信息(请参阅我在问题中的第二个编辑)。任何想法是什么导致这个? 不幸的是,我只是盲目地按照你的指示在这里,我没有太多的帮助。 – Elensil