Posted: 2025/03/26   RePosted: 0000/00/00
\\ KayuProg   Storage //

01Plan Bulletin

契機

普段自分のタスク管理は,Google CalenderとiOSのリマインダーを使っています.

その日の大きなイベント(テストだったり,レポート提出だったり)はGoogle Calender,
細かいタスク(その日の課題だったり,買い出し)はリマインダーという使い分けです.

二つアプリを使っているため,予定確認の際にアプリを行き来しなくてはいけないことがあり面倒です.

さらにはいちいちアプリを開いて今日の予定を確認するのも面倒に感じました.

なので当日の予定・タスクを一画面に表示してくれるものを作ります.

Raspberry pi3 model B+を使います.

iOSリマインダー

まず,普段から使い続けているiOSのリマインダーからデータを取得しようとしました.

方法としてpythonのreminder moduleを見つけました.

ラズパイ上でpip installしたら次のエラーが出た.


ERROR: Could not find a version that satisfies the requirement reminders

これは

【解決済み】pipによるパッケージのインストールに失敗する(Could not find a version that satisfies the requirement)

によるとPythonバージョンとの非互換性によるエラーらしい

解決方法としてPyenv(Pythonで作成した仮想環境)によるPython version切り替えの必要があるそうです.まぁこれは大丈夫.

この方法で行くとしたら方法として,まずiPhone上でPythonプログラムを定期的に実行してクラウドにリマインダーのデータをアップロードします.

そのクラウドデータをラズパイに取得させるものです.

面倒ですね.

この方法はなしで.

ではどうするのか.

結局,iOSのリマインダーからGoogle Tasksというリマインダーアプリに移行することにしました.
なぜなら,Google TasksにはAPIがあるからです.

APIを使って直接ラズパイにデータを持ってこさせるということです.

また,Google Calender使っているので同じGoogleのアプリならコード書きやすいのではないかとも考えていました.

さすが天下のGoogle様.

以降にラズパイでデータを取得できるようになった過程,バグ解決を書いていきます.

Python 3.12 install

Google TasksのAPIを使うためにはPython 3.10.7以上が必要みたいです.

せっかくだしもう少し新しいやつを(良くないことが起こりそうですが)installします.

Raspberry Pi に最新の Python3 をインストールする

にしたがって進めていきますます.

Pythonをインストールした場所は/usr/bin/pythonです.(注意!)

さっきのサイトではうまくいかなかったのでこっちのサイトを参照します.

UbuntuのOSシステムのでpythonの使われ方を教えてください。

たまに


Python-3.12.3.tar.xz: Permission denied
Cannot write to ‘Python-3.12.3.tar.xz’ (Permission denied).



というエラーが出る.この場合は実行するコマンドにsudoをつけたらOK.

Pyenvによるversion管理

Raspberry PiにpyenvをインストールしてPythonをバージョン管理

このサイトを参照しながらPyenvによるversion管理を進めていきます.

Pyenvをinstallしたとき,エラーが出てinstallできないときはラズパイのメモリが足りない場合があります.

そういう時は,


sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile


というコマンドでスワップを増やしたら解決できます.

pyenv not commandみたいな感じになったのエラーが出てしまったら,


$ source ~/.bash_profile


を使って読込なおせば行けます.

次に仮想環境を構築してPythonのversionを変更する方法です.

以下のコマンドに従って実行すれば達成できます.
詳しいことはコメントアウト?してあるところを確認ください.


#仮想環境を作りたいフォルダに移動
kayu@raspberrypi:~$ cd /home/kayu/Desktop/test/plan/
#現在のこのpython version確認 
kayu@raspberrypi:~/Desktop/test/plan $ pyenv versions 
    system
 * 3.9.2 (set by /home/kayu/Desktop/test/plan/.python-version)
    3.12.3
#一度この仮想環境にしたいフォルダを使いたいpython versionに変更
kayu@raspberrypi:~/Desktop/test/plan $ pyenv local 3.12.3 
#一応Versionが変わっているのか確認
kayu@raspberrypi:~/Desktop/test/plan $ python -V
Python 3.12.3
#このpython versionが3.12.3の状態で仮想環境構築する
kayu@raspberrypi:~/Desktop/test/plan $ python -m venv .venv 
#仮想環境有効化
kayu@raspberrypi:~/Desktop/test/plan $ . .venv/bin/activate
#仮想環境状態でpython versionが3.12.3になっているか確認  
(.venv) kayu@raspberrypi:~/Desktop/test/plan $ python -V 
Python 3.12.3
#仮想環境状態終了
(.venv) kayu@raspberrypi:~/Desktop/test/plan $ deactivate
#仮想環境をオフにした状態でpythonのversionを戻す. 
kayu@raspberrypi:~/Desktop/test/plan $ pyenv local 3.9.2
#python versionが3.12.3ではなく3.9.2であることを確認. 
kayu@raspberrypi:~/Desktop/test/plan $ pyenv versions 
    system
 * 3.9.2 (set by /home/kayu/Desktop/test/plan/.python-version)
    3.12.3


以上がpyenvの環境構築は終了.

Google Work Spaceでの準備

ここでGoogle Cloudでprojectを作成する.

Google Cloud プロジェクトを作成する

ここを参考に進めていく.まずはTasksの取得から.

進めていく中で


Google の審査プロセスを完了していません。このアプリは現在テスト中で、デベロッパーに承認されたテスターのみがアクセスできます。アクセス権があると思われる場合は、デベロッパーにお問い合わせください。


というエラーが出た.

なんでや....

<解決方法>
Google Cloudの確認ステータスで「検証は不要です」となるようにスコープ?権限を調整(全てオフにした)したら行けた.

やった!取得できた!

APIキー、OAuthクライアントID、サービスアカウントキーの違い:Google APIs

ここがわかりやすかった.APIキー,クライアントIDなどの解説がしてある

google apiはここのサイトを最初参照した.


pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib


これがインストールすべきライブラリたち

取得した後

取得した内容はこんな感じで返ってくる.


    [{'kind': 'tasks#task',
    'id': 'ZlBHdmtHbUM4R2l0X29sSA',
    'etag': '"LTU5MDgzMzAzNQ"',
    'title': 'タスク1',
    'updated': '2024-10-30T09:36:27.000Z',
    'selfLink': 'https://www.googleapis.com/tasks/v1/lists/MTQzMjUyMTY0MTk3NDc0NDg1ODU6MDow/tasks/ZlBHdmtHbUM4R2l0X29sSA', 
    'position': '00000000000000000000', 
    'status': 'needsAction', 
    'due': '2024-10-31T00:00:00.000Z', 
    'links': [], 
    'webViewLink': 'https://tasks.google.com/task/fPGvkGmC8Git_olH?sa=6'}]


dueがこのタスクの期日になる.
RFC3339 timestampというのが使われていた.

Tasks API . tasks Instance Methods

に書いてありました.

" "due": "A String", # Due date of the task (as a RFC 3339 timestamp). Optional. The due date only records date information; the time portion of the timestamp is discarded when setting the due date. It isn't possible to read or write the time that a task is due via the API." 'due': '2024-10-31T00:00:00.000Z'

上述にある通り,日付だけしかAPIで引っ張れないらしい.

う~ん.

時差計算

同様に似たGoogle calender用のページでGoogle Calenderからも情報をとれるようにしました.

Python クイックスタート

ここら辺のサイトを参照した.

\\ KayuProg   Storage //

02表示方法

ラズパイかな?・・・・

次にどのようにディスプレイに表示させるかを考えた.

1.ラズパイ上でWebサーバを構築してWeb上で表示させる.

2.デスクトップアプリを作ってその上に表示させる.

Webサーバ構築のほうは時間がかかる上にWebを定期的に自動更新させる必要が出てくる.

HTMLをpythonで編集していく形になると思うが内容を随時更新されるようにできるだろうか.

うーん.デスクトップアプリ作ったほうが早そうだし,簡単そう.

ということでpythonでデスクトップアプリ作ります

ただ文字列を表示させて定期的に更新するだけだからpythonでデスクトップアプリ作っても問題ないだろう.

リアルタイム性が重要でもないしな.

最終的にiPadに文字を表示するにあたってデスクトップアプリを作成することにした.

そこで,以前使ったことのあるTkinterを使おうとしたが,UIの作成にかなり苦労したことを思い出した.

そこでpythonで作るデスクトップアプリについて調べていたら次のようなものを見つけた.

Pythonを使ったGUIアプリを「お手軽に」作りたかった件

このサイトで見つけたFletです.

Pythonだけでクロスプラットフォームなアプリを作れるFletについて

ここら辺の記事を軽く読んだだけでもこれはやばい.すごそう.

すごい簡単にデスクトップアプリへのビルド,Webアプリとしてデプロイもできるらしい.

ほんとすごい.

Webと同じ感覚でflexbox的に要素配置できるのも素晴らしい.

いうことありません.これを使おう.

ドキュメントはこちら Flet Getting started

ドキュメントでは仮想環境でのapp製作を進められているのでpyenvの仮想環境で進めていきます.

注意!WSL or Linux環境でのinstallはいろいろ操作が必要みたいだからドキュメントちゃんと読んだ方がいい!

ちなみにAudio使ったflet appのbuildには--include-packages flet_audioというコマンドが必要らしい.

flet build apk --include-packages flet_audioこんな感じらしい.

videoを使う場合も追加でinstall必要みたい.(ドキュメント自分で読んでね)

Fletでデスクトップアプリを作る

フレットでデスクトップアプリを作る環境構築で次のエラーたちが出た.

1.共有ライブラリない問題


error while loading shared libraries: libmpv.so.1: cannot open shared object file: No such file or directory


共有ライブラリが見つからないらしい

Ubuntu22.04にFletをインストールした話

結局このサイトで行けそう(行けた).

2.サーバがうまく動作しない問題

またエラー出た.(いつも通り)


Unable to init server: Could not connect: Connection refused


これは単に自分の場合はラズパイのデスクトップを表示していないことが問題だった.

ラズパイをずっとからvscodeでssh接続で使っていたが,ラズパイをディスプレイに接続すると解消した

flet runをしたい場合はVNC Viewerなどでラズパイのデスクトップを直接表示させて実行する必要ることがわかった.

3.謎

そろそろ行けるかな?

しかし,またエラー


atk_socket_embed: assertion 'plug_id != NULL' failed


うーん.調べてもよくわからなかった.

そこでとりあえず一度ラズパイを再起動した.



え?

急にちゃんと表示された.

terminalには似たようなエラー. 何が起こっている...

わからない.怖い

まあ,動いたしいっか(後で苦労しそう)

次にbashファイルから呼び出せるか試そう.

適当なbashファイルに


# 実行権限を付与。これでダブルクリックした際に「端末で実行」が出てくる
cd /home/kayu/Desktop/plan/
. .venv/bin/activate

cd test/test_flet
flet run

# cd test
#python test/a.py


と記述して実行してもちゃんと開きました.

うーんできてる.

やっぱreboot大事だな.

うん.

沼ったらとりあえずrebootしよう(適当)

作りたいけどデプロイどうする問題

すごい.文法がすごく簡単.

わかりやすい.直感的で扱いやすい(おまじないが少ないということ)

初めて使うためあまり勝手がわからないので試しにドキュメントにあるチュートリアルのto-do appを作ってみる.

Tutorial Todos

これだけでもかなりクオリティの高いものが作れる.

ほんとすごいな.なんだったんだTkinterは

ある程度容量はつかんだので実際に作る.

チュートリアル英語だし,プログラムの写経疲れた...

とりあえず一度アプリとして出力してみたいと思う.

どうなるかな..

え,,アプリとしてビルドするには普通にflutterのコンパイルツール入れなきゃいけないの...

なにそれ,知らない...



求められるハードウェアの性能がこんな感じ.

うーん.ラズパイ上で開発するべきじゃないなこれ.

Free disk space が60Gくらいほしいのか.(残り30GB)

厳しくないか...

え...

どうしよう

もう他のパソコンでビルドするしかないよな

Publishing Flet app to multiple platforms

この画像を見るとわかるが,私の持っているのはwindowsのみでbuildはwindowsデバイスようにしかできない.

そうすると,buildしてラズパイにアプリ移すならwebアプリしかない..

画像はここから持ってきました.

ここに乗ってる一番左のapk/aabはアンドロイド関係のやつ見たい.

WSL使ってLinux環境にビルドできるみたいだけどLinux系のアプリとかビルドってすごいversionにうるさいイメージがある.

以前にLinux使っててversion関係で沼った上に諦めなくてはいけなかった記憶がある.

やっぱりwebアプリとしてビルドするしかなさそう.

そうすると,どうなるのだろう.

flutterでwebアプリを作る原理?みたいなのを調べてみるとDart(プログラミング言語)をJavascriptに変換しているらしい.

今回使っているfletで使用しているのはPython.

その上にGoogle APIを使うためにライブラリを何個もインストールしている.

これビルドできるのか?

もうとりあえずやるしかなさそう.

ラズパイ上でのビルドはできないことはほぼ確定しているからまず,Windows上にfletを扱うための環境構築をする.

頑張ろう....





頑張った.webアプリとして動的なものとして扱ってみる.

Flet Hosting Flet web apps

ドキュメントに書いてある説明を読んでFly.ioを使うことにした.

Replitに関してはweb IDEで少しやりたいことと違う.
Self HostingはUbuntuとかLinux環境じゃないと厳しそう.
ということで聞いたこともないFly.ioを使います.

無理そうだったらReplit使おうかな.

Docs Installl fly ctl

ここ見てwindowにインストール.

powershellで実行.cmdじゃないよ

fly.ioのサイトに従ってアプリをデプロイしようとしたが,次のエラーが表示された.


Error: We need your payment information to continue! Add a credit card or buy credit: https://fly.io/dashboard/USER_NAME/billing


fly.ioを使うには料金が発生した時のためにクレジットカードを追加しなくてはいけないらしい.

嫌だな.う~ん.

Replitにしようかな.

少し面倒すぎる.

ということで一度fly.ioは諦めてReplitでデプロイをしてみようと思います.

割とすぐできました.



問題としては,スクリプトを実行した際に新しくWindowが表示されるが,そこでは何らかの原因でAppが表示されない.

表示するためには直接Replのウィンドウから開く必要がある

これは開けても開発用デバッグ用らしく,Replのセッションを切断するとウィンドウも使えなくなってしまう.

有料のものを使えばセッション切断後も使えるらしいが有料は嫌です.

デプロイ方法はこの開発用ウィンドウにしようかな.

考えに考えた結果,buildしないでflet runで表示されるデバック用画面?を直接使うことにしました.

これだと,ラズパイ上でfletが動く環境だけ作って,外部で開発したコードをもってくるだけでいい.

これで行きます.

共有方法はGit hubかな.

変更がある度にGit pullすれば大丈夫でしょう.

Githubへのpushの注意点

製作中のスクリプトファイルをgit hubにpushしようとしたらGoogle Aouthの気密ファイル(トークン情報)含んでいるってことではじかれた.

知らなかった.

機密ファイルをpush使用とした私も馬鹿だが,そんな馬鹿でも大丈夫なような仕組みがあるらしい

機密ファイルはちゃんとpushしないよう.gitignoreに設定した.

\\ KayuProg   Storage //

時刻フォーマットについて

基本的に時刻についてのやり取りはisoフォーマットで行われている.

日付をICT標準から日本時刻に直し,isoフォーマットに変換する方法は次のコード


#datetime.datetimeで取得できる時間はICT標準だから日本時間にオフセット
now_time=datetime.datetime.utcnow()+datetime.timedelta(hours=9)
#strの後ろに"+09:00"をつけることで日本時刻であることを表す.
now= (now_time + datetime.timedelta(days=0)).replace(hour=0, minute=0, second=0).isoformat()+"+09:00"
next_day= (now_time + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0).isoformat()+"+09:00"


isoフォーマットから時刻のみを取得したいときは以下


event_time=datetime.fromisoformat(event["date"]).strftime("%H:%M")


Calenderの予定の背景色の取得

カレンダー内の予定の色を取得したいとき


#背景カラーの辞書配列を取得する.
colors = service.colors().get().execute()
#イベントの情報取得
events = events_result.get("items", [])
#enents内の各予定のcolorIdを取得する.
#"1"は色が指定されていない場合に1を入れるということ.
color=event.get("colorId","1")
#別にこっちでもcoloIdの取得はできる.
color=event["colorId"]


画面の更新方法

Calenderでの予定変更追加に際して画面を更新する必要がある.

textに関しては.valueで変更ができそうだが,contentのdescription追加などが難しそう.


self.plans=ft.Column(controls=[],expand=True)


という風にもう一回初期化して行けるかと思ったけどこれはダメだった.
さんざんいろいろ試して以下のように処理することにした.

まず,


self.plans.controls.clear()


self.plansのcontrolを一度すべて消す

次にもう一度CalenderとTasksの情報を取得しなおして

self.plans.controls.append()


を使ってもう一回self.plansにcontrolsを登録しなおす

これでできた!Happy!!.

Tasks画面制作


#google.auth.exceptions.RefreshError: ('invalid_scope: Bad Request', {'error': 'invalid_scope', 'error_description': 'Bad Request'})


というエラーが出て実行できないとき

スコープが正しいか確認しトークンを再発行することで解決した.

例)SCOPES = ["https://www.googleapis.com/auth/tasks"]
例)SCOPES = ["https://www.googleapis.com/auth/tasks.readonly"]

Tasks側の自動更新について

Calender側と同様に画面の自動更新プログラムを作成したら以下のエラーが出た.


[SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:2580)


つまり,Google APIを通じて変更を加えようとしたときにエラーが出たということ.

ssl.SSLError: [SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:2548)

参考になりそうなサイトを見つけたがPythonのversionが異なったりしていて微妙.

結局うまくいく方法見つからなかった.だから,Google APIにアクセスしてエラーが出たらリトライする仕組みにしようかな.

画面の自動updateとスライドスイッチの処理が競合したときにこのエラーになるっぽい.

だから,calenderの方だけ自動的にupdateされるようにしてtasksはrefleshボタンを設置して手動にすることに一旦する.

(結局どうしたかは後述.ちなみに自動更新するようにできました.)

ディスプレイの切り替え

既に終了したタスク,残っているタスクを表示する画面を別々にしたい

そこで,画面を遷移するためのボタンを作ることにした.

プログラムとしては,

・self.display_flag==1の時にはremain tasksが表示されている.
・self.display_flat==0の時にはcompleted tasksが表示されている.

というふうに画面のインスタンス作成時にdisplayの状態を記録する変数を初期化しておく.

関数内では


def change_display(self,e):
    #contentsの消去
    self.tasks_list.controls.clear()
    if self.display_flag==1:
        self.tasks_list.controls.append(self.completed)
        self.display_flag=0
    elif self.display_flag==0:
        self.tasks_list.controls.append(self.remain)
        self.display_flag=1
    #画面の更新
    self.page.update()


というふうにself.display_flagの値によってappendするcontrolsを変更している.

ただし,self.pageはインスタンス作成時に引数としてmain()で作成したft.pageを持ってきている

Tasks側の機能について

Tasks側の画面の構成を考えていた.

表示画面からtaks完了のstatus変更ができたら楽だろうなと...

ということでいろいろ調べた.

Tasks API . tasks Instance Method

に"status": "A String", #Status of the task. This is either "needsAction" or "completed".

という記述があった.できそう.よし.
ここをいじればいいらしい

for文を回していて値を保持しておいてほしい

状況としては,まずTasksをstatus:completedとするためのスライドボタンの作成はfor文を使って各タスクに対して作成している

そのボタンを作成すると同時に,スライドボタンを押した際に実行する関数を決定する.

この関数の処理にはfor文を回してボタンを作成した際に使ったtask IDなどの情報が必要になる

しかし,for文が回りきっていると関数が参照するtask IDはfor文の最後の処理の値となってしまい,該当するボタンの情報と異なる.

そこで,情報を保持したままfor文を回してやりたい.

そんな時はデフォルト引数を使うといい.

ボタン作成の関数実行部分に


on_change=lambda e, tsid=tasklist_id, tid=task_id, ts=status:tasks.change_status(tsid, tid, ts,e.control.value)


と記述することでデフォルト関数を使い,値(task ID)を保持することができた.

スライドボタンを押してtaskをcompletedに変更するにあたって以下を使った.


service.tasks().update(
    tasklist=tasklistid,
    task=task_id,
    body={"status": "completed"}  # ステータスを "completed" に設定
    ).execute()


しかし,このようにAPIから変更をしようとしたときに


HttpError 400 when requesting "Something URL" returned "Missing task ID". Details: "[{'message': 'Missing task ID', 'domain': 'global', 'reason': 'invalid'}]"


というエラーが返される.

解決にはupdateではなくpatchを使う

なぜかは不明.HTTP通信の何かがupdateだとおかしいらしい.

なのでコードは

service.tasks().patch(
    tasklist=tasklistid,
    task=task_id,
    body={"status": "completed"}  # ステータスを "completed" に設定
    ).execute()


に変更した.

Tasks API . tasks Instance Method

詳しい使い方はこのサイトを参照して!

次の問題が,どのようにしてtasksのstatus変化後に画面を変化させるか.

単純に画面をpage.update()で更新するだけでは実現できない

ここでCalenderでの画面更新を応用してみた.

まず,self.tasks_list_create()でcontents作成する際に


self.remain.controls.clear()
self.completed.controls.clear()


を追加してcontrolの中身を全て空にする
それからもう一度中身を作成させる.

ここまではCalenderの時と同じ

問題になるのがスライドスイッチを変化させてすぐにGoogle APIに問い合わせてもまだ,Google TasksにはStatusの変化が反映されないことだ.

これは再度controlsの中身を作成しなおすまでに少し時間をおくことで対応する.

これが原因でTasks側のスライドボタンを押してから変化するまでに時間を要してしまう.

これはAPIを使っている関係上しょうがない.諦めよう

今のところ2秒くらいかな.

スライドスイッチを変化させたときの関数は次の感じ

def task_complete(self,tid,ts,val):
    tasks.change_status(tid, ts,val)
    #ここにtasksの表示をupdateする関数の実行を書く.
    time.sleep(2)#status変化が反映されるまで待つ.
    self.tasks_list_create()
    self.page.update()
    return 0


ここら辺の関係でstatus変化の処理が終わる前にスライドスイッチを再度押しちゃうとバグってfletが落ちてしまう.

だから,一つ一つ処理が終わるまで待つ必要がある.

非同期処理を使うといい感じになるかも,と考え一度やってみる

その並列処理待った! 「Python 並列処理」でググったあなたに捧ぐasync, threading, multiprocessingのざっくりとした説明

Pythonで非同期処理をするときのasyncioの使い方

ここら辺使いながら非同期処理の勉強した.

実際に実装したが
非同期処理のbtnのon_changeが反応しなくなってしまった.

詳しくいろいろ調べてたけどわからず,結論としてGPTにきいたところfletでのスライドスイッチのイベントループと競合しているのではないかとのこと.


btn= ft.Switch(value=True,on_change=lambda e,tid=task_id, ts=status:self.task_complete(tid, ts,e.control.value))


という関数でon_changeの関数が非同期だと実行されない問題が発生した.

on_change自体は同期的なコールバック関数らしく非同期処理をすることがでいない?らしい.

諦めます.他の並列処理(threading,multiprocessing)も試そうと思えばできるけど疲れました...

さらに,非同期処理をしたからといって余り処理速度が今回は向上しなかったためこのまま行きます.



無念.....



解決方法として,ボタンを押して処理をしている最中は他のボタンを使えないようにしました

こうすれば処理が終わる前にボタンが押される心配はありませんからね!
さらにこの解決策でTasksを自動的に更新するプログラムも正常に動作させることができました.

tasksを更新しているときはswicthを使えないようにして,更新が終わったときにswitchを使えるようにすればいいという事です.

#スイッチのインスタンスまでアクセスする.
sw=tasks.remain.controls[0].content.controls[-1]
#.disabledのTrue,Falseを切り替えることでボタンの使用の可否を決定する.
sw.disabled= True or False


これでTasks側のrefrshスイッチが必要なくなった.

関数は次のように設定した.


def switch_disabled():
    tasks_num=len(tasks.remain.controls)
    for i in range(tasks_num):
        tasks.remain.controls[i].content.controls[-1].disabled=True
    tasks_num=len(tasks.completed.controls)
    for i in range(tasks_num):
        tasks.completed.controls[i].content.controls[-1].disabled=True

def switch_abled():
    tasks_num=len(tasks.remain.controls)
    for i in range(tasks_num):
        tasks.remain.controls[i].content.controls[-1].disabled=False
    tasks_num=len(tasks.completed.controls)
    for i in range(tasks_num):
        tasks.completed.controls[i].content.controls[-1].disabled=False


また,Tasks側の画面の自動更新は次のように設定した


if timer>300:
    #tasksのスイッチをdisabledにする.
    switch_disabled()
    page.update()
    #ここからがTaskの方を自動更新するプログラム.
    tasks.tasks_list_create()#これを実行するとtasksがupdateされる.
    #tasksのスイッチをabledにする.
    switch_abled()


一応スライドスイッチのable,disableを切り替える関数も載せておきます


def switch_diabled(self):
    tasks_num=len(self.remain.controls)
    for i in range(tasks_num):
        self.remain.controls[i].content.controls[-1].disabled=True
        tasks_num=len(self.completed.controls)
    for i in range(tasks_num):
        self.completed.controls[i].content.controls[-1].disabled=True
    #change buttonも使えないようにする.
    self.change_button.content.disabled=True
        
def switch_abled(self):
    tasks_num=len(self.remain.controls)
    for i in range(tasks_num):
        self.remain.controls[i].content.controls[-1].disabled=False
        tasks_num=len(self.completed.controls)
    for i in range(tasks_num):
        self.completed.controls[i].content.controls[-1].disabled=False
    self.change_button.content.disabled=False


\\ KayuProg   Storage //

04ラズパイに実装

文字化けしとるやん

いままではwindowsでfletアプリを作成した.
なのでラズパイに実装してみる

ラズパイに実装したところ次の画像みたいに文字化けしてしまった.



特にエンコード,デコードには問題ないみたい.

おそらく,ラズパイに日本語対応したフォントがないためにこうなっていそう.

そこで

Raspberry Pi : IPA 日本語フォントの導入

このサイトにある

$ sudo apt install fonts-ipaexfontを実行して日本語のフォントを入れた.

ダメでした.

次はflet自体のassetに日本語対応フォントを入れて使ってみる.
つまり,自分でfletアプリのフォントを指定するということ

フォントは

LINE seed

からダウンロードした(このサイトはかなり重いので開かないほうがいいです.)

Installした中にあるLINESeedJP_TTF_Bdを使う.ちなみにDesktop用のやつです.

これをmain.pyのある階層でassets/LINESeedJP_TTF_Bd.ttfとなるように配置する.

最後にfletのpageを作成するところに以下を追記

    
    page.fonts={"Go_bd:"./assets/LINESeedJP_TTF_Bd.ttf}
    page.theme=ft.Theme(font_family="Go_bd")
    
    

これでpageに日本語フォントが適用されたことになる!!

解決しました.

この指定方法は
Flet documents -Fonts-
を参照した.

ラズパイデスクトップからpythonスクリプト実行

ラズパイ起動と同時にこのアプリが立ち上がってほしい

なのでデスクトップにシェルスクリプトを作成する.

内容は次のように編集

    
    # 実行権限を付与。これでダブルクリックした際に「端末で実行」が出てくる
    cd /home/kayu/Desktop/plan/#動作させたいフォルダまで移動
    chmod +x ./test/a.py#実行権限の付与
    python test/a.py#pythonを実行.
    
    

注意しなくてはいけないのが実行したいpythonスクリプト内で相対パスなどをしている場合,次の内容をpythonスクリプト内に書く必要がある

        
        import os.path
        os.chdir('/home/kayu/Desktop/plan/')#パスは実行したいスクリプトのあるフォルダ
        
        

これめっちゃ大事!このせいで沼りかけました.

.shファイルを作成した後は
    
    chmod 755 testscript.sh
    
    

として実行権限を付与しないとシェルスクリプトを実行できません

これがわからず私は半日使いました.

仮想環境入れない問題

シェルスクリプト内で

. .venv/bin/activate

を永遠に実行していたが仮想環境に入れないという問題にあたった.

サイトには bash file name としろだとかいろいろあったけど,
そもそも .venv/bin/activate に実行権限が付与されていなかった

    
    chmod +x .venv/bin/activate
    source .venv/bin/activate    
    
    

さっきと同じ実行権限問題.つらい....

ここら辺を調べていて学んだのが,

sourceを使うと同じシェルスクリプト上で実行させることができる.

ということ.

これが必要だった.

sourceを使わないとpython実行したときに新しくターミナルが作成される.
これは,venvをターミナルでactivateしていても別のターミナルには反映されないからmain.pyを実行したときに

flet not found

になっていた,なるほどね.
いい勉強になった.

最終的なシェルスクリプト

    
    #!/bin/bash#ちなみにここが1行目出ないと実行しない.
    cd /home/kayu/Desktop/plan/
    chmod +x ./main.py
    
    source .venv/bin/activate
    
    sleep 1
    
    python ./main.py
    
    

ラズパイ起動時に実行

まず,外出前に傘が必要か喋るシステム
でも使った~/.profileに追記するという方法でやってみた.

    
    #source /home/kayu/Desktop/plan_bulletin.sh
    
    

ログファイルを作成してわかったが,スクリプトが実行されてもfletが立ち上がらなくなっていた.

なぜ...........

ほんと実装までが長いよーーーーーーー

とりあえず今度はcron使ってみる

Raspberry Piでcronを使ってPythonを定期実行する

を参照した. 失敗..........

~/.profileに追記したときと同じ挙動であった.

私はこのアプリの自動実行を諦めました.

結局手動で立ち上げるのですが,なぜかpulan bulletin.shを実行しfletを起動するにはわざわざラズパイを一度ディスプレイに接続しなくてはいけないという,めんどくさいことになりました

まぁ,これでもいいか.

VNC viewerのTimeoutする問題

このアプリを部屋でずっと表示しておくディスプレイとしてタブレットを使うことにしました.

VNC viewerを使って接続し,ラズパイのデスクトップを常に表示しておこうということです

しかし,VNC viewerはある程度時間が経つとTimeoutしてラズパイとの接続を切ってしまいます.

調べていると仕方がない,という記述を多数見たのですが,そんなことはありませんでした.

VNCの接続を切らないでいるための方法は次です

ラズパイ側のvncviewerを右クリック,options,epert,にて
idletimeoutの値を0にする.



こうするとVNC接続を切れないようにできました!!

タブレットの設置

タブレットを部屋に設置するにあたって問題があった.

設置場所はベッドの横に吊り下げる形にする.



こんな感じ

しかし,これだと画面が少し下を向いていて画面を見づらい.

なので画面の角度をいい感じにするためのホルダーを作成する.

いつも通りCADで設計.



で,印刷.



設置しました.



タブレットの画面の角度変化はこんな感じ.



ちょうどよい.

まとめ

今回は単純に予定を知りたいときにいちいち携帯を開きたくないという理由から,こんなアプリを作った.

大学の長期休みでの制作としていいものができたと思う.

このおかげでアプリを作るにあたってfletというライブライを知れたし,APIをうまく使うともっといいものが作れそうということも感じた.

次の動画はAPIを経由してGoogle Tasksアプリとの連携確認をしているところです.

やっぱりプログラミング使って何かを作るのは楽しいですね!!!!!!

しつこいぐらいのエラーに沼りながら改善して,目的の動作したときの達成感がすごい.



コードはこちらのGit hubに挙げています.

https://github.com/KayuProg/plan_bulletin_raspi

上述を参考にしながら進めればこのプログラムで動くと思います

では.

Posted: 2002/09/10   RePosted: 0000/00/00
\\ KayuProg   Storage //

05TITLE

head