Hey #b3d peeps!!
bpy.data.versionでは?
「ファイルのバージョンを調べるだけだったらデータロードした後にPythonコンソールから bpy.data.version でいいのでは?」という指摘を受けました。ありがとうございます。
確かにそれでも「ファイルのバージョンを表示する」にはいいんですが、「データをロードする」ことによって、「その直後に[Ctrl]+[S]を無意識のうちに押していた」とかやると大変幸せなことになるので、それは採用しませんでした。
例えば以下のようなケース。ファイル保存した時、ファイルに記録されるバージョンは「現在使っているBlenderのバージョン」ですが、bpy.data.versionは更新されません。
Blender 2.72でファイルを保存(file=272)
Blender 2.75で保存したファイルを開く(file=272,bpy.data.version=272)
[Ctrl]+[S]キーを押して保存(file=275,bpy.data.version=272)
checkblendfilever.py のコードを読んでいきます。(スクリプトや使い方等はこちらのファイルをダウンロード、展開します。)
(結構適当に。)
checkblendfilever.py を見ていきましょう。
#!BPY # ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # <pep8 compliant> import bpy bl_info = { "name": "show .blend file version", "author": "blugjp", "blender": (2, 75, 0), "location": "File > Check Blender file Version", "description": "Check .blend file,and show the version when it saved.", "warning": "", "category": "misc", } def open_file(filepath): file = open(filepath, "rb") header = file.read(16) file.close() errmsg = "Wrong file." if len(header) < 16: errmsg = "Can\'t get enough..." head = header[:5] if head != b'BLEND': errmsg = "Wrong header.This is not .blend file?" head = header[5:7] version = "" if head == b'ER': version = header[9:12] elif head == b'FI': # for old compressed file errmsg = "Can¥'t get the version. compressed?" version = "" if version != '': message = bpy.path.basename(filepath) + " - " \ + version[0:1].decode('utf-8') \ + "."+version[1:3].decode('utf-8') else: message = "[Error]:" + errmsg bpy.context.scene.bvdialog_message = message bpy.ops.object.dialog_operator('INVOKE_DEFAULT') class DialogOperator(bpy.types.Operator): bl_idname = "object.dialog_operator" bl_label = "Information" def execute(self, context): return {'FINISHED'} def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) def draw(self, context): layout = self.layout col = layout.column() col.label(text=bpy.context.scene.bvdialog_message) class CheckBlendfileVerOperator(bpy.types.Operator): """Check Blender Ver""" bl_label = "Check Blender file Version" bl_idname = "wm.check_blendfile_ver" filepath = bpy.props.StringProperty(subtype="FILE_PATH") def execute(self, context): open_file(self.filepath) return {'FINISHED'} def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} # Registration def add_object_button(self, context): self.layout.operator(CheckBlendfileVerOperator.bl_idname, text="Check Blender file Version", icon='TEXT') def register(): bpy.utils.register_class(CheckBlendfileVerOperator) bpy.types.INFO_MT_file.append(add_object_button) bpy.utils.register_class(DialogOperator) bpy.types.Scene.bvdialog_message =\ bpy.props.StringProperty(name="message", default='<message>') def unregister(): bpy.utils.unregister_class(CheckBlendfileVerOperator) bpy.types.INFO_MT_file.remove(add_object_button) bpy.utils.unregister_class(DialogOperator) del bpy.types.Scene.bvdialog_message if __name__ == "__main__": register()
#!BPY
シェルから実行時、先頭一行目が決まった書き方であったら、プログラムを起動して読み込ませる仕組み、ですが、BlenderPythonの場合は #!BPY を識別子としてのみ使用します。
通常のPythonスクリプトでは #!/usr/bin/env python ですね。
ちなみに、2行目に、# -*- coding: utf-8 -*- と書かれているスクリプトもあります。これは「このファイルの文字エンコードが UTF-8」というもので、そう宣言しておいて、日本語を含むスクリプトを保存・実行することもできるようになっています。
# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK #####
GPL(GNU Public License)のライセンスヘッダーを書いています(“BEGIN GPL LICENSE BLOCK”から”END GPL LICENSE BLOCK”で囲まれた部分)が、これはBlenderが「Blender Python API を使う場合にGPLでリリースすることを推奨する」、ということに従ったものです。
例えば組織内のみで使用する Add-on を書いて、それを公開しない場合には付けなくてもいいです。
# <pep8 compliant>
Pythonには PEP8 というコード規約があります。それに従って書かれたコードはこのコメント行を追加できます。
pep8コマンド(要パッケージ)を使って、該当のPythonソースコードをPEP8準拠かどうかチェックし、エラーの出たところを修正します。
import bpy
Pythonでは import コマンドでパッケージをインポートして使用することができますが、ここではBlender Python API のパッケージであるbpyパッケージをインポートしています。
bl_info = { "name": "show .blend file version", "author": "blugjp", "blender": (2, 75, 0), "location": "File > Check Blender file Version", "description": "Check .blend file,and show the version when it saved.", "warning": "", "category": "misc", }
Add-on として登録する際に必要な情報を bl_info に記載します。Add-on名や対応するBlenderのバージョンなど。
def open_file(filepath): file = open(filepath, "rb") header = file.read(16) file.close() errmsg = "Wrong file." if len(header) < 16: errmsg = "Can\'t get enough..." head = header[:5] if head != b'BLEND': errmsg = "Wrong header.This is not .blend file?" head = header[5:7] version = "" if head == b'ER': version = header[9:12] elif head == b'FI': # for old compressed file errmsg = "Can¥'t get the version. compressed?" version = "" if version != '': message = bpy.path.basename(filepath) + " - " \ + version[0:1].decode('utf-8') \ + "."+version[1:3].decode('utf-8') else: message = "[Error]:" + errmsg bpy.context.scene.bvdialog_message = message bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
実際にファイルを開いて先頭の16バイトを読み込み、記録されているバージョンの情報を取得している関数です。最後にダイアログを表示します。
class DialogOperator(bpy.types.Operator): bl_idname = "object.dialog_operator" bl_label = "Information" def execute(self, context): return {'FINISHED'} def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) def draw(self, context): layout = self.layout col = layout.column() col.label(text=bpy.context.scene.bvdialog_message)
ファイルのバージョンを表示するダイアログのためのクラスです。
class CheckBlendfileVerOperator(bpy.types.Operator): """Check Blender Ver""" bl_label = "Check Blender file Version" bl_idname = "wm.check_blendfile_ver" filepath = bpy.props.StringProperty(subtype="FILE_PATH") def execute(self, context): open_file(self.filepath) return {'FINISHED'} def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'}
メニューをクリックした際に実行されるクラスです。ボタンが押されるとファイルセレクターを表示します。
def add_object_button(self, context): self.layout.operator(CheckBlendfileVerOperator.bl_idname, text="Check Blender file Version", icon='TEXT')
メニューを登録する関数です。
def register(): bpy.utils.register_class(CheckBlendfileVerOperator) bpy.types.INFO_MT_file.append(add_object_button) bpy.utils.register_class(DialogOperator) bpy.types.Scene.bvdialog_message =\ bpy.props.StringProperty(name="message", default='<message>')
Add-on 登録時に実行される関数です。
def unregister(): bpy.utils.unregister_class(CheckBlendfileVerOperator) bpy.types.INFO_MT_file.remove(add_object_button) bpy.utils.unregister_class(DialogOperator) del bpy.types.Scene.bvdialog_message
Add-on 解除時に実行される関数です。
if __name__ == "__main__": register()
これはおまじないのようなもの、と考えていただければ。このスクリプト自体が実行された時に register 関数が実行されます。
http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons 参照。
例えばOS Xはファイルヘッダーを元にファイルの先頭数バイトでファイルの種別を判別でき、シェル上で file <ファイル> として実行すると、以下のような情報を表示します。
Blender3D, saved as 64-bits little endian with version 2.75
他にも、Linux、FreeBSD等では magic ファイルにBlender用の記載がされていれば、同じく find コマンドで判別が可能です。
CheckBlendfileVerOperator や DialogOperator は「オペレータークラス」といい、人間の操作に対応するためのクラスとなっています。今回の場合はメニューをクリック、という操作を起点にファイルブラウザを開く、開いた後に確認用のダイアログを出すところに対応します。
オペレータークラスは決まった名前の関数を持ちます。今回出てくるのは以下の関数。
draw:
invoke:
excute:
パネルなど、表示を行う関数
ダイアログなど、ユーザーの操作を中断する処理を行う関数
決定時に実行される関数
CheckBlendfileVerOperator の処理
DialogOperator の処理
CheckBlendfileVerOperatorクラスをファイルメニューの選択肢をクリックすることで実行するためのメニューアイテムを登録します。
登録オペレーター名:
タイトル:
アイコン:
CheckBlendfileVerOperator.bl_idname
“Check Blender file Version”
‘TEXT’
Add-on登録時に、 CheckBlendfileVerOperator クラス、DialogOperator クラスを登録します。また、ダイアログ出力時にパラメーターをやりとりするための bpy.props.StringProperty を登録します。
Add-on登録解除時に、CheckBlendfileVerOperator クラス、DialogOperator クラスを削除します。また、ダイアログ出力時にパラメーターをやりとりするための bpy.props.StringProperty を破棄します。
このAdd-onでは、メニューからファイルダイアログを呼び出す、結果としてダイアログに表示させる、ということをしました。実行順がが一方方向な、このような処理だと比較的簡単に書けますので、最初はこの辺りからチャレンジしてみるといいかと思います。