函数运行时如何禁用tkinter'X'

时间:2019-07-25 13:46:19

标签: python tkinter

我这里有几行代码,我想在函数运行时禁用X button,并在函数结束后将其设置回正常状态。

我可以在功能运行时禁用X button,但想将其设置为正常状态,以便在功能运行完成后将其关闭。

import tkinter as tk
from tkinter import messagebox
import tkinter.ttk as ttk



def my_message_box(event=None):
    my_m = messagebox.askyesno("Close window", "Are you sure ?")
    if my_m > 0:
        root.destroy()


def do_nothing():
   # messagebox.showinfo("ASSS", "U can't close the window wait for the process to finish")
    pass


def run_range_func():
    for bn in range(1, 100):
        print(bn)
        rt = label.config(text=bn)
        label.after(1000, rt)

        root.protocol("WM_DELETE_WINDOW", do_nothing)


root = tk.Tk()
root.geometry("500x500")


button = tk.Button(root, text="START", command=run_range_func)
button.pack()


label = tk.Label(root, text="numbers to be displayed here")
label.place(x=200, y=200)

root.protocol("WM_DELETE_WINDOW", my_message_box)
root.mainloop()

2 个答案:

答案 0 :(得分:1)

只要我在Tkinter程序中有长期工作,就会将其分为三个部分:

  1. start回调,与“开始”按钮或菜单项耦合。
  2. 一个step回调,完成了一部分工作。
  3. 一个stop回调函数,与一个STOP按钮耦合。

例如,start回调禁用“开始”按钮。 self.runbutton['state'] = tk.DISABLED。它还启用了停止按钮。它使用step方法设置after回调,并将“运行中”状态变量设置为True

只要“运行”为stepafter回调就可以完成一小部分工作,更新状态并使用True重新提交。

stop回调将“运行”设置为False,重新启用开始按钮并禁用自身。

我正在从下面的github回购中嵌入一个完整的示例。 这是一个用于文件的小型查找和替换程序。

#!/usr/bin/env python3
# file: far.py
# vim:fileencoding=utf-8:fdm=marker:ft=python
#
# Copyright © 2018 R.F. Smith <rsmith@xs4all.nl>.
# SPDX-License-Identifier: MIT
# Created: 2018-02-27T23:38:17+0100
# Last modified: 2018-04-17T00:11:57+0200

from tkinter import filedialog
from tkinter import ttk
from tkinter.font import nametofont
import argparse
import os
import shutil
import sys
import tkinter as tk

__version__ = '0.1'


class FarUI(tk.Tk):

    def __init__(self, rootdir='', findname='', replacement=''):
        tk.Tk.__init__(self, None)
        self.running = False
        self.finditer = None
        self.create_window()
        self.tree['text'] = rootdir
        self.find.insert(0, findname)
        self.replace['text'] = replacement

    def create_window(self):
        """Create the GUI"""
        # Set the font.
        default_font = nametofont("TkDefaultFont")
        default_font.configure(size=12)
        self.option_add("*Font", default_font)
        # General commands and bindings
        self.bind_all('q', self.quit_cb)
        self.wm_title('Find and Replace v' + __version__)
        self.columnconfigure(4, weight=1)
        self.rowconfigure(4, weight=1)
        # First row
        ftxt = ttk.Label(self, text='Find:')
        ftxt.grid(row=0, column=0, sticky='w')
        fe = ttk.Entry(self, justify='left')
        fe.grid(row=0, column=1, columnspan=4, sticky='ew')
        self.find = fe
        # Second row
        treetxt = ttk.Label(self, text='In tree:')
        treetxt.grid(row=1, column=0, sticky='w')
        te = ttk.Label(self, justify='left')
        te.grid(row=1, column=1, columnspan=4, sticky='ew')
        tb = ttk.Button(self, text="browse...", command=self.tree_cb)
        tb.grid(row=1, column=5, columnspan=2, sticky='ew')
        self.tree = te
        # Third row
        reptxt = ttk.Label(self, text='Replace with:')
        reptxt.grid(row=2, column=0, sticky='w')
        re = ttk.Label(self, justify='left')
        re.grid(row=2, column=1, columnspan=4, sticky='ew')
        rb = ttk.Button(self, text="browse...", command=self.replace_cb)
        rb.grid(row=2, column=5, columnspan=2, sticky='ew')
        self.replace = re
        # Fourth row
        run = ttk.Button(self, text="run", command=self.start_replace_cb)
        run.grid(row=3, column=0, sticky='ew')
        stop = ttk.Button(self, text="stop", command=self.stop_replace_cb, state=tk.DISABLED)
        stop.grid(row=3, column=1, sticky='w')
        self.runbutton = run
        self.stopbutton = stop
        qb = ttk.Button(self, text="quit", command=self.destroy)
        qb.grid(row=3, column=2, sticky='w')
        ttk.Label(self, justify='left', text='Progress: ').grid(row=3, column=3, sticky='w')
        progress = ttk.Label(self, justify='left', text='None')
        progress.grid(row=3, column=4, columnspan=2, sticky='ew')
        self.progress = progress
        # Fifth row
        message = tk.Text(self, height=4)
        message.grid(row=4, column=0, columnspan=6, sticky='nsew')
        s = ttk.Scrollbar(self, command=message.yview)
        s.grid(row=4, column=6, sticky='nse')
        message['yscrollcommand'] = s.set
        self.message = message

    def quit_cb(self, event):
        """
        Callback to handle quitting.

        This is necessary since the quit method does not take arguments.
        """
        self.running = False
        self.quit()

    def tree_cb(self):
        rootdir = filedialog.askdirectory(
            parent=self, title='Directory where to start looking', mustexist=True
        )
        self.tree['text'] = rootdir

    def replace_cb(self):
        replacement = filedialog.askopenfilename(parent=self, title='Replacement file')
        self.replace['text'] = replacement

    def start_replace_cb(self):
        rootdir = self.tree['text']
        filename = self.find.get()
        replacement = self.replace['text']
        if self.running or not rootdir or not filename or not replacement:
            self.message.delete('1.0', tk.END)
            self.message.insert(tk.END, 'Missing data!')
            return
        self.running = True
        self.message.delete('1.0', tk.END)
        self.message.insert(tk.END, 'Starting replacement\n')
        self.runbutton['state'] = tk.DISABLED
        self.stopbutton['state'] = tk.NORMAL
        self.finditer = os.walk(rootdir)
        self.after(1, self.replace_step)

    def replace_step(self):
        if not self.running:
            return
        try:
            path, _, files = self.finditer.send(None)
            rootlen = len(self.tree['text']) + 1
            # Skip known revision control systems directories.
            for skip in ('.git', '.hg', '.svn', '.cvs', '.rcs'):
                if skip in path:
                    self.progress['text'] = 'skipping ' + path[rootlen:]
                    return
            if len(path) > rootlen and path[rootlen] != '.':
                self.progress['text'] = 'processing ' + path[rootlen:]
                filename = self.find.get()
                if filename in files:
                    original = path + os.sep + filename
                    replacement = self.replace['text']
                    repfile = os.path.basename(replacement)
                    dest = path + os.sep + repfile
                    self.message.insert(tk.END, "Removing '{}'\n".format(original))
                    os.remove(original)
                    self.message.insert(tk.END, "Copying '{}' to '{}'\n".format(replacement, dest))
                    shutil.copy2(replacement, dest)
            self.after(1, self.replace_step)
        except StopIteration:
            self.stop()
            self.message.insert(tk.END, 'Finished replacement.\n')

    def stop(self):
        self.running = False
        self.finditer = None
        self.runbutton['state'] = tk.NORMAL
        self.stopbutton['state'] = tk.DISABLED
        self.progress['text'] = 'None'

    def stop_replace_cb(self):
        self.stop()
        self.message.insert(tk.END, 'Replacement stopped by user.\n')


def main():
    """Main entry point for far.py"""
    # Parse the arguments.
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        '-d', '--rootdir', type=str, default=os.getcwd(), help='Directory to start looking in.'
    )
    parser.add_argument('-f', '--findname', type=str, default='', help='Name of the file to find.')
    parser.add_argument(
        '-r', '--replacement', type=str, default='', help='Path of the replacement file.'
    )
    parser.add_argument('-v', '--version', action='version', version=__version__)
    args = parser.parse_args(sys.argv[1:])
    if not args.rootdir.startswith(os.sep):
        args.rootdir = os.getcwd() + os.sep + args.rootdir
    # Create the UI.
    root = FarUI(args.rootdir, args.findname, args.replacement)
    root.mainloop()


if __name__ == '__main__':
    # Detach from the terminal on POSIX systems.
    if os.name == 'posix':
        if os.fork():
            sys.exit()
    # Run the program.
    main()

答案 1 :(得分:0)

您的代码中缺少一些部分(即函数rt),但是我想您的run_range_func应该是这样的:

def run_range_func():
    root.protocol("WM_DELETE_WINDOW", do_nothing)

    for bn in range(1, 100):
        print(bn)
        rt = label.config(text=bn)
        label.after(1000, rt)

    root.protocol("WM_DELETE_WINDOW", my_message_box)

如果您的run_range_func是一个复杂的过程,则可能需要Thread,这样它才不会冻结GUI。