2022/09/09(金) [n年前の日記]
#2 [python][windows] PyOpenGLが使うfreeglut.dllの場所を調べた
Windows10 x64 21H2 + Python 3.9.13 x64 + PyOpenGL 3.1.6 で実験をしていたのだけど、freeglut.dll がどこに置いてあるのかが気になったので少し調べてみた。
参考ページは以下。
_【Python】PyOpenGLのインストール方法を解説 | ジコログ
参考ページは以下。
_【Python】PyOpenGLのインストール方法を解説 | ジコログ
◎ 前提条件。 :
まず、自分の環境では、以下から .whl を入手してインストールしている。
_Archived: Python Extension Packages for Windows - Christoph Gohlke
インストールは以下。
この .whl には freeglut*.dll が同梱されているので、巷の解説ページで書かれているように、freeglut.dll を別途入手して C:\Windows\System32\ にコピーしなくても PyOpenGL が動いてくれる。
_Archived: Python Extension Packages for Windows - Christoph Gohlke
- PyOpenGL-3.1.6-cp39-cp39-win_amd64.whl
- PyOpenGL_accelerate-3.1.6-cp39-cp39-win_amd64.whl
インストールは以下。
pip install PyOpenGL-3.1.6-cp39-cp39-win_amd64.whl pip install PyOpenGL_accelerate-3.1.6-cp39-cp39-win_amd64.whl
この .whl には freeglut*.dll が同梱されているので、巷の解説ページで書かれているように、freeglut.dll を別途入手して C:\Windows\System32\ にコピーしなくても PyOpenGL が動いてくれる。
◎ freeglut.dllの在り処。 :
さておき。その freeglut.dll は、実際はどこにあるのか…。調べた感じでは、どうも以下のフォルダに .dll が入ってるように見えた。
以下の2つが入っていた。
ちなみに、Python 2.7.18 + PyOpenGL 3.1.0 上で確認してみたら、以下のファイルが入っていた。freeglut だけではなく GLUT も入っている。
これらの .dll は、以下のスクリプトから読み込まれている。
処理をしてる部分を抜き出してみる。
(Python3インストールフォルダ)\Lib\site-packages\OpenGL\DLLS\
以下の2つが入っていた。
- freeglut64.vc16.dll
- gle64.vc16.dll
ちなみに、Python 2.7.18 + PyOpenGL 3.1.0 上で確認してみたら、以下のファイルが入っていた。freeglut だけではなく GLUT も入っている。
- freeglut32.vc9.dll
- gle32.vc9.dll
- glut32.vc9.dll
これらの .dll は、以下のスクリプトから読み込まれている。
(Python3インストールフォルダ)\Lib\site-packages\OpenGL\platform\win32.py
処理をしてる部分を抜き出してみる。
def GLUT( self ): for possible in ('freeglut%s.%s'%(size,vc,), 'glut%s.%s'%(size,vc,)): # Prefer FreeGLUT if the user has installed it, fallback to the included # GLUT if it is installed try: return ctypesloader.loadLibrary( ctypes.windll, possible, mode = ctypes.RTLD_GLOBAL ) except WindowsError: pass return None「freeglut*.*.dll」もしくは「glut*.*.dll」が見つかったらそれらを使うようだなと…。
◎ freeglut.dllを差し替えてみる。 :
試しに、前述の処理部分を以下のように修正してみた。
これで、freeglut??.vc*.dll ではなく、freeglut.dll を読み込んでくれるのではないかと…。
以下から、freeglut 3.0.0 MSVC Package (freeglut-MSVC-3.0.0-2.mp.zip) を入手。
_freeglut Windows Development Libraries
解凍すると、bin/x64/freeglut.dll が入ってる。
以下のフォルダにコピー。
加えて、freeglut64.vc16.dll を freeglut64.vc16.dll.orig にリネームした。これで freeglut64.vc16.dll は呼び出されない状態になって、別途導入した freeglut.dll が呼び出されてる状態になるはず。
手元のサンプルスクリプトを動かしてみたところ、この状態でも動いてくれた。
しかし、上記の freeglut.dll はバージョンが 3.0.0 と古い。
MSYS2 + MinGW-w64 で入手できる freeglut.dll (libfreeglut.dll) は 3.2.2 なので、そちらも試してみた。
以下のファイルをコピーしてくる。
これを、freeglut.dll にリネームして、前述の場所にコピー。この状態でも簡単なサンプルなら動いてくれた。
ところで。手元の環境では、Python 3.9.13 のインストールフォルダ直下に freeglut.dll を置いても動いているわけで…。もし、その場所に置いても問題無く動くのであれば…。巷の解説ページは C:\Windows\System32\ 以下にコピーしてる事例がほとんどだけど、Pythonインストールフォルダに入れたほうが、まだ安全だったりしないのだろうか…。
for possible in ('freeglut%s.%s'%(size,vc,), 'glut%s.%s'%(size,vc,)): # ↓ for possible in ('freeglut', 'freeglut%s.%s' % (size,vc), 'glut%s.%s' % (size,vc)):
これで、freeglut??.vc*.dll ではなく、freeglut.dll を読み込んでくれるのではないかと…。
以下から、freeglut 3.0.0 MSVC Package (freeglut-MSVC-3.0.0-2.mp.zip) を入手。
_freeglut Windows Development Libraries
解凍すると、bin/x64/freeglut.dll が入ってる。
以下のフォルダにコピー。
(Python3インストールフォルダ)\Lib\site-packages\OpenGL\DLLS\ or (Python3インストールフォルダ)\
加えて、freeglut64.vc16.dll を freeglut64.vc16.dll.orig にリネームした。これで freeglut64.vc16.dll は呼び出されない状態になって、別途導入した freeglut.dll が呼び出されてる状態になるはず。
手元のサンプルスクリプトを動かしてみたところ、この状態でも動いてくれた。
しかし、上記の freeglut.dll はバージョンが 3.0.0 と古い。
MSYS2 + MinGW-w64 で入手できる freeglut.dll (libfreeglut.dll) は 3.2.2 なので、そちらも試してみた。
以下のファイルをコピーしてくる。
(MSYS2インストールフォルダ)\mingw64\bin\libfreeglut.dll
これを、freeglut.dll にリネームして、前述の場所にコピー。この状態でも簡単なサンプルなら動いてくれた。
ところで。手元の環境では、Python 3.9.13 のインストールフォルダ直下に freeglut.dll を置いても動いているわけで…。もし、その場所に置いても問題無く動くのであれば…。巷の解説ページは C:\Windows\System32\ 以下にコピーしてる事例がほとんどだけど、Pythonインストールフォルダに入れたほうが、まだ安全だったりしないのだろうか…。
◎ 余談。glutIdleFunc()が動かなくて悩んだ。 :
PyOpenGL関係のサンプルファイルを眺めていると、glutIdleFunc() を使っている事例を結構見かけるのだけど。手元の環境、Windows10 x64 21H2 + Python3.9.13 + PyOpenGL 3.1.6 上ではエラーが出てしまってかなり悩んだ。
やりたいことは一定の時間間隔で処理を呼び出したいだけなので、glutIdleFunc() の代わりに glutTimerFunc() を使えば目的は果たせるのだけど…。若干気になる状態なわけで。ググってみても仕様が変わった等の話を見かけないし。この症状が気になって freeglut.dll を色々差し替えていたわけで。結局動作は変わらなかったのだけど。
ふと、渡していた関数に引数があることに気づいた。glutTimerFunc() に渡す関数は引数を一つ用意するのだけど、その関数を安易に流用して、glutIdleFunc() にそのまま渡していたのが問題だった。何の引数も取らない関数を渡したらエラーが解消された。そういうオチか…トホ。
> py 03_teapot_draw_idle.py Traceback (most recent call last): File "C:\Python\Python39-64\lib\site-packages\OpenGL\GLUT\special.py", line 130, in safeCall return function( *args, **named ) TypeError: idle() missing 1 required positional argument: 'value' GLUT Idle callback <function idle at 0x000002A6438D8B80> with (),{} failed: returning None idle() missing 1 required positional argument: 'value'
やりたいことは一定の時間間隔で処理を呼び出したいだけなので、glutIdleFunc() の代わりに glutTimerFunc() を使えば目的は果たせるのだけど…。若干気になる状態なわけで。ググってみても仕様が変わった等の話を見かけないし。この症状が気になって freeglut.dll を色々差し替えていたわけで。結局動作は変わらなかったのだけど。
ふと、渡していた関数に引数があることに気づいた。glutTimerFunc() に渡す関数は引数を一つ用意するのだけど、その関数を安易に流用して、glutIdleFunc() にそのまま渡していたのが問題だった。何の引数も取らない関数を渡したらエラーが解消された。そういうオチか…トホ。
def on_timer(value): """timer callback.""" global yrot yrot += 0.5 glutPostRedisplay() # Redraw glutTimerFunc(int(1000 / FPS), on_timer, 0) def idle(): """idle callback.""" global yrot yrot += 0.5 glutPostRedisplay() # Redraw # ... if USE_IDLE: glutIdleFunc(idle) else: glutTimerFunc(int(1000 / FPS), on_timer, 0)
[ ツッコむ ]
以上です。