質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.75%

  • Python

    6841questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Tkinter で動作をイベントとして登録する方法

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 628
退会済みユーザー

退会済みユーザー

プログラミング初学者です

音楽プレーヤーをtkinterを使って製作しているのですが
ボタン押すと新しい子ウィンドウが作成され、子ウィンドウが出ている間はボタンをDISABLEして、ウィンドウが新たに作成されないようにしたいのですが、
"子ウィンドウを閉じるとまたボタンをNORMALにする"という部分をコードとして書く方法がよく分からず困っています

つまりある動作(ここでいえば子ウィンドウが開いているかいないか)をイベントとして登録するというツールや発想があれば教えていただきたいです
よろしくお願いします

import tkinter as Tk

class VolumeWindow:

    def __init__(self, master):
        self.master = master

    def make_window(self, event=None):
        self.top = Tk.Toplevel(self.master)
        self.f = Tk.Frame(self.top)
        self.f.pack()
        self.volume = Tk.IntVar()
        self.volume.set(50)
        self.volume_bar = Tk.Scale(self.f, orient='h',variable=self.volume)
        self.volume_bar.pack()

class Frame(Tk.Frame):
    def __init__(self, master=None):
        Tk.Frame.__init__(self, master)
        self.f = Tk.Frame(self)
        self.f.pack()

        self.volume = VolumeWindow(self.f)
        self.f_button = Tk.Frame(self.f)
        self.f_button.pack()

        self.volume_button = Tk.Button(self.f_button, text='Volume', command=self.control_volume, state=Tk.NORMAL)

        self.volume_button.pack(padx=5, pady=5, side=Tk.LEFT)

    def control_volume(self):
        self.volume.make_window()
        self.volume_button.configure(state=Tk.DISABLED)

if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • umyu

    2018/01/21 05:12

    1,VolumeWindowに__init__が2個定義されています。2,class Frame(Tk.Frame):の定義がコンパイルエラーになります!

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2018/01/21 05:34

    コピペミスです、、、申し訳ないです、編集しました

    キャンセル

  • umyu

    2018/01/21 05:37 編集

    えっと、質問文のコードだけを別ファイルにしてプログラムを実行してみてくださいな。エラー発生しませんか?

    キャンセル

回答 1

checkベストアンサー

+1

プログラミング初学者です

1,これは誰でも初学者の時期があるので問題は無いのですが、質問するときのヒントのページを見て、質問の初心者にはならないでくださいな。

2,質問文のソースコードは1行ではなく、もう少し全体のコードを投稿してくださいな。

■本題
self.f_buttonとVolumeWindowの型が分からなかったので、
VolumeWindowを1個しか開かない簡単なサンプルコードを添付しておきます。
メインウィンドウの制御は、自分で行ってくださいな。

# -*- coding: UTF-8 -*
from tkinter import ttk
from tkinter import *
import functools


class VolumeWindow(Toplevel):
    def __init__(self, root):
        super().__init__()
        # ウィンドウを閉じたときのイベントを登録
        self.protocol('WM_DELETE_WINDOW', functools.partial(self.on_window_exit, param=2))
        self.volumes = Scale(self, {'label': 'volume', 'from_': 0, 'to': 100,
                                    'length': 200, 'command': self.__on_changed_scale_value})
        self.volumes.pack()
        # 初期状態は非表示
        self.set_visible(False)

    def __on_changed_scale_value(self, event):
        # Scale(スライダ)を移動時にイベント発生
        pass

    def on_window_exit(self, param):
        # VolumeWindowウィンドウを閉じた時に呼ばれる処理
        if param == 2:
            self.set_visible(False)

    def set_visible(self, visible):
        # ウィンドウの表示を切り替える
        # visible:True 表示,False 非表示
        if visible:
            self.deiconify()
        else:
            self.withdraw()


class App(object):
    def __init__(self):
        self.root = Tk()
        self.root.title('Q109871')
        # ウィンドウを閉じたときのイベントを登録
        self.root.protocol('WM_DELETE_WINDOW', functools.partial(self.on_window_exit, param=1))
        # ボリュームウィンドウを作成して、self.volume_windowに変数を保持
        self.volume_window = VolumeWindow(self.root)
        # ボタンクリック時はVolumeWindow#set_visibleをTrueで呼び出す。
        self.volume_button = Button(self.root, text='Volume',
                                       command=functools.partial(self.volume_window.set_visible, visible=True))
        self.volume_button.pack()

    def on_window_exit(self, param):
        # sys.exitを呼び出し。
        if param == 1:
            sys.exit(0)

    def run(self):
        self.root.mainloop()


def main():
    app = App()
    app.run()


if __name__ == '__main__':
    main()

コメント欄の質問を受けて追加

まず、Python言語自体が簡単なテストアプリならいいのですが、
本格的な画面(GUI)アプリケーション作成には向いていない言語だと個人的に思っています。
すぐに思いつく理由としては
・情報がtkinterのチュートリアルレベルで終わっている事が多い。
・他のGUIアプリを開発した事がある前提の情報が多い。
・日本語の情報が少なめ。
・体感 GUIアプリ開発者のユーザーが少なめ

どういうユーザーを対象にしてアプリケーションを作成しているのかが分かりませんが。
例えば、OSがWindowsならVisual Studio Communityを使ったアプリになるのでは。

あとはそのアプリに質問者様がどれだけの時間(作業工数)を掛けれるかでしょうか。

個人的には最低限、画面レイアウトをコードではなく、XMLやJSONで定義できるGUIツールキットを使ったほうが、
画面の定義に掛かる時間を短く出来ます。

なければ、昔、別の質問で回答した感じの事をプログラムで作成して、
JSONファイルなどから読む形にするといいですよ。
とりとめのない回答になってしまいましたが、参考になればー。


質問文の編集ありがとうございました。以下のサンプルコードで希望する動作になりますかー?

サンプルコード1)元ソースをあまり変更しないタイプ

# -*- coding: UTF-8 -*
import tkinter as Tk


# ↓追加 ここから
def set_window_visible(window, visible):
    if visible:
        window.deiconify()
    else:
        window.withdraw()
# ↑追加 ここまで


class VolumeWindow:

    def __init__(self, master):
        # masterはFrame型
        self.master = master
        self.top = Tk.Toplevel(self.master)
        self.f = Tk.Frame(self.top)
        self.f.pack()
        self.volume = Tk.IntVar()
        self.volume.set(50)
        self.volume_bar = Tk.Scale(self.f, orient='h',variable=self.volume)
        self.volume_bar.pack()
        set_window_visible(self.top, False)
        #VolumeWindowウィンドウを閉じる時のイベントを追加
        self.top.protocol('WM_DELETE_WINDOW', self.on_window_exit)

    def on_window_exit(self):
        # VolumeWindowウィンドウを閉じた時に呼ばれる処理
        # Frame型の変数volume_buttonを設定
        self.master.volume_button.configure(state=Tk.NORMAL)
        # ボリュームウィンドウを非表示に
        set_window_visible(self.top, False)


class Frame(Tk.Frame):
    def __init__(self, master=None):
        Tk.Frame.__init__(self, master)
        self.f = Tk.Frame(self)
        self.f.pack()
        # 引数をFrameに変更
        self.volume = VolumeWindow(self)
        self.f_button = Tk.Frame(self.f)
        self.f_button.pack()
        self.volume_button = Tk.Button(self.f_button, text='Volume', command=self.control_volume, state=Tk.NORMAL)
        self.volume_button.pack(padx=5, pady=5, side=Tk.LEFT)

    def control_volume(self):
        # make_windowはしない。ボリュームウィンドウを表示するだけ。
        #self.volume.make_window()
        set_window_visible(self.volume.top, True)
        self.volume_button.configure(state=Tk.DISABLED)


if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()

サンプルコード2)個人的な変更版
1,VolumeWindowをTk.Toplevelに変更して変数を削除
2,メイン画面のボタンが増えた時用にFrame#widgets_set_state関数を追加

# -*- coding: UTF-8 -*
import tkinter as Tk


# ↓追加
def set_window_visible(window, visible):
    if visible:
        window.deiconify()
    else:
        window.withdraw()
#

#Tk.Toplevelを継承するように変更
class VolumeWindow(Tk.Toplevel):
    def __init__(self, master):
        super().__init__(master)
        self.f = Tk.Frame(self)
        self.f.pack()
        self.volume = Tk.IntVar()
        self.volume.set(50)
        self.volume_bar = Tk.Scale(self.f, orient='h',variable=self.volume)
        self.volume_bar.pack()
        set_window_visible(self, False)
        #VolumeWindowウィンドウを閉じる時のイベントを追加
        self.protocol('WM_DELETE_WINDOW', self.on_window_exit)

    def on_window_exit(self):
        # VolumeWindowウィンドウを閉じた時に呼ばれる処理
        # 親ウィンドウのボタンを活性化
        self.master.widgets_set_state(True)
        # ボリュームウィンドウを非表示に
        set_window_visible(self, False)


class Frame(Tk.Frame):
    def __init__(self, master=None):
        Tk.Frame.__init__(self, master)
        self.f = Tk.Frame(self)
        self.f.pack()
        # 引数をFrameに変更
        self.volume = VolumeWindow(self)
        self.f_button = Tk.Frame(self.f)
        self.f_button.pack()
        self.volume_button = Tk.Button(self.f_button, text='Volume', command=self.control_volume, state=Tk.NORMAL)
        self.volume_button.pack(padx=5, pady=5, side=Tk.LEFT)

    def control_volume(self):
        # make_windowはしない。ボリュームウィンドウを表示するだけ。
        #self.volume.make_window()
        set_window_visible(self.volume, True)
        self.widgets_set_state(False)

    def widgets_set_state(self, state):
        if state:
            self.volume_button.configure(state=Tk.NORMAL)
        else:
            self.volume_button.configure(state=Tk.DISABLED)


if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()


個人的な意見としてはボリュームウィンドウを表示しても、別にメインウィンドウの操作を制限する必要はないのでは・・と思いますが。。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/21 04:28

    申し訳ないです
    中途半端に1行だけ投稿するべきではなかったです
    作業依頼になってしまわないように、発想やツールがあるならば教えていただきたいと思い、このような中途半端な投稿にしてしまいました
    質問の仕方を変えるべきでした

    キャンセル

  • 2018/01/21 06:36

    ありがとうございます、希望通りの動きになっています
    あとは変更点を理解するようにします

    キャンセル

  • 2018/01/21 07:50

    確かにこの方法ならボタンを押したときにWindowを新たにcreateするわけではないから、ボタンを制御する必要はないですね
    本当にありがとうございました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.75%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python

    6841questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。