PythonistaのUI実装で遊ぶ[Actionの実装]


はじめに

Pythonistaで作ったUIに動きをつけることにけっこう悩んだので記事にしておきます。あんまりPythonistaのUI実装を触らないでここ見てる人は以下を先に読んだほうがいいと思いますよ!
Pythonista3のUI実装で遊ぶ[超超入門]
PythonistaのUI実装で遊ぶ[画面要素]

Action

PythonistaのUI実装では、各要素にActionという属性が設けられていて、たとえばボタンであれば、押下(タップやクリック)されたときの動作を定義することができます。
赤い丸の部分です。

ここに、処理を定義した関数名やメソッド名を指定すると、ボタンが押下されたときに呼び出されるってわけです。
処理は、pyuiに対応するpyファイルに記述します。

ui_sample.py
import ui
import console

def button_clicked(sender):
    console.alert('Button1 clicked!')

v = ui.load_view()
v.present('sheet')

これを、上画のsample_ui.pyuiでButton1を選択し、Actionにbutton_clickedを指定します。

実行してボタン押してみると。。。

ってわけです。ではソースの説明を。

import ui
import console

余談ですが、consoleモジュールはメッセージ出力とかで使えるので憶えるとよいですよ。Pythonistaの組み込みモジュールです。

def button_clicked(sender):
    console.alert('Button1 clicked!')

これが、ボタン押したときに呼び出したい処理です。
Actionに指定する関数やメソッドには、必ず引数senderを指定します。
(pyuiには引数が指定できないため、senderしか指定できません)

senderにはActionの指定元であるオブジェクトが渡されます。この場合はButtonですね。

画面内の要素の連携

上の画像で示したUIですが、ボタンだけでなくテキストフィールドもありますねぇ。
こうなってくると、テキストフィールドに入力した文字列を、ボタン押下したときにプログラムの中で取り出したくなりますよね?なるに決まってます!

ビューに配置した要素というのは、

v = ui.load_view()

したときに、すべてvの中に読み込まれています。
load_view()の戻り値から要素を取り出すには、各要素のNameをディクショナリのキーとして指定します。

s = v['button1'].title
print(s)  # button1のtitle(図の例では「OK」)がprintされます。

ってかんじです。試してみてね。

ちなみに上記の指定方法で、逆に値をセットすることもできます。

v = ui.load_view()

v['button1'].title = 'NG'  # pyuiでボタンに表示するtitleを「OK」に
#                          # 設定したけど「NG」にする

v.present('sheet')         # 値セットはpresentで描画する前

さて、カンのいいガキは気づいたかもしれませんが、メイン処理部分で定義しているvは、senderだけを引数に取るbutton1のAction用の関数に渡すことができません。
が、以下のようにしてsenderからView要素を引っ張ることができます。

def button_clicked(sender):
    v = sender.superview     # superview: sender(button1)の親要素
    console.alert(v['textfield1'].text)

button1textfield1は同じCustomViewに置いているので、superviewを利用しbutton1の親をたどることで取得できます。

Viewをクラス化する

さて、画面要素も配置できた。Actionも実装できる。やってみよう!
ってことで画面作って、要素ぜんぶのActionを実装して...と

import ui

def button1_clicked(sender):
    # 処理1

def button2_clicked(sender):
    # 処理2

def button3_clicked(sender):
    # 処理3

def button4_clicked(sender):
    # 処理4

def button5_clicked(sender):
    # 処理5

v = ui.load_view()
v.present('sheet')

どうです?そろそろView(画面)くらいの単位でクラス化したくなってきたのではないでしょうか?

まぁざっくりたとえばこんなかんじ

import ui

class CustomView1():
    def button1_clicked(sender):
        # 処理1

    def button2_clicked(sender):
        # 処理2
    ...

v = ui.load_view()
v.present('sheet')

pyui側のActionにはクラス名も指定してCustomView1.button1_clickedのように指定します。

おや?クラス内のメソッドなのに引数のselfを指定してませんね。
これ、こう指定しないと動きません。selfと書いても、senderに渡るオブジェクトが入るだけです。selfsenderとか、複数渡すこともできません。

このままではクラス変数とかも使えないので、ほんとに括っただけになってしまいますね。

ところが、以下のように書けば大丈夫です!

import ui

class CustomView1(object):
    def button1_clicked(self, sender):
        # 処理1

    def button2_clicked(self, sender):
        # 処理2
    ...

v = ui.load_view()

cv = CustomView1()
v['button1'].action = cv.button1_clicked

v.present('sheet')

さっき紹介した、ソースから画面要素にアクセスする方法で、メソッドを直接actionに指定しましょう。
actionの指定にpyuiなんて使っていてはいけません。

おわりに

詳しい処理は何一つ触れてませんがだいたいこれで動きができてきましたね!
次は画面遷移について書こうと思います!!