@RSrion (RS rs)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

tkinterを別のスレッドで動かしてdiscordpyをブロッキングしないようにしたい。

今していること

Discordpyとtkinterでアモアスなどで使うミュートシステムを作っています。
あるコマンドを打つことによってウィンドウが出てきて、そこのボタンを押すことでミュートの制御をします。

発生している問題・エラー

discordpyのイベントループの中にtkinterのイベントループがあるとブロッキングが起こってしまうのでthreadingモジュールで別スレッドで処理しようと思ったところ

Exception in thread Thread-1 (mutesysgui):
Traceback (most recent call last):
  File "C:\Users\rsrio\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "C:\Users\rsrio\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 975, in run
    self._target(*self._args, **self._kwargs)
  File "c:\Users\rsrio\OneDrive\デスクトップ\新しいフォルダー\testpy\test.py", line 24, in mutesysgui
    root.mainloop()
  File "C:\Users\rsrio\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1485, in mainloop
    self.tk.mainloop(n)
RuntimeError: Calling Tcl from different apartment

と表示されてしまいます。

該当するソースコード

import discord
from discord.ext import commands
import tkinter as tk
import threading
mute = False

bot=commands.Bot(command_prefix="!",intents=discord.Intents.all())
token = "toooooookun"

def button_clicked():
    global mute
    if mute == False:
        button.configure(text="ミュート解除")
        mute = True
    else:
        button.configure(text="ミュート")
        mute = False

def on_closing():
    pass

def mutesysgui():
    root.mainloop()
    
root = tk.Tk()
root.geometry("357x73+0+950")
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)

# ボタンを作成
button = tk.Button(root, text="ミュート",height=1,width=19,command=button_clicked,font=("けいふぉんと",20))

# ボタンを配置
button.place(x = 10, y = 10)

thread = threading.Thread(target=mutesysgui)



@bot.event
async def on_ready():
    print("起動しました。")

@bot.event
async def on_message(message):
    if message.content == "hello":
        thread.start()
        await message.channel.send("どーも")

bot.run(token)

環境

Windows 10
Pythonのバージョン:3.1.1 64bit
tkinterのバージョン:8.6
Discordpyのバージョン:2.2.2

threadingモジュールを使ってどのようにすればこのエラーを回避できるか教えてくれるとうれしいです。並列処理ができるものならthreadingモジュールじゃなくても構いません。よろしくお願いします。

0 likes

1Answer

原則として、そのようなことは不可能です。
discord.py は asyncio の下で動いている一方、tkinterは独自のスレッドで動いているため、両者をうまく統合して動かすことはできません。

もう少し具体的に言うと、tkinter の root.mainloopは 非同期関数でないため、実行時に discord.py の非同期イベントループをブロックしてしまいます。そのため、discord.py のコードは、tkinterウィンドウを閉じるまで実行することができません。

他の方法として
・discord.py を使わず、tkinter の中で生のdiscord APIを叩く
・非同期関数を使っているGUIライブラリ(≠tkiknter)を使用する。→なお私はそのようなものは知りません。
・非同期関数を使っていないdiscordライブラリを使用する。→なお私はそのようなものは知りません。
・tkinter と discord.pyによるclientをそれぞれ独立なアプリケーションとして実装し、websocket等の間接的な通信によって制御する。
のいずれかになります。

0Like

Your answer might help someone💌