wxPythonでアプリケーションを開発していると、wx.Clipboard(クリップボード)やドラッグ&ドロップ(D&D)の実装に必ずと言っていいほど登場するのが wx.DataObject です。
「wx.Clipboard.SetData() に wx.TextDataObject を渡したけど、この DataObject って一体何?」 「ドラッグ&ドロップでも DataObject が出てくる。クリップボードと何が違うの?」 「テキストやファイルは扱えたけど、自作クラスのオブジェクトのような独自データをコピペするにはどうすれば?」
wx.DataObject は、wxPythonにおけるデータ転送の「核」となる非常に重要な概念です。
この記事では、wx.DataObject のアーキテクチャ(仕組み)から、標準的な使い方、さらには独自データを転送するためのwx.CustomDataObject の自作方法まで、データ転送の裏側を徹底的に解説します。
wx.DataObject とは? – データの「運び屋」
wx.DataObject とは、ひと言でいうと**「転送されるデータそのものと、その『データの形式』をカプセル化したオブジェクト(入れ物)」**です。
wx.Clipboard(クリップボード)や wx.DropTarget(D&Dの受け手)は、データを運ぶための**「輸送路」に例えられます。wx.DataObject は、その輸送路の上を流れていく「荷物」**そのものです。
wx.DataObject 自体は「抽象基底クラス」と呼ばれる「設計図」のようなものです。そのため、私たちがプログラムで直接 wx.DataObject のインスタンスを作ることはありません。
実際には、wx.TextDataObject(テキスト用)や wx.FileDataObject(ファイル用)といった、wx.DataObject の設計図を具体的に実装した「具象クラス」を使います。
データの「形式」を識別する wx.DataFormat
wx.DataObject が「荷物」だとすれば、その荷物に貼られている「品名ラベル」(例: “テキスト”、”画像”、”ファイル”)の役割を果たすのが wx.DataFormat です。
wx.DataFormat は、そのデータが何の種類であるかを示すIDのようなものです。wx.Clipboard やD&Dの受け手は、この DataFormat を見て、自分が処理できるデータかどうかを判断します。
wxPythonには、よく使われる標準的な形式があらかじめ定義されています。
wx.DF_TEXT: テキスト形式wx.DF_BITMAP: ビットマップ(画像)形式wx.DF_FILENAME: ファイル名(パス)のリスト形式wx.DF_UNICODETEXT: (推奨)Unicodeテキスト形式
wx.DataFormat("my/custom-format") のように、独自の形式ID(MIMEタイプに似た文字列)を自分で定義することも可能です。
wxPythonが提供する標準的な DataObject 3選
多くの場合、私たちが独自に DataObject を作る必要はなく、wxPythonがあらかじめ用意してくれている標準の DataObject を使うだけで十分です。
wx.TextDataObject (または wx.UnicodeTextDataObject)
- 形式:
wx.DF_UNICODETEXT(またはwx.DF_TEXT) - 用途: クリップボードでのテキストのコピー&ペースト
- 使い方:
SetText(string): 送信したい文字列をセットします。GetText(): 受信した文字列を取得します。
- 補足: 現代のアプリケーションでは、ASCII文字以外(日本語など)を正しく扱うために、
wx.DF_TEXTを使うwx.TextDataObjectよりも、wx.DF_UNICODETEXTを使うwx.UnicodeTextDataObjectの使用が強く推奨されます。ただし、利便性のためにwx.TextDataObjectが内部でwx.DF_UNICODETEXTを扱ってくれることも多いです。
import wx
# コピー(書き込み)
text_to_copy = "こんにちは"
data_obj = wx.TextDataObject(text_to_copy) # wx.UnicodeTextDataObject を使うのがより望ましい
# wx.TheClipboard.SetData(data_obj)
# ペースト(読み込み)
data_obj = wx.TextDataObject()
# wx.TheClipboard.GetData(data_obj)
# received_text = data_obj.GetText()wx.FileDataObject
- 形式:
wx.DF_FILENAME - 用途: 主にドラッグ&ドロップ(D&D)
- 使い方:
AddFile(path): 送信したいファイルパス(フルパス)を追加します。GetFilenames(): 受信したファイルパスのリストを取得します。
- 補足: エクスプローラーなどからファイルをアプリ上にドラッグ&ドロップされた際、
wx.DropTargetと組み合わせて使い、ドロップされたファイルの一覧を取得するのが主な用途です。
wx.BitmapDataObject
- 形式:
wx.DF_BITMAP - 用途: 画像のコピー&ペースト
- 使い方:
SetBitmap(wx.Bitmap): 送信したいwx.Bitmapオブジェクトをセットします。GetBitmap(): 受信したwx.Bitmapオブジェクトを取得します。
wx.DataObject の仕組み: SetData と GetData
では、wx.TextDataObject の GetText() や wx.BitmapDataObject の GetBitmap() は、内部でどのように動いているのでしょうか?
wx.DataObject の中核は、Python側からは直接見えにくい、C++層の抽象メソッド SetData(format, data) と GetData(format) です。
wx.TextDataObjectは、SetDataが呼ばれると渡されたバイトデータを内部でSetTextし、GetData(wx.DF_TEXT)が要求されるとGetText()の内容をバイトデータとして返すように実装されています。- wxPythonの
wx.Clipboard.GetData()は、「クリップボードさん、wx.DF_TEXT形式のデータをください」と要求します。 - クリップボードは、現在保持している
DataObject(例えばwx.TextDataObject)に「wx.DF_TEXT形式でデータを頂戴」と依頼します。 wx.TextDataObjectは、自身のGetText()の内容を返します。
この仕組みのおかげで、私たちは wx.Clipboard.GetData() に「空の wx.TextDataObject」を渡すだけで、自動的にテキストデータが GetText() で取得できる状態になるのです。
wx.DataObjectComposite – 複数の形式を同時に扱う
クリップボードにコピーされるデータは、必ずしも1種類とは限りません。
例えば、WebページやExcelのセルをコピーしたとき、クリップボードには「リッチテキスト(RTF)形式」「HTML形式」「Unicodeテキスト形式」「CSV形式」など、複数の形式のデータが同時に格納されます。
ペースト側のアプリ(Wordやメモ帳など)は、自分が処理できる形式(WordならRTF、メモ帳ならテキスト)を選んで GetData を要求します。
これをwxPythonで実現するのが wx.DataObjectComposite です。これは、複数の DataObject を1つの「荷物セット」にまとめるための DataObject です。
import wx
# テキストデータ
text_data = wx.TextDataObject("これはテキストです")
# (仮の)ビットマップデータ
bitmap = wx.Bitmap(100, 100)
bitmap_data = wx.BitmapDataObject(bitmap)
# 2つの形式を1つにまとめる
composite_data = wx.DataObjectComposite()
composite_data.Add(text_data)
composite_data.Add(bitmap_data) # Addの順番は優先順位(デフォルト)として使われる
# これをクリップボードにセットする
# wx.TheClipboard.SetData(composite_data)こうすることで、ペースト側が wx.DF_TEXT を要求すればテキストが、wx.DF_BITMAP を要求すれば画像が返されるようになります。
【中級者向け】wx.CustomDataObject で独自データを転送する
wx.DataObject の真価は、テキストやファイルだけでなく、アプリケーション独自のデータ(例: 自作クラスのインスタンス)を転送できる点にあります。
ここでは、Pythonの pickle モジュールを使い、任意のPythonオブジェクトをクリップボードやD&Dで転送する方法を紹介します。
ステップ1: 独自の wx.DataFormat を定義する
まず、独自データ形式のIDを決めます。他のアプリと重複しないよう、MIMEタイプのように命名するのが一般的です。
# 独自形式のIDを定義
MY_CUSTOM_FORMAT = wx.DataFormat("application/x-my-app-object")ステップ2: wx.CustomDataObject を継承する
wx.DataObject を直接継承するのは複雑なため、wxPythonは wx.CustomDataObject という便利な基底クラスを提供しています。
import pickle
class MyCustomDataObject(wx.CustomDataObject):
def __init__(self, obj=None):
# 1. 親クラスのコンストラクタで、独自のDataFormatを渡す
super().__init__(MY_CUSTOM_FORMAT)
# 2. オブジェクトをシリアライズ(バイト化)してセットする
if obj:
self.SetObject(obj)
def SetObject(self, obj):
# Pythonオブジェクトを pickle でバイト列に変換
data_bytes = pickle.dumps(obj)
# 内部メソッド SetData でバイト列をセット
self.SetData(data_bytes)
def GetObject(self):
# 内部メソッド GetData でバイト列を取得
data_bytes = self.GetData()
if not data_bytes:
return None
# バイト列を pickle でPythonオブジェクトに復元
try:
return pickle.loads(data_bytes)
except:
return Noneステップ3: SetData / GetData メソッドを実装する
wx.CustomDataObject は、SetData(bytes) と GetData() (bytesを返す) という抽象メソッドを持っています(上記コードでは SetObject/GetObject 内で暗黙的に使用)。
私たちは、pickle.dumps(オブジェクト → バイト列)と pickle.loads(バイト列 → オブジェクト)を使って、Pythonオブジェクトとバイト列を相互に変換する処理を実装します。
ステップ4: クリップボードやD&Dで使う
あとは wx.TextDataObject と同じように使うだけです。
# --- 送信側 (コピー) ---
my_data = {"name": "Taro", "age": 30} # 送信したいPythonオブジェクト
data_obj = MyCustomDataObject(my_data)
# wx.TheClipboard.SetData(data_obj)
# --- 受信側 (ペースト) ---
data_obj = MyCustomDataObject()
# wx.TheClipboard.GetData(data_obj)
received_data = data_obj.GetObject() # {"name": "Taro", "age": 30} が復元される注意: pickle はPython専用の仕組みなので、この方法で転送したデータは、同じ MyCustomDataObject の実装を持つ他のwxPythonアプリでしか受け取れません。
まとめ
wx.DataObject は、wxPythonにおけるデータ転送(クリップボードやドラッグ&ドロップ)の「荷物」そのものであることを解説しました。
wx.DataObjectはデータとwx.DataFormat(形式ラベル)をカプセル化します。wx.TextDataObjectやwx.FileDataObjectなど、標準のDataObjectで多くの場合は十分です。- 複数の形式を同時に扱いたい場合は
wx.DataObjectCompositeを使います。 - 独自のPythonオブジェクトを転送したい場合は
wx.CustomDataObjectを継承し、pickleを活用します。
この DataObject の仕組みを理解することで、単なるコピペ機能を超え、アプリケーション間でリッチなデータを連携させる高度な機能を実装できるようになります。


コメント