ここで紹介した方法でmatplotlibやspicyをinstallしたが、実際にmatplotlibを使おうとするとハングする問題が発生。
アクティビティモニターでチェックしてみると"fc-list"なるプロセスがCPUを食ってるよう。
色々調べてみるとここで、同じ現象が取り上げられてる。対策は以下のコマンドを打てとのこと。
$ cd ~/.matplotlib/
$ fc-list
上をうつと大量のログが出力されたのち、普通に使えるようになった。
PyQtでvtkの3次元表示ViewをWidgetとしてEmbbedしたい場合の自分メモ
普通はQVTKWidgetPlugin.dll/.soなりをQt Designerのpluginsフォルダに入れる事によって、 Qt DesignerのサイドバーにQVTKWidgetが表示され、それをDrag&DropするだけでLayoutされる!というスマートなやり方が正しいはずなんだが。
ことpython + QVTKWidgetに限ってはなんか以下の理由でできない(なんで?と思うが) (Windows + Anaconda環境と、MacOS + homebrew環境で確認)
↓のサイトによるとfrom QVTKWidget import QVTKWidgetに存在しない。from vtk import QVTKWidgetなら存在するとかあるが、これも存在しないと起こられる。
で、色々探してみるとfrom vtk.qt4.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor を使えというサイトを見つけた。
ためしにこれで試すとうまくいくが、もしQt DesignerでQVTKWidgetPlugin.dll/soを使ってDrag&DropでLayoutをすると強制的にQVTKWidgetをloadされちゃうので できない。
なのでQt Desingerのpromote toを使って、
1.で追加したWidgetを右クリックしてpromote to -> QVTKRenderWindowInteractorを選択してpromoteさせる
のQVTKRenderWindowInteractorクラスをPromotion用に追加した所↓
4.の後↓のようになる
という手順を踏むとQt Designderでの変更に対して何もしなくても以下のpythonコードで呼べる。 (C++と.uiファイルが共通化できなかったりというデメリットもあるが、現状これしかない気もする。)
import sys
import vtk
from PyQt4 import QtCore, QtGui, uic
#from ui_mainwindow import Ui_MainWindow
class testPyQt(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow = uic.loadUiType("ui_mainwindow.ui", self)[0]
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# setup connection
self.ui.pushButton.clicked.connect(self.onClickButton)
### !!! Added from here !!! ###
self.renderer = vtk.vtkRenderer()
self.ui.qvtkWidget.GetRenderWindow().AddRenderer(self.renderer)
self.ui.qvtkWidget.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
# Add initial actores
self.axes = vtk.vtkAxesActor()
transform = vtk.vtkTransform()
transform.Translate(0.0, 0.0, 0.0)
self.axes.SetUserTransform(transform)
self.axes.GetXAxisCaptionActor2D().GetTextActor().SetTextScaleModeToNone()
self.axes.GetYAxisCaptionActor2D().GetTextActor().SetTextScaleModeToNone()
self.axes.GetZAxisCaptionActor2D().GetTextActor().SetTextScaleModeToNone()
self.axes.SetTotalLength(1, 1, 1)
self.renderer.AddActor(self.axes)
self.renderer.ResetCamera()
### !!! Added until here !!! ###
# Added to draw call should be called after open window
def show(self):
QtGui.QMainWindow.show(self)
self.ui.qvtkWidget.Initialize()
@QtCore.pyqtSlot()
def onClickButton(self):
self.ui.label.setText("Pushed!!")
def main():
app = QtGui.QApplication(sys.argv)
window = testPyQt()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
show()関数をoverrideしているのは、RenderingがWindowが開かないうちに実行されるのを避ける為。例えば、Mac環境でinit内でself.qvtkWidget.Initialize()をやってしまうと以下の様なエラーが大量に出る
ERROR: In /tmp/vtk20151007-9396-c4u0qe/VTK-6.2.0/Rendering/OpenGL/vtkOpenGLPolyDataMapper2D.cxx, line 442
vtkOpenGLPolyDataMapper2D (0x7f9d3c171460): failed after RenderOverlay 1 OpenGL errors detected
0 : (1286) Invalid framebuffer operation
以下のForumで議論されている内容で、Forumでもshowを明示的に呼んでからRenderしろのような事が書かれている(と思う)。 http://paulklemm.com/zenf/blog/2012/08/20/install-python-together-with-vtk-using-homebrew-for-mountain-lion/ なのでmain()内で呼ばれるshow()の後にInteractorのInitialize()が呼ばれる様にするためにこの構成。
PyQt(PyQt4)でGUIプログラムする自分用メモ
.uiファイル+Qt Designerが使いやすいのもQtのメリットだと思ってるのでpythonからもUIのレイアウトはこれでやりたい。
.uiでレイアウトしてpythonで読み込むには2つ方法があるらしい。
1.は.pyに統一できていいが、開発中は毎UI変更の度に変換が必要でめんどい。
2.は変換は不要だが.uiファイルをPackageに含まないといけない(それでもいい場合もある?)。
↓によると開発中は2.でお手軽に、配布時に1.で変換するみたいのが良いってことかな?
https://riverbankcomputing.com/pipermail/pyqt/2010-September/027970.html
というわけでサンプル作ってみた。
まずQt Designerで簡単なUI(PushButtonを押すと、Labelの文字が変わるというだけ)を作成
.uiファイルをloadして表示するコードが以下。
import sys
from PyQt4 import QtCore, QtGui, uic
# form classを直接importする場合は必要
#from ui_mainwindow import Ui_MainWindow
class testPyQt(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# form classを直接importする場合は↓の行が不要になる
Ui_MainWindow = uic.loadUiType("ui_mainwindow.ui", self)[0]
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# setup connection
self.ui.pushButton.clicked.connect(self.onClickButton)
@QtCore.pyqtSlot()
def onClickButton(self):
self.ui.label.setText("Pushed!!")
def main():
app = QtGui.QApplication(sys.argv)
window = testPyQt()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
testPyQtクラスのself.uiにform class(C++ではUi::MainWindowClassとかでmoc_*.cppファイルに生成されてたやつだと思う)をnewする。
検索するとtestPyQtクラス自体をform classから継承する例や、self.uiにuic.loadUi()で生成されたQMainWindow(or QWidget)を直接showするコード(下記)もあったというか結構多い。
前者はUIと制御コードが一緒のclassになっちゃうので避けたい実装、後者はtestPyQtでQMainWindowを継承している意味がわかんなくなるので、その辺のやり方を避けると上のやり方に落ち着きそう。
class testPyQt(QtGui.QMainWindow):
def __init__(self):
# uic.loadUi returns QMainWindow
self.ui = uic.loadUi("ui_mainwindow.ui")
self.ui.show()
def main():
app = QtGui.QApplication(sys.argv)
window = testPyQt()
# window.show() # ここでshowしない
sys.exit(app.exec_())
結果、こんな感じにできる。