QPython で GUI プログラミング1（サンプルスクリプトを動かす）

## QPython には QSL4A が搭載されている
QPython には GUI で動く環境がいくつか用意されており、 SL4A をQPython用に改造した QSL4A （プロジェクト化したものが QSL4AApp）なるものがその一つ。
以下の記事だと QuietScripts と表記されているものがそれに該当すると思われるが、初期生成されるサンプルスクリプトは異なる。

https://chayarokurokuro.hatenablog.com/entry/2019/05/18/QPython%E3%81%AB%E3%82%88%E3%82%8BQPython%E3%81%AE%E7%82%BA%E3%81%AEGUI%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA/%E3%82%A4%E3%83%B3%E3%83%97%E3%83%83%E3%83%88%E3%83%80%E3%82%A4%E3%82%A2

この QSL4A や QSL4AApp、検索してもほとんど情報が出てこない。
SL4A などのページも参考に、 QPython での GUI プログラミングを試してみたい。

## サンプルスクリプト
Editor で新規作成ボタン（`+`）を押すと、 File（コンソール用）の他に、 ConsoleApp、WebApp、QSL4AApp、PygameApp を選ぶことができる。
![QSL4AApp_test_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/d70dff22-3248-fb0f-0ea2-7919793bab16.png)
QSL4AApp を選びプロジェクト名を付けると、 project3 フォルダの中にプロジェクト名のフォルダが生成され、サンプルスクリプトとなる main.py が開く。
![QSL4Aapp_test_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/f02aea13-72cd-40e2-9afd-d72ec2d294e9.png)
![QSL4Aapp_test_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/a1bd49d1-eb32-4797-a58a-8bf2e82b0715.png)
![QSL4Aapp_test_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/a98bd8dc-bcb3-1db2-c129-e419b9896d41.png)

このサンプルスクリプトを実行すると、 `Hello, QPython` のメッセージと、下部に `LOAD` ・ `EXIT` の2つのボタンが出てくる。
![QSL4Aapp_test_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/d99254e0-4cbe-aac6-ac4c-ebd3e96e1538.png)

`LOAD` ボタンを押すと、上部にロゴ画像が読み込まれる。
![QSL4Aapp_test_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/9023607a-0e42-0937-8209-1195c96673c2.png)

`EXIT` ボタンを押すと終了してエディタ画面に戻る。

## サンプルスクリプトを各部に分けて見てみる
### 1. 前処理部
```python:
#qpy:quiet
#-*-coding:utf8;-*-
"""
This is a sample project which use SL4A UI Framework,
There is another Sample project: https://github.com/qpython-android/qpy-calcount
"""
import qpy
import androidhelper
try:
    import urllib.request as ur
except:
    import urllib as ur
from qsl4ahelper.fullscreenwrapper2 import *

droid = androidhelper.Android()
```
冒頭でコンソールを出さない設定 `#qpy:quiet` 、モジュールのインポート等が行われている。

### 2. XML で画面レイアウト
```python:
class MainScreen(Layout):
    def __init__(self):
        super(MainScreen,self).__init__(str("""<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:background="#ff0E4200"
	android:orientation="vertical"
	xmlns:android="http://schemas.android.com/apk/res/android">
	<ImageView
		android:id="@+id/logo"
		android:layout_width="fill_parent"
		android:layout_height="0px"
		android:layout_weight="10"
	/>
	<LinearLayout
		android:layout_width="fill_parent"
		android:layout_height="0px"
		android:orientation="horizontal"
		android:layout_weight="20">

		<TextView
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
			android:textSize="8dp"
			android:text="Hello, QPython"
			android:textColor="#ffffffff"
			android:layout_weight="1"
			android:gravity="center"
		/>
    </LinearLayout>

	<ListView
		android:id="@+id/data_list"
		android:layout_width="fill_parent"
		android:layout_height="0px"
		android:layout_weight="55"/>

	<LinearLayout
		android:layout_width="fill_parent"
		android:layout_height="0px"
		android:orientation="horizontal"
		android:layout_weight="8">
		<Button
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
			android:text="Load"
			android:id="@+id/but_load"
			android:textSize="8dp"
			android:background="#ffEFC802"
			android:textColor="#ffffffff"
			android:layout_weight="1"
			android:gravity="center"/>
		<Button
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
			android:text="Exit"
			android:id="@+id/but_exit"
			android:textSize="8dp"
			android:background="#ff06AF00"
			android:textColor="#ffffffff"
			android:layout_weight="1"
			android:gravity="center"/>
	</LinearLayout>
</LinearLayout>
"""),"SL4AApp")
```
どうやら `@+id/` のついた部分は下記の関数等で差し替えたり機能を持たせたりできるようだ。

### 3. 関数定義
```python:
    def on_show(self):
        self.views.but_exit.add_event(click_EventHandler(self.views.but_exit, self.exit))
        self.views.but_load.add_event(click_EventHandler(self.views.but_load, self.load))

        pass

    def on_close(self):
        pass

    def load(self, view, dummy):
        droid = FullScreenWrapper2App.get_android_instance()
        droid.makeToast("Load")

        saved_logo = qpy.tmp+"/qpy.logo"
        ur.urlretrieve("https://www.qpython.org/static/img_logo.png", saved_logo)
        self.views.logo.src = "file://"+saved_logo

    def exit(self, view, dummy):
        droid = FullScreenWrapper2App.get_android_instance()
        droid.makeToast("Exit")
        FullScreenWrapper2App.close_layout()
```
イベントの定義と、イベント発生時に行う機能の定義。

### 4. メイン
```
if __name__ == '__main__':
    FullScreenWrapper2App.initialize(droid)
    FullScreenWrapper2App.show_layout(MainScreen())
    FullScreenWrapper2App.eventloop()
```
初期化して、XML をもとにレイアウトして、イベント発生を待つ…ということのようだ。

## Load ボタンを押すと真ん中の文字が表示されるようにしよう
このサンプルスクリプトでトースト通知と画像の表示の方法はわかったが、文字の差し替え・表示についてはわからない。
サンプルスクリプトが紹介している [qpy-calcount](https://github.com/qpython-android/qpy-calcount) のソースを見てみると、どうやら
```
self.views.((ID名)).text = "((表示させたい文字))"
```
とすればよいようだ。

XML 部分、 Load 部分を以下のように修正してみる（一部抜粋、このほか `datetime` モジュールの呼び出しや文字サイズの適宜修正をした）。
```python:
	<LinearLayout
		android:layout_width="fill_parent"
		android:layout_height="0px"
		android:orientation="horizontal"
		android:layout_weight="10">

		<TextView
			android:id="@+id/time"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
			android:textSize="20dp"
			android:text=""
			android:textColor="#ffffffff"
			android:layout_weight="1"
			android:gravity="right"
		/>
	</LinearLayout>
	<LinearLayout
		android:layout_width="fill_parent"
		android:layout_height="0px"
		android:orientation="horizontal"
		android:layout_weight="20">

		<TextView
			android:id="@+id/title"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
			android:textSize="30dp"
			android:text=""
			android:textColor="#ffffffff"
			android:layout_weight="1"
			android:gravity="center"
		/>
	</LinearLayout>
```
```python:
    def load(self, view, dummy):
        curdatetime = datetime.datetime.now()
        self.views.time.text = curdatetime.time().isoformat()[0:8]
        self.views.title.text = "Load button pressed!"
```
最初は text が空なので表示されず、
![QSL4Aapp_test_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/a9942b78-0f5d-60ae-9aac-78b76c8b842a.png)

Load ボタンを押すと時刻とメッセージが表示される。
![QSL4Aapp_test_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/eee91959-847a-4946-9dc0-5709b3e7aaa6.png)

うまくいった。今回はここまで。

次回はこのスクリプトを発展させ YouTube チャンネル登録者数カウンターにする予定。