PythonNetによるXAMLハイブリッドプログラミング


昨日の記事では、PythonNetを利用してPythonからWPFクラスライブラリを呼び出す方法を紹介しました.これは第一歩にすぎない.次に直面する課題は、複雑で変化の激しいUI設計にどのように効果的に対処するかということです.XAML言語はこの問題を解決するために用いられ,インタフェース設計とプログラミングロジックを分割する.しかしXAMLはVisual Studioでは多くのツールで自動的に接続コードを生成します.私たちの純粋な手作りPythonプログラムに対して、XAMLツールをどのように利用することができますか?
実は、WPFクラスライブラリのMarkupネームスペースの下で提供されるXamlReaderクラスは、XAML言語を動的にロードする機能を提供しています.ただし、XamlReader言語でロードされたXAML言語にはイベント属性を含めることはできません.ButtonラベルにClick="xxx"属性を簡単に追加することはできません.
この問題の解決は,実は良好なプログラム構造が要求されている.Visual Studioで生成されたC#プロジェクトでは、WPFウィザードがインタフェース設計のxxx.xamlファイル、インタフェースのイベント応答プログラムファイル(隠しファイルとも呼ばれる)xxx.xaml.cs、自動生成のxxx.g.csファイル、bamlファイルなど、これらを自動的に構成してくれました.コンパイルの過程で、まずxamlファイルをbamlファイル、すなわちバイナリのフォーマットにコンパイルし、空間占有を減らし、それからそれをプログラムの資源にパッケージし、自動的に生成されたxxx.g.csファイルの中で、プログラムはパッケージされた資源からbamlをロードし、それからイベント応答プログラムをインストールする.
これらの自動的なプロセスは、Pythonで作成されると、私たちが手作りするしかありません.この文章では、著者は3つのプログラミングモードを整理しようとした:1)純粋なコード構築;2)XAML動的ロード混合符号化;3)XAMLは、リソースにコンパイルされ、混合符号化される.興味のある方は閲覧してみてください.
はい、これらの考えがあれば、私は簡単なXAMLダイナミックロードの模範プログラムを書き始めました.このプログラムのメインインタフェースはGridPanelのレイアウトコントロールで、上下2つのグリッドに分けて、2つのボタンを置いて、ボタンを押すとダイアログボックスがポップアップします.
インタフェースをデザインするには無料のKaxamlを選びます.
次に、Python MainWindowクラスを定義し、WPF Windowクラスから継承します.
class MainWindow(Window):
  def __init__(self):
    Window.__init__(self)

コンストラクション関数では、まずXamlReaderを使用してXAMLコードを動的にロードし、ルート要素を取得します.このルート要素はPageです.
    # dynamically create page from XAML
    xaml = """
    
        
      
        
        
      
      
      
      
    
    """
    page = XamlReader.Parse(xaml)

次に、ルート要素の2つのグリッドボタンのオブジェクトを取得し、このオブジェクトのメンバー変数に格納し、イベント委任をインストールします.
    # connect Button1
    self.Button1 = LogicalTreeHelper.FindLogicalNode(page, "Button1")
    self.Button1.Click += self.Button1_Click

    # connect Button2
    self.Button2 = LogicalTreeHelper.FindLogicalNode(page, "Button2")
    self.Button2.Click += self.Button2_Click

最後に、メインウィンドウの基本プロパティを設定し、XAMLでロードされたPageオブジェクトをこのフォームのContentに設定します.
    # set main window properties, and install the page
    self.Title = "Python WPF App with XAML!"
    self.Width = 350
    self.Height = 300
    self.Content = page

次のように動作します.
完全なコード:
import clr
clr.AddReference("PresentationFramework.Classic, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
clr.AddReference("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")

from System.Windows import Application
from System.Windows import Window
from System.Windows import MessageBox
from System.Windows import LogicalTreeHelper
from System.Windows.Markup import XamlReader
from System.Threading import Thread
from System.Threading import ApartmentState
from System.Threading import ThreadStart
from System import *

class MainWindow(Window):

  def __init__(self):
    Window.__init__(self)

    # dynamically create page from XAML
    xaml = """
    
        
      
        
        
      
      
      
      
    
    """
    page = XamlReader.Parse(xaml)

    # connect Button1
    self.Button1 = LogicalTreeHelper.FindLogicalNode(page, "Button1")
    self.Button1.Click += self.Button1_Click

    # connect Button2
    self.Button2 = LogicalTreeHelper.FindLogicalNode(page, "Button2")
    self.Button2.Click += self.Button2_Click

    # set main window properties, and install the page
    self.Title = "Python WPF App with XAML!"
    self.Width = 350
    self.Height = 300
    self.Content = page

  def Button1_Click(self, sender, e):
    MessageBox.Show(self.Button1.Content + " is clicked!")

  def Button2_Click(self, sender, e):
    MessageBox.Show(self.Button2.Content + " is clicked!")

def STAMain():
  app = Application()
  app.Run(MainWindow())

def main():
  t = Thread(ThreadStart(STAMain))
  t.ApartmentState = ApartmentState.STA
  t.Start()
  t.Join()

if __name__ == "__main__":
  main()