PysimpleGuiでTrelloを操作する簡易GUIアプリを作ってみました。
完成系のイメージです。
ここからボードを選んで、ボード内のリストを選択して、カード内容を出力する、というような形です。
このように出力されます。
まずはTrelloを操作するTrelloClient
クラスを作成します。
このクラスで、リストの名前を出力したり、カードの名前を出力したりするイメージです。
そしてTrelloClient
を継承したTrelloGui
クラスを作り、実際の画面としての機能を実装させる、というような流れです。
TrelloをPythonから操作するにはAPI KeyとAPI SecretとAPI TokenとUser IDが必要です。
Trelloにログイン後、下記のリンクから取得できます。
早速コードを書いていきましょう。
クラスは一緒のファイルにしてもいいのですが、コードが長いので、モジュールを分けることにします。
#Trello.py
import requests
from datetime import datetime
from datetime import timedelta
class TrelloClient:
def __init__(self, user_id, key, secret, token):
self.user_id = user_id
self.key = key
self.secret = secret
self.token = token
self.URL = 'https://trello.com/1/'
def get_board_id(self, board_name):
"""
ボード名からボードidを特定する
"""
end_point = f"members/{self.user_id}/boards?key={self.key}&token={self.token}&fields=name"
json_data = requests.get(self.URL + end_point).json()
for json in json_data:
if json['name'] == board_name:
return json['id']
def get_board_names(self):
"""
ボード名のリストを返す
"""
end_point = f"members/{self.user_id}/boards?key={self.key}&token={self.token}&fields=name"
json_data = requests.get(self.URL + end_point).json()
return [json['name'] for json in json_data]
def get_list_id(self, board_id, list_name):
"""
ボードidとTrelloリスト名からTrelloリストidを特定して返す
"""
end_point = f"boards/{board_id}/lists?key={self.key}&token={self.token}&fields=name"
json_data = requests.get(self.URL + end_point).json()
for json in json_data:
if json['name'] == list_name:
return json['id']
def get_list_ids_and_names(self, board_id):
"""
idとnameがタプルになったリストを返す
"""
end_point = f"boards/{board_id}/lists?key={self.key}&token={self.token}&fields=name"
json_data = requests.get(self.URL + end_point).json()
return [(json['id'], json['name']) for json in json_data]
def add_task(self, list_id, card_name, due_date=None, due_time=None, desc=None):
"""
カードを特定のリストに追加する
"""
end_point = "cards"
if due_date and due_time:
due = datetime.strptime(due_date + ' ' + due_time, '%Y/%m/%d %H:%M')
# そのまま登録すると13時間後になる仕様のため
due = due - timedelta(hours=13)
due = due.isoformat()
else:
due = ""
query = {
'key': self.key,
'token': self.token,
'idList': list_id,
'name': card_name,
'desc': desc,
'due': due}
requests.request("POST", self.URL + end_point, params=query)
def get_cards_in_list(self, list_id):
"""
Trelloリストの中のカードをjson形式で返す
"""
end_point = f"lists/{list_id}/cards"
query = {
'key': self.key,
'token': self.token
}
response = requests.request(
"GET",
self.URL + end_point,
params=query
)
return response.json()
このファイルの中で各メソッドを起動して動作を確認してみましょう。
def job():
user_id = 'user...'
key = 'your api key'
secret = 'your secret'
token = 'your token'
client = TrelloClient(user_id, key, secret, token)
# ボード名の取得
print(client.get_board_names())
>>>
['プレイリスト作成', '好きなアルバム']
プレイリスト作成
と好きなアルバム
という二つのTrelloボードが存在しています。
プレイリスト作成
というボード名を指定することでボードIDが取得できます。
board_id = client.get_board_id(board_name='プレイリスト作成')
print(board_id)
>>>
90b7692hoge795fuga71476a
このボードのリスト名を確認するにはこうします。
list_id_and_names=client.get_list_ids_and_names(board_id)
print(list_id_and_names)
>>>
[('90b7692hoge795fuga71476a', 'しばたさとこ島'), ('90b7692hoge795fuga71476a', 'いじわる全集'),
('90b7692hoge795fuga71476a', '愛の休日'), ('90b7692hoge795fuga71476a', 'プレイリスト')]
ボード名からも推測された通り、お気に入りのプレイリストを作るためのものでした。
せっかくなのでプレイリスト
に入れられているカードを確認してみましょう。
このようにします。
list_id = client.get_list_id(board_id=board_id, list_name="プレイリスト")
cards_json = client.get_cards_in_list(list_id=list_id)
for json in cards_json:
print(json['name'])
>>>
あなたはあなた
スプライト・フォー・ユー
後悔
芝の青さ
いのちがtoo short!!
遊んで暮らして
コーポオリンピア
ときだより
良い感じですね。
私が好きな柴田聡子さんのプレイリストを整理するためのボードだったということです。
次にPySimpleGuiを使ってGUIアプリを作成していきましょう。
紹介したTrelloClient
クラスはそれなりに使えますが、色々と問題があります。
例えば今回は特定のアーティストのプレイリストを出力しましたが、別のアーティストのプレイリストを作りたくなるかもしれません。
そうした場合に、出力するためにソースコードの中のボード名やリスト名をその都度書き換えるのは面倒です。
このあたりを動的にするためにTrelloClient
を継承したTrelloGui
クラスを作ります。
# gui.py
import PySimpleGUI as sg
from Trello import TrelloClient
class TrelloGui(TrelloClient):
def __init__(self, user_id, key, secret, token):
super().__init__(user_id=user_id, key=key, secret=secret, token=token)
# ボード名をインスタンス変数に格納
self.board_names = super().get_board_names()
# windowのインスタンス化
self.window = sg.Window('for Trello...', size=(560, 560), finalize=True).Layout(self.layout())
# GUI操作に応じてセットされる変数
self._trello_board_id = None
self._trello_list_id = None
self._trello_list_names = None
self._trello_list_ids = None
def set_trello_board_id(self, board_id):
self._trello_board_id = board_id
def set_trello_list_id(self, list_id):
self._trello_list_id = list_id
def set_trello_list_names(self, ids_and_names):
self._trello_list_names = [name for list_id, name in ids_and_names]
def set_trello_list_ids(self, ids_and_names
):
self._trello_list_ids = [list_id for list_id, name in ids_and_names]
def layout(self):
"""レイアウトを定義します"""
# カード追加のフレーム
col1 = [[sg.T('due date', size=(10, 1))],
[sg.InputText('', size=(10, 1), key="DUE_DATE")]]
col2 = [[sg.T('time', size=(5, 1))],
[sg.InputText('', size=(5, 1), key="DUE_TIME")]]
frame_add_card = sg.Frame('Add Card', [
[sg.T('Card Name')],
[sg.MLine('', size=(35, 2), key="CARD_NAME")],
[sg.T('Description')],
[sg.MLine('', size=(35, 2), key="DESC")],
[sg.Column(col1), sg.Column(col2)],
[sg.Submit('ADD', key="ADD_CARD")]
])
# カード出力のフレーム
frame_print_card = sg.Frame('Print Card', [
[sg.Button('All Card in Board', key='ALL_LIST_PRINT'),
sg.Button('Just Selected List', key='SELECTED_LIST_PRINT')],
[sg.MLine('', size=(35, 20), key='PREVIEW', enable_events=True)]
])
return [
[sg.T('Trello GUI',
size=(30, 1),
justification='center',
font=("Helvetica", 20),
relief=sg.RELIEF_RIDGE)],
[sg.T('Choice Board')],
[sg.Combo(values=self.board_names, size=(20, 1), key='BOARD_NAME', enable_events=True)],
[sg.T('Choice List')],
[sg.Combo(values=[''], size=(20, 1), key='LIST_NAME', enable_events=True)],
[frame_add_card, frame_print_card]
]
def event_loop(self):
while True:
event, values = self.window.read()
if event is None:
print('exit')
break
# 選択されたボード名に応じて、Trelloリスト名コンボボックスの更新、
# インスタンス変数にTrelloリスト情報をセットします。
if event == 'BOARD_NAME':
board_name = values['BOARD_NAME']
board_id = super().get_board_id(board_name)
self.set_trello_board_id(board_id)
id_and_name_of_trello_list = super().get_list_ids_and_names(board_id)
self.set_trello_list_names(id_and_name_of_trello_list)
self.set_trello_list_ids(id_and_name_of_trello_list)
self.window.FindElement('LIST_NAME').Update(values=self._trello_list_names)
# Trelloリスト名が選択されたら、Trelloリストidをセットします
if event == 'LIST_NAME':
list_name = values['LIST_NAME']
list_id = super().get_list_id(self._trello_board_id, list_name)
self.set_trello_list_id(list_id)
# カードを追加します
if event == 'ADD_CARD':
card_name = values['CARD_NAME']
desc = values['DESC']
due_date = values['DUE_DATE']
due_time = values['DUE_TIME']
super().add_task(self._trello_list_id, card_name, due_date, due_time, desc)
# 選択されているボードの全カードを出力します
if event == 'ALL_LIST_PRINT':
preview_text = ''
for list_id, list_name in zip(self._trello_list_ids, self._trello_list_names):
json_data = super().get_cards_in_list(list_id)
preview_text += f'=====\n{list_name}\n=====\n'
for json in json_data:
card_name = json['name']
print(card_name)
preview_text += f'{card_name}\n---\n'
self.window['PREVIEW'].Update(preview_text)
# 選択されているTrelloリストのカードを出力します
if event == 'SELECTED_LIST_PRINT':
preview_text = ''
list_name = values['LIST_NAME']
preview_text += f'=====\n{list_name}\n=====\n'
json_data = super().get_cards_in_list(self._trello_list_id)
for json in json_data:
card_name = json['name']
preview_text += f'{card_name}\n---\n'
self.window['PREVIEW'].Update(preview_text)
def job():
user_id = 'user...'
key = 'your api key'
secret = 'your secret'
token = 'your token'
gui = TrelloGui(user_id, key, secret, token)
gui.event_loop()
if __name__ == '__main__':
job()
まず、TrelloClient
の時と同様にTrelloの認証に必要な情報を渡して、guiをインスタンス化します。
TrelloGui
クラスはTrelloClient
クラスの機能を継承しながら、インスタンス時にGUI Windowを作成します。
Trelloの認証情報に合わせて、self.board_names
からコンボボックスの中身を生成してます。
このように表示されます。
好きなアルバム
のボードを選んでみましょう。
このコンボボックスを選択すると、以下の処理が実行されます。
if event == 'BOARD_NAME':
board_name = values['BOARD_NAME']
board_id = super().get_board_id(board_name)
self.set_trello_board_id(board_id)
id_and_name_of_trello_list = super().get_list_ids_and_names(board_id)
self.set_trello_list_names(id_and_name_of_trello_list)
self.set_trello_list_ids(id_and_name_of_trello_list)
self.window.FindElement('LIST_NAME').Update(values=self._trello_list_names)
適宜TrelloClient
クラスの機能を使用して、Trelloリスト名を取得し、GUI上のTrelloリスト名を表すコンボボックスを更新しています。
カードを加えるのも簡単にできます。
カネコアヤノ
さんの新作よすが
を加えてみましょう。
普通ならTrelloはこんな使い方はせずにタスク管理に使うことが多いと思うので、例として期限を設定しておきます。
こういう感じで打ち込んでADD
ボタンを押します。
そうすると以下の処理が実行されます。
if event == 'ADD_CARD':
card_name = values['CARD_NAME']
desc = values['DESC']
due_date = values['DUE_DATE']
due_time = values['DUE_TIME']
super().add_task(self._trello_list_id, card_name, due_date, due_time, desc)
ここも親クラスを呼び出してカードを追加する処理をしています。
無事追加されましたね。
期限と詳細もちゃんと書きこまれています。
TrelloはUIに優れているので、入力、つまりカードの追加はGUIをわざわざ使わなくても簡単にできます。
問題は出力のほうです。
Trelloは有料版だとcsvでエクスポートできますが、無料だとjson形式でしかエクスポートできません。
json形式は一般的に使うにはちょっと微妙なので、テキストで一覧に出力できるようにしています。
しばたさとこ島
とよすが
をfavorite
リストに移動させて出力します。
favorite
を選択して、Just Selected List
ボタンを押しますと、以下の処理が実行されます。
if event == 'SELECTED_LIST_PRINT':
preview_text = ''
list_name = values['LIST_NAME']
preview_text += f'=====\n{list_name}\n=====\n'
json_data = super().get_cards_in_list(self._trello_list_id)
for json in json_data:
card_name = json['name']
preview_text += f'{card_name}\n---\n'
self.window['PREVIEW'].Update(preview_text)
ここも親クラスから、カード名の一覧を取得しています。
このように表示されます。
ALL Card in Board
を押すと、選択しているボードのすべてのカード名が出力されます。