【Twitter自動運用】CustomTkinterでツールにモダンなGUIデザインを実装


雑談

↓前回話した記事はこちら siro-yamaneko.hatenablog.jp

前回は、Twitterの「いいね」を押す動作を自動化するプログラムを作成、および実行する為のGUITkinterを用いて作成した。

ただ、個人的にデザインが古臭く感じてしまい、GUI関連のライブラリを調べていたところ、ちょうど求めていたライブラリに出会えた。 CustomTkinterというライブラリで、Tkinterをベースにしつつ、洗練されたモダンなデザインのGUIを作成できるという特徴がある。

こちらを用いて、改修した結果、以下のように現代風のプログラムになって個人的に満足度が上がった。

使用方法

1.ユーザー名、パスワードを設定する。 2.処理内容を選択する。(現時点では、自動いいね、自動フォローバックが選択可能) 3.実行、または保存ボタンを押下する。 保存ボタンを押下すると、プログラムを実行しているディレクトリにsetting.jsonとして保存データが生成され、今後起動時に保存したデータが読み込まれる。

今後

今後は、タスクスケジューラを用いたスケジュール実行を実装予定

参考までに、CustomTkinterを用いたGUIソースコードを以下に記載するので、よければご参考にどうぞ。

import customtkinter as ctk
import json
import subprocess
import os

# ファイルディレクトリを取得
current_dir = os.path.dirname(os.path.abspath(__file__))
SETTINGS_FILE = os.path.join(current_dir, 'settings.json')

# 設定をロードする関数
def load_settings():
    try:
        with open(SETTINGS_FILE, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        return {}

# 設定を保存する関数
def save_settings(settings):
    with open(SETTINGS_FILE, 'w') as f:
        json.dump(settings, f)

# ウィンドウの設定
ctk.set_appearance_mode("System")
ctk.set_default_color_theme("blue")

root = ctk.CTk()
root.title("twitter Auto Tools")
root.geometry("400x300")  # ウィンドウサイズ
root.resizable(False, False)

# 設定をロード
settings = load_settings()

# フォントの指定
font = ('メイリオ', 10)

# 入力フィールドの設定
input_frame = ctk.CTkFrame(root)
input_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky='nsew')

# ユーザー名入力フィールド
ctk.CTkLabel(input_frame, text="ユーザー名", anchor='w', font=font).grid(row=0, column=0, padx=8, pady=4, sticky='nsew')
username_entry = ctk.CTkEntry(input_frame, font=font)
username_entry.grid(row=0, column=1, padx=16, pady=4, sticky='ew')
username_entry.insert(0, settings.get('username', ''))

# パスワード入力フィールド
ctk.CTkLabel(input_frame, text="パスワード", anchor='w', font=font).grid(row=1, column=0, padx=8, pady=4, sticky='nsew')
password_entry = ctk.CTkEntry(input_frame, show="*", font=font)
password_entry.grid(row=1, column=1, padx=16, pady=4, sticky='ew')
password_entry.insert(0, settings.get('password', ''))

# 処理パターンプルダウン
ctk.CTkLabel(input_frame, text="処理パターン", anchor='w', font=font).grid(row=2, column=0, padx=8, pady=4, sticky='nsew')
pattern_var = ctk.StringVar(value='処理を選択してください')
pattern_menu = ctk.CTkOptionMenu(input_frame, variable=pattern_var, values=["自動いいね", "自動フォローバック"])
pattern_menu.grid(row=2, column=1, padx=16, pady=4, sticky='ew')

# OptionMenuのフォント設定
menu = pattern_menu._dropdown_menu
menu.configure(font=font)

# URL入力フィールド
url_label = ctk.CTkLabel(input_frame, text="URL", anchor='w', font=font)
url_label.grid(row=3, column=0, padx=8, pady=0, sticky='w')
url_entry = ctk.CTkTextbox(input_frame, height=60, font=font)
url_entry.grid(row=3, column=1, padx=16, pady=0, sticky='ew')
url_entry.insert('1.0', settings.get('url', ''))

# メッセージ表示用のフレームとラベル
message_frame = ctk.CTkFrame(root)
message_frame.grid(row=4, column=0, columnspan=2, pady=4, padx=8, sticky='ew')

message_label_font = ('メイリオ', 12)
status_label = ctk.CTkLabel(message_frame, text="ステータス", font=font)
status_label.grid(row=0, column=0, sticky='w', padx=8, pady=0)
message_label = ctk.CTkLabel(message_frame, text="", font=(message_label_font[0], message_label_font[1], 'bold'))
message_label.grid(row=0, column=1, sticky='w', padx=8, pady=0)

message_frame.grid_columnconfigure(1, weight=1)

# メッセージを3秒後に消す関数
def clear_message():
    message_label.configure(text="")

# 保存ボタンの処理
def save_and_exit():
    if pattern_var.get() == "処理を選択してください":
        message_label.configure(text="ERROR:処理が選択されていません", text_color="red")
        root.after(3000, clear_message)
        return

    if not username_entry.get() or not password_entry.get() or (pattern_var.get() == "自動いいね" and not url_entry.get("1.0", 'end').strip()):
        message_label.configure(text="ERROR:空欄が存在します", text_color="red")
        root.after(3000, clear_message)
    else:
        settings = {
            'username': username_entry.get(),
            'password': username_entry.get(),
            'url': url_entry.get("1.0", 'end').strip() if pattern_var.get() == "自動いいね" else '',
            'pattern': pattern_var.get()
        }
        save_settings(settings)
        message_label.configure(text="SUCCESS:保存が完了しました", text_color="green")
        root.after(3000, clear_message)

# 終了ボタンの処理
def close_program():
    root.destroy()

# 実行ボタンの処理
def execute():
    if pattern_var.get() == "処理を選択してください":
        message_label.configure(text="ERROR:処理を選択してください", text_color="red")
        root.after(3000, clear_message)
        return

    if not username_entry.get() or not password_entry.get() or (pattern_var.get() == "自動いいね" and not url_entry.get("1.0", 'end').strip()):
        message_label.configure(text="ERROR:空欄が存在します", text_color="red")
        root.after(3000, clear_message)
        return

    settings = {
        'username': username_entry.get(),
        'password': username_entry.get(),
        'url': url_entry.get("1.0", 'end').strip() if pattern_var.get() == "自動いいね" else '',
        'pattern': pattern_var.get()
    }
    save_settings(settings)

    try:
        if settings['pattern'] == "自動いいね":
            script_path = os.path.join(os.path.dirname(__file__), "twitterAutoLike.py")
            subprocess.run(["python", script_path, settings['username'], settings['password'], settings['url']])
        elif settings['pattern'] == "自動フォローバック":
            script_path = os.path.join(os.path.dirname(__file__), "twitterAutofollowback.py")
            subprocess.run(["python", script_path, settings['username'], settings['password'], settings['url']])
    except Exception as e:
        message_label.configure(text=f"ERROR:{settings['pattern']}を実行中にエラーが発生しました: {e}", text_color="red")
    else:
        message_label.configure(text=f"SUCCESS:{settings['pattern']}が完了しました", text_color="green")
    root.after(3000, clear_message)

# 処理パターン変更時の処理
def on_pattern_change(*args):
    if pattern_var.get() == "自動フォローバック":
        url_label.grid_remove()
        url_entry.grid_remove()
    else:
        url_label.grid()
        url_entry.grid()

pattern_var.trace("w", on_pattern_change)

# 初期設定に応じた表示
on_pattern_change()

# ボタンの設定 
button_frame = ctk.CTkFrame(root)
button_frame.grid(row=5, column=0, columnspan=2, pady=(4, 10), padx=(10, 10), sticky='ew')

button_width = 40  # ボタンの横幅を設定
ctk.CTkButton(button_frame, text="実行", command=execute, width=button_width, height=1, font=font).grid(row=0, column=0, padx=5, pady=5)
ctk.CTkButton(button_frame, text="保存", command=save_and_exit, width=button_width, height=1, font=font).grid(row=0, column=1, padx=5, pady=5)
ctk.CTkButton(button_frame, text="終了", command=close_program, width=button_width, height=1, font=font).grid(row=0, column=2, padx=5, pady=5)

# ウィンドウとフレームのサイズ連動を設定
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
input_frame.grid_rowconfigure(3, weight=1)
input_frame.grid_columnconfigure(1, weight=1)

root.mainloop()