使用 tkinter 和 multiprocessing 可执行创建多个 windows

Executable using tkinter and multiprocessing creates multiple windows

我已经使用 tkinter 为脚本构建了一个图形用户界面。

我已经尝试使用 cx_freeze 和 pyinstaller 构建可执行文件。

我广泛使用 scipy、numpy、statsmodels 和 matplotlib

每当我单击 "Run" 按钮时,它会生成另一个 window 并且其下方的 window 停止响应。这似乎可以无限期地发生。

我确实在我的应用程序中使用了多重处理,但我在 python 脚本版本

中修复了多个 windows 问题

如何解决我的独立程序中的多个 windows 问题?

如果没有合理的修复,有没有其他办法打包程序使用?也许是自定义 Python 发行版?

from tkinter import *
from tkinter import ttk
def run():
    import multiprocessing
    import risers_fallers
    import time
    time_periods = list()
    time_periods.append(month.get())
    time_periods.append(threemonth.get())
    time_periods.append(sixmonth.get())
    time_periods.append(year.get())
    #print(infile.get(), time_periods, filter_val.get(), outfile.get())

    #loading screen 
    toplevel = Toplevel()
    toplevel.focus_force()
    loading = Label(toplevel, text = "RUNNING")#PhotoImage(file= photopath, format="gif - {}")
    loading.pack()

    subproc = multiprocessing.Process(target = risers_fallers.risers_fallers, args = (infile.get(), time_periods, filter_val.get(), outfile.get(),
            top_x.get(), excel.get(), tableau.get(), onecat.get(), multicat.get(), external.get()))
    subproc.start()
    subproc.join()
    toplevel.destroy()
def browsecsv():
    from tkinter import filedialog
    Tk().withdraw() 
    filename = filedialog.askopenfilename()
    #print(filename)
    infile.set(filename)

if __name__ == "__main__":
    #initialize tk
    root = Tk()
    #set title
    root.title("FM Risers and Fallers")
    #create padding, new frame, configure columns and rows
    #mainframe = ttk.Frame(root, padding="3 3 12 12")

    #our variables
    month = BooleanVar()
    threemonth = BooleanVar()
    sixmonth = BooleanVar()
    year = BooleanVar()
    excel = BooleanVar()
    tableau = BooleanVar()
    onecat = BooleanVar()
    multicat = BooleanVar()
    external = BooleanVar()
    top_x = StringVar()
    top_x.set("15")

    infile = StringVar()
    outfile = StringVar()

    filter_val = StringVar()
    photopath = "./41.gif"
    #default values
    filter_val.set("30")
    import datetime
    from re import sub
    d = datetime.datetime.now()
    outfile.set(sub('[^0-9|\s]',' ', str(d)))
    """
    our widgets
    """
    #labels for tab 1 Not needed with pane view
    # ttk.Label(mainframe, text="Input file") #.grid(column=3, row=1, sticky=W)
    # ttk.Label(mainframe, text="Output name") #.grid(column=3, row=1, sticky=W)
    # ttk.Label(mainframe, text="Dashboard period of times") #.grid(column=3, row=1, sticky=W)
    # ttk.Label(mainframe, text="Filter by growth rate") #.grid(column=3, row=1, sticky=W)

    master = Frame(root, name = 'master')
    master.pack(fill=BOTH)

    #notebook container
    notebook = ttk.Notebook(master)
    notebook.pack(fill=BOTH, padx=2, pady=3)
    tab0 = ttk.Frame(notebook)
    tab1 = ttk.Frame(notebook)
    tab2 = ttk.Frame(notebook)

    notebook.add(tab0, text='Risers and Fallers')
    notebook.add(tab1, text='Help')
    notebook.add(tab2, text='About')

    #tab 1 panes
    panel_0 = ttk.Panedwindow(tab0, orient=VERTICAL)
    file_pane_0 = ttk.Labelframe(panel_0, text='Input and Output', width = 300, height=100)
    dashboard_pane_0 = ttk.Labelframe(panel_0, text='Dashboard Time Period', width = 300, height=200)
    filter_pane_0 = ttk.Labelframe(panel_0, text='Filter by Growth Rate', width = 300, height=200)
    run_frame_0 = Frame(panel_0, width = 300, height=100)
    output_options_frame = ttk.Labelframe(panel_0, text='Output Options', width = 300, height=200)
    top_x_frame_0 = ttk.Labelframe(panel_0, text = "Number of Risers", width = 300, height=100)

    #grid it
    panel_0.pack(fill=BOTH, expand=1)
    file_pane_0.grid(row = 0, column = 0, columnspan = 3)
    dashboard_pane_0.grid(row = 1, column = 0)
    filter_pane_0.grid(row = 2, column = 0)
    output_options_frame.grid(row = 1, column = 1)
    top_x_frame_0.grid(row = 2, column = 1)
    #pack em!
    # panel_0.pack(fill=BOTH, expand=1)
    # file_pane_0.pack(fill=X, expand=1)
    # dashboard_pane_0.pack(side = LEFT, fill = Y)
    # filter_pane_0.pack(side = RIGHT, fill = Y)
    # top_x_frame_0.pack(fill=BOTH, expand=1)
    #tab 2 panes
    panel_1 = ttk.Panedwindow(tab1, orient=VERTICAL)
    file_pane_1 = ttk.Labelframe(panel_1, text='Input and Output', width = 300, height=100)
    dashboard_pane_1 = ttk.Labelframe(panel_1, text='Dashboard Time Period', width = 300, height=300)
    filter_pane_1 = ttk.Labelframe(panel_1, text='Filter by Growth Rate', width = 300, height=300)

    #pack em!
    panel_1.pack(fill=BOTH, expand=1)
    file_pane_1.pack(fill=BOTH, expand=1)
    dashboard_pane_1.pack(side = LEFT,  fill = Y)
    filter_pane_1.pack(side = RIGHT, fill = Y)

    #tab 3 panes
    panel_2 = ttk.Panedwindow(tab2, orient=VERTICAL)
    about_pane = ttk.Labelframe(panel_2, text='About', width = 300, height=200)
    description_pane = ttk.Labelframe(panel_2, text='Description', width = 300, height=200)
    citation_pane = ttk.Labelframe(panel_2, text='Citations', width = 300, height=200)

    #pack em!
    panel_2.pack(fill=BOTH, expand=1)
    about_pane.pack(fill=BOTH, expand=1)
    description_pane.pack(fill=BOTH, expand=1)
    citation_pane.pack(fill=BOTH, expand=1)

    #labels for tab 2 (help)
    dashboard_help = ttk.Label(dashboard_pane_1, text= """Choose what units of
    time to create a
    dashboard for.
    Note that time
    periods are most
    recent, not a 
    defined quarter or
    calendar year.""")

    io_help = ttk.Label(file_pane_1, text="""Output file: the first words to use in the naming of the output files
    Input file: the BW output that you want to analyze.
    See documentation for required format.""")
    dashboard_help.pack()
    io_help.pack(fill = BOTH)

    #labels for tab 3 (about)
    about_section = ttk.Label(about_pane, text="""The FM Risers and Fallers project was created by Jeremy Barnes
    from May 2016 - July 2016 as a way to identify highest growing
    parts.
    Business logic was created based upon discussions with
    Daniel DiTommasso, David Enochs, Alex Miles
    and Robert Pietrowsky.
    """)
    description_section = ttk.Label(description_pane, text="""The FM Risers and Fallers application loads BW output from a
    specific format, performs seasonal adjustment on the data,
    derives information from the dataand then outputs all
    derived information and dashboards with ranking information.
    """)
    citations_section = ttk.Label(citation_pane, text="""The FM Risers and Fallers project was created using
    x13-ARIMA-SEATS by the US Census Bureau
    pandas
    statsmodels

    Download links are available in the documentation
    """)
    #pack em
    about_section.pack(fill=BOTH, expand=1)
    description_section.pack(fill=BOTH, expand=1)
    citations_section.pack(fill=BOTH, expand=1)

    #file entry

    #output
    #output = Frame(file_pane_0, width = 300, height=50)
    output_label = ttk.Label(file_pane_0, text="Output file").grid(column=1, row=1, sticky=W)
    outfile_entry = ttk.Entry(file_pane_0, width = 50, textvariable = outfile).grid(column=2, row=1, sticky=W)
    # output.pack(side = TOP, fill = X)
    # output_label.pack(side = LEFT)
    # outfile_entry.pack(side = RIGHT)

    #input
    #input = Frame(file_pane_0, width = 300, height=50)
    input_label = ttk.Label(file_pane_0, text="Input file").grid(column=1, row=2, sticky=W)
    infile_entry = ttk.Entry(file_pane_0, width = 50, textvariable = infile).grid(column=2, row=2, sticky=W)
    #cbutton.grid(row=10, column=3, sticky = W + E)
    bbutton= Button(file_pane_0, text="Browse", command= browsecsv).grid(column = 3, row = 2)
    # input.pack(side = BOTTOM, fill = X)
    # input_label.pack(side = LEFT)
    # infile_entry.pack(side = RIGHT)

    #top_x
    top_x_label = ttk.Label(top_x_frame_0, text="Risers/Fallers to identify").grid(column=1, row=2, sticky=W)
    top_x_entry = ttk.Entry(top_x_frame_0, width = 7, textvariable = top_x).grid(column=2, row=2, sticky=W)

    #dashboard times
    monthly = Checkbutton(dashboard_pane_0, text = "Month", variable = month).grid(row = 1, sticky=W, pady=1)
    threemonthly = Checkbutton(dashboard_pane_0, text = "3 Months", variable = threemonth).grid(row = 2, sticky=W, pady=1)
    sixmonthly = Checkbutton(dashboard_pane_0, text = "6 Months", variable = sixmonth).grid(row = 3, sticky=W, pady=1)
    yearly = Checkbutton(dashboard_pane_0, text = "Year", variable = year).grid(row = 4, sticky=W, pady=1)

    #output options
    excel_button = Checkbutton(output_options_frame, text = "Excel", variable = excel).grid(row = 1, sticky=W, pady=1)
    tableau_button = Checkbutton(output_options_frame, text = "Tableau", variable = tableau).grid(row = 2, sticky=W, pady=1)
    onecat_button = Checkbutton(output_options_frame, text = "One Category", variable = onecat).grid(row = 3, sticky=W, pady=1)
    multicat_button = Checkbutton(output_options_frame, text = "Many Categories", variable = multicat).grid(row = 4, sticky=W, pady=1)
    external_button = Checkbutton(output_options_frame, text = "External Report", variable = external).grid(row = 5, sticky=W, pady=1)

    #growth rate stuff
    growth_input_label = ttk.Label(filter_pane_0, text="Analyze only top: ").grid(row = 1,  column = 1, sticky=W, pady = 4, padx = 4)
    growth_input = ttk.Entry(filter_pane_0, width = 7, textvariable = filter_val).grid(row = 1,  column = 2, sticky=W, pady = 4, padx = 4)
    # Radiobutton(filter_pane_0, text = "Standard Deviations", variable = stddev, value = 1).grid(row = 2, column = 1, sticky=W, pady = 4)
    # Radiobutton(filter_pane_0, text = "Percentage", variable = stddev, value = 0).grid(row = 2, column = 2, sticky=W, pady = 4)
    #growth_default_label = ttk.Label(filter_pane_0, text="(Leave blank for default").grid(row = 3, column = 1, sticky=W, pady = 4, padx = 4)
    percent_label = ttk.Label(filter_pane_0, text=" %").grid(row = 1,  column = 3, sticky=W, pady = 4, padx = 4)
    #launch button
    run_buttom_frame = Frame(panel_0, width = 300, height=50)

    run_button = Button(run_buttom_frame, text = "RUN ANALYSIS", command = run)
    run_button.pack()
    run_buttom_frame.grid(row = 3, column = 0, columnspan = 2, pady = 4)
    root.mainloop()

两个或多个进程不能共享一个根 window。每个创建小部件的进程都将获得自己的 window.

我通过一些实验设法解决了这个问题。

在我在同一进程中启动业务逻辑后,它不再启动多个 windows。