PyQt5とvtkを使ってOpenFOAMの結果を可視化してみた その2(「ファイルを開く」の実装編) 

#はじめに
以前に投稿した内容 ↓ をさらに拡張していきます．
[PyQt5とvtkを使ってOpenFOAMの結果を可視化してみた](https://qiita.com/matsubaradaisuke/items/0c1eeaf587ba7676a906 "PyQt5とvtkを使ってOpenFOAMの結果を可視化してみた")
今回は任意の計算結果を開く機能と閉じる機能を追加していきます．

完成イメージは下図になります．
![プレゼンテーション1.png](https://qiita-image-store.s3.amazonaws.com/0/132299/77e62ae9-c8c7-4406-3dd7-c96e1918ae06.png)
#環境
OS: windows10
OpenFOAM v4.0
python 3.6.4
pyqt5-5.9.2
vtk-8.0.0
numpy

#ファイル構成
case/
　├ ToolBar.py　(本プログラム)
　├ open_file.png
　├ quit.png
　└ pitzDaily
　　　　├ 0 (中身はp,Uなど)
　　　　├ constant
　　　　├ system
　　　　└ a.foam (可視化指定用の空ファイル)

Github↓
https://github.com/matsubaraDaisuke/my_qiita/tree/master/02%20ToolBarClass

これを完成させるにあたって以下の手順を踏んでいきます．
1 ツールバーの実装
2「ファイルを開く」の機能を実装
3「ファイルを閉じる」の機能を実装

また，クラス構成は以下のようにしています．
1 MyForm :メインとなるクラス．QMainWindowを継承している．ボタン実行時の関数はここで実装している．
2 Ui_MainWindow :ボタンの配置などを司るクラス．クラス「MyForm」の中で実体化される．メソッド「 setupUi」でUI関係の配置を指定する．今回配置するツールバーもここで配置される．

#1. ツールバーの実装
pyqtでツールバーを使いたい場合はQToolBarを用います．QToolBarについては多くの資料があるためここでは詳しくは説明しません．
QToolBarにアイコンを追加する場合はQActionを用います．
QActionでアイコン画像や，ステータスバーへのメッセージを指定します．

本プログラムでは，「ファイルを開く」機能のQAction名を「actionOpenFile」，「ファイルを閉じる」機能のQAction名を「actionResetFile」，これらを配置するQToolBar名を「toolBar」としている．

ツールバーの実装を行っているクラス「Ui_MainWindow」の実装を以下に示します．

```py3:ToolBar.py
#!/usr/bin/env python

__version__ = "1.0"

import sys
import os

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtk as vtk
import numpy as np
from vtk.util.numpy_support import vtk_to_numpy

#UI配置用のクラス
class Ui_MainWindow(object):

	def setupUi(self, MainWindow):
		
		#引数で得たMainWindowに色々設定
		MainWindow.setObjectName("MainWindow")
		MainWindow.setWindowTitle("ToolBar v%s" %__version__)
		MainWindow.setEnabled(True)
		MainWindow.resize(1280,720)

		self.centralwidget = QWidget(MainWindow)

		self.toolBar = QToolBar(MainWindow) #ツールバーを用意
		self.toolBar.setObjectName("toolBar")
		MainWindow.addToolBar(self.toolBar) #ツールバーを配置
       #「ファイルを開く」用のアクションを用意
		self.actionOpenFile = QAction(MainWindow)
       #アイコンを設定
	    self.actionOpenFile.setIcon(QIcon("open_file.png"))
       #ツールバーに「ファイルを開く」アイコンを配置
	    self.toolBar.addAction(self.actionOpenFile)
       #「ファイルを削除」用のアクションを用意
        self.actionResetFile = QAction(MainWindow) 
       #アイコンを設定
        self.actionResetFile.setIcon(QIcon("quit.png")) 
       #ツールバーに「ファイルを削除」アイコンを配置
        self.toolBar.addAction(self.actionResetFile)      
```

#2.「ファイルを開く」の機能を実装

計算結果を指定して可視化できるように関数を定義していきます．
大部分は前回の記事と重複しているので，詳しい説明は省略します．
[PyQt5とvtkを使ってOpenFOAMの結果を可視化してみた](https://qiita.com/matsubaradaisuke/items/0c1eeaf587ba7676a906 "PyQt5とvtkを使ってOpenFOAMの結果を可視化してみた")
新しい部分として，QFileDialogがありますが，これを使うことでGUIでよく見られる「ファイルを開く」機能を使うことができます．第二引数でダイアログ名，第三引数で初期パス，第三引数で開くファイルの拡張子を指定できます．
![プレゼンテーション2.png](https://qiita-image-store.s3.amazonaws.com/0/132299/f3cb63ba-9da2-3069-f92a-9e1a9c27c5b9.png)

```py3:ToolBar.py
def openFile(self):
	
		#ここでファイルを開くを実行．開けるファイルは「.foam」形式に限定
		fileName = QFileDialog.getOpenFileName(self, 'Open file', os.path.expanduser('~') + '/Desktop',"OpenFOAM File (*.foam)")

		print(fileName)
		reader = vtk.vtkOpenFOAMReader()
		reader.SetFileName(str(fileName[0]))	#パス＋ファイル名が格納されるのは[0]番．「1]にはファイルの形式「OpenFOAM File (*.foam)」が格納される．

		reader.CreateCellToPointOn()
		reader.DecomposePolyhedraOn()
		reader.EnableAllCellArrays()
		reader.Update()

		filter2 = vtk.vtkGeometryFilter()
		filter2.SetInputConnection(reader.GetOutputPort())

		mapper = vtk.vtkCompositePolyDataMapper2()
		mapper.SetInputConnection(filter2.GetOutputPort())
		actor = vtk.vtkActor()
		actor.SetMapper(mapper)
		ren = vtk.vtkRenderer()
		##背景色の設定
		ren.GradientBackgroundOn()      #グラデーション背景を設定
		ren.SetBackground2(0.2,0.4,0.6) #上面の色
		ren.SetBackground(1,1,1)        #下面の色
		self.widget.GetRenderWindow().AddRenderer(ren)
		ren.AddActor(actor)

```
#3.「ファイルを閉じる」の機能を実装

「ファイルを閉じる」の関数を実装していく．


```py3
	def resetFile(self):


		self.ren = vtk.vtkRenderer()		#空のレンダーを作成
		##背景色の設定
		self.ren.GradientBackgroundOn()      #グラデーション背景を設定
		self.ren.SetBackground2(0.2,0.4,0.6) #上面の色
		self.ren.SetBackground(1,1,1)        #下面の色
	
		self.widget.GetRenderWindow().AddRenderer(self.ren) #貼り直し
		self.widget.Render()				#これがないと，反映が遅れる
```
#4. 関数をまとめるクラス「MyForm」を作成

```py3
#MainWindow用のクラス
class MyForm(QMainWindow): 
	#UIの配置や初期画面の設定はここで行う.
	def __init__(self, parent=None):
		QWidget.__init__(self, parent)
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)

		self.widget = QVTKRenderWindowInteractor(self)
		self.widget.Initialize()
		##背景色の設定
		self.ren = vtk.vtkRenderer()
		self.ren.GradientBackgroundOn()      #グラデーション背景を設定
		self.ren.SetBackground2(0.2,0.4,0.6) #上面の色
		self.ren.SetBackground(1,1,1)        #下面の色

		self.widget.GetRenderWindow().AddRenderer(self.ren)
		self.widget.Start()
		self.widget.show()

		self.setCentralWidget(self.widget)
		#UIのインスタンスを介してアイコンと実体(関数)をつなげる
		self.ui.actionOpenFile.triggered.connect(self.openFile)
		self.ui.actionResetFile.triggered.connect(self.resetFile)

	#アイコンの実体(関数)はここで宣言
	def resetFile(self):


		self.ren = vtk.vtkRenderer()		#空のレンダーを作成
		##背景色の設定
		self.ren.GradientBackgroundOn()      #グラデーション背景を設定
		self.ren.SetBackground2(0.2,0.4,0.6) #上面の色
		self.ren.SetBackground(1,1,1)        #下面の色
	
		self.widget.GetRenderWindow().AddRenderer(self.ren) #貼り直し
		self.widget.Render()				#これがないと，反映が遅れる
		
	def openFile(self):
	
		#ここでファイルを開くを実行．開けるファイルは「.foam」形式に限定
		fileName = QFileDialog.getOpenFileName(self, 'Open file', os.path.expanduser('~') + '/Desktop',"OpenFOAM File (*.foam)")

		print(fileName)
		reader = vtk.vtkOpenFOAMReader()
		reader.SetFileName(str(fileName[0]))	#パス＋ファイル名が格納されるのは[0]番．「1]にはファイルの形式「OpenFOAM File (*.foam)」が格納される．

		reader.CreateCellToPointOn()
		reader.DecomposePolyhedraOn()
		reader.EnableAllCellArrays()
		reader.Update()

		filter2 = vtk.vtkGeometryFilter()
		filter2.SetInputConnection(reader.GetOutputPort())

		mapper = vtk.vtkCompositePolyDataMapper2()
		mapper.SetInputConnection(filter2.GetOutputPort())
		actor = vtk.vtkActor()
		actor.SetMapper(mapper)
		ren = vtk.vtkRenderer()
		##背景色の設定
		ren.GradientBackgroundOn()      #グラデーション背景を設定
		ren.SetBackground2(0.2,0.4,0.6) #上面の色
		ren.SetBackground(1,1,1)        #下面の色
		self.widget.GetRenderWindow().AddRenderer(ren)
		ren.AddActor(actor)

if __name__ == '__main__':

	import sys

	app = QApplication(sys.argv)
	form = MyForm()
	form.show() 
	sys.exit(app.exec_())
```

さらに「MyForm」の重複部分を削除，統一化して完成です．

```py3
#MainWindow用のクラス
class MyForm(QMainWindow): 
	#UIの配置や初期画面の設定はここで行う.
	def __init__(self, parent=None):
		QWidget.__init__(self, parent)
		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)

		self.reader = vtk.vtkOpenFOAMReader()
		self.filter2 = vtk.vtkGeometryFilter()
		self.mapper = vtk.vtkCompositePolyDataMapper2()
		self.actor = vtk.vtkActor()

		self.widget = QVTKRenderWindowInteractor(self)
		self.widget.Initialize()
		##背景色の設定
		self.ren = vtk.vtkRenderer()
		self.widget.GetRenderWindow().AddRenderer(self.ren)
		self.widget.Start()
		self.widget.show()

		self.setCentralWidget(self.widget)
		#UIのインスタンスを介してアイコンと実体(関数)をつなげる
		self.ui.actionOpenFile.triggered.connect(self.openFile)
		self.ui.actionResetFile.triggered.connect(self.resetFile)
		self.setupInitView()

	def setupInitView(self):

		self.ren.GradientBackgroundOn()      #グラデーション背景を設定
		self.ren.SetBackground2(0.2,0.4,0.6) #上面の色
		self.ren.SetBackground(1,1,1)        #下面の色

	#アイコンの実体(関数)はここで宣言
	def resetFile(self):


		self.ren = vtk.vtkRenderer()		#空のレンダーを作成
        self.setupInitView()
	
		self.widget.GetRenderWindow().AddRenderer(self.ren) #貼り直し
		self.widget.Render()				#これがないと，反映が遅れる
		
	def openFile(self):
	
		#ここでファイルを開くを実行．開けるファイルは「.foam」形式に限定
		fileName = QFileDialog.getOpenFileName(self, 'Open file', os.path.expanduser('~') + '/Desktop',"OpenFOAM File (*.foam)")

		print(fileName)
		
		self.reader.SetFileName(str(fileName[0]))	#パス＋ファイル名が格納されるのは[0]番．「1]にはファイルの形式「OpenFOAM File (*.foam)」が格納される．

		self.reader.CreateCellToPointOn()
		self.reader.DecomposePolyhedraOn()
		self.reader.EnableAllCellArrays()
		self.reader.Update()

		self.filter2.SetInputConnection(self.reader.GetOutputPort())

		self.mapper.SetInputConnection(self.filter2.GetOutputPort())
		self.actor.SetMapper(self.mapper)
		self.ren.AddActor(self.actor)
		self.widget.GetRenderWindow().AddRenderer(self.ren)
```
#使い方
計算結果を開く場合
![プレゼンテーション３.png](https://qiita-image-store.s3.amazonaws.com/0/132299/25b1e3a3-f58b-8549-8897-7032db8fc092.png)
![プレゼンテーション４.png](https://qiita-image-store.s3.amazonaws.com/0/132299/84a69328-8d8c-1f61-4e5d-89ef909dc782.png)

計算結果を閉じる場合
![プレゼンテーション６.png](https://qiita-image-store.s3.amazonaws.com/0/132299/91a1c12d-2dd2-cd09-a240-9f52ac243f94.png)
#まとめ
また一歩paraviewに近づきました(paraviewはc++ですが)．
次回は，メッシュ表示切替やview方向の切り替えあたりをまとめようと思います