python - tkinter 中的进度条不工作

标签 python tkinter progress-bar

我正在编写一个小应用程序来复制一些文件。我几乎做了我想要的所有东西,除了三件事:

1) 复制选项移动时移动的进度条。我可以显示它,但它没有反应。

我用这个来展示它:

self.p = ttk.Progressbar(self, orient=HORIZONTAL, length=300, mode='indeterminate')
self.p.grid(row=5)

然后在按下按钮时调用的另一个定义中启动它:

 self.p.start()

 shutil.copytree(self.source_direcotry0, self.cam0)
 shutil.copytree(self.source_direcotry1, self.cam1)
 shutil.copytree(self.source_direcotry2, self.cam2)

 self.p.stop()

不幸的是,复制发生了,但栏根本没有移动。

2)第二个问题与我在应用程序窗口底部显示的信息栏有关:

self.status = Label(self.master, text="Waiting for process to start...", bd=1, relief=SUNKEN, anchor=W)
self.status.pack(side=BOTTOM, fill=X)

然后当在它的开头调用相同的复制定义时,我有这个:

self.status['text'] = "Files are being copyied, have patience ;)".format(self.status)

并且状态没有改变,这很奇怪,因为在这个 def 的末尾我也有相同的命令来改变状态,并且这个命令有效:

self.status['text'] = "Files have been copyied".format(self.status)

3)我似乎无法附加图片我已经检查了各种不同的选项,但它们似乎都不起作用,这里呈现的选项似乎试图显示一些东西(窗口变大),但图片是不可见:

 self.img = ImageTk.PhotoImage(Image.open("az.png"))
 self.panel = Label(self, image=self.img, bg="#E6E6E6")
 self.display = self.img
 self.panel.grid(row=8)

我有点不确定为什么会这样,以防万一,为了了解更多信息,我在这里发布了完整的代码:

from tkinter import *
from tkinter import ttk
import re
from tkinter import messagebox
from tkinter import filedialog
import ntpath
import os
import shutil
import tkinter.filedialog as fdialog
from send2trash import send2trash
from PIL import Image, ImageTk

#os.system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''')


# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)

class Window(Frame):

    # Define settings upon initialization. Here you can specify
    def __init__(self, master=None):

        # parameters that you want to send through the Frame class. 
        Frame.__init__(self, master, bg="#E6E6E6")   

        #reference to the master widget, which is the tk window                 
        self.master = master

        #with that, we want to then run init_window, which doesn't yet exist
        self.init_window() 

    def copyy(self):

        self.status['text'] = "Files are being copyied, have patience ;)".format(self.status)


        self.source_direcotry0= '/Volumes/CAM0/DCIM/100HDDVR'
        self.source_direcotry1= '/Volumes/CAM1/DCIM/100HDDVR'
        self.source_direcotry2= '/Volumes/CAM2/DCIM/100HDDVR'
        self.source_direcotry3= '/Volumes/CAM3/DCIM/100HDDVR'
        self.source_direcotry4= '/Volumes/CAM4/DCIM/100HDDVR'
        self.source_direcotry5= '/Volumes/CAM5/DCIM/100HDDVR'
        self.source_direcotry6= '/Volumes/CAM6/DCIM/100HDDVR'
        self.source_direcotry7= '/Volumes/CAM7/DCIM/100HDDVR'
        self.source_direcotry8= '/Volumes/CAM8/DCIM/100HDDVR'
        self.source_direcotry9= '/Volumes/CAM9/DCIM/100HDDVR'
        self.source_direcotry10= '/Volumes/CAM10/DCIM/100HDDVR'
        self.source_direcotry11= '/Volumes/CAM11/DCIM/100HDDVR'

        self.path0="recording/CAM0"
        self.path1="recording/CAM1"
        self.path2="recording/CAM2"
        self.path3="recording/CAM3"
        self.path4="recording/CAM4"
        self.path5="recording/CAM5"
        self.path6="recording/CAM6"
        self.path7="recording/CAM7"
        self.path8="recording/CAM8"
        self.path9="recording/CAM9"
        self.path10="recording/CAM10"
        self.path11="recording/CAM11"

        self.cam0=os.path.join(self.Destination.get(), self.path0)
        self.cam1=os.path.join(self.Destination.get(), self.path1)
        self.cam2=os.path.join(self.Destination.get(), self.path2)
        self.cam3=os.path.join(self.Destination.get(), self.path3)
        self.cam4=os.path.join(self.Destination.get(), self.path4)
        self.cam5=os.path.join(self.Destination.get(), self.path5)
        self.cam6=os.path.join(self.Destination.get(), self.path6)
        self.cam7=os.path.join(self.Destination.get(), self.path7)
        self.cam8=os.path.join(self.Destination.get(), self.path8)
        self.cam9=os.path.join(self.Destination.get(), self.path9)
        self.cam10=os.path.join(self.Destination.get(), self.path10)
        self.cam11=os.path.join(self.Destination.get(), self.path11)

        self.p.start()

        shutil.copytree(self.source_direcotry0, self.cam0)
        shutil.copytree(self.source_direcotry1, self.cam1)
        shutil.copytree(self.source_direcotry2, self.cam2)
        # shutil.copytree(self.source_direcotry3, self.cam3)
        # shutil.copytree(self.source_direcotry4, self.cam4)
        # shutil.copytree(self.source_direcotry5, self.cam5)
        # shutil.copytree(self.source_direcotry6, self.cam6)
        # shutil.copytree(self.source_direcotry7, self.cam7)
        # shutil.copytree(self.source_direcotry8, self.cam8)
        # shutil.copytree(self.source_direcotry9, self.cam9)
        # shutil.copytree(self.source_direcotry10, self.cam10)
        # shutil.copytree(self.source_direcotry11, self.cam11)

        self.p.stop()

        self.status['text'] = "Files have been copyied".format(self.status)

    def deletee(self):
        send2trash('/Volumes/CAM0/DCIM')
        send2trash('/Volumes/CAM1/DCIM')
        send2trash('/Volumes/CAM2/DCIM')
        # send2trash('/Volumes/CAM3/DCIM')
        # send2trash('/Volumes/CAM4/DCIM')
        # send2trash('/Volumes/CAM5/DCIM')
        # send2trash('/Volumes/CAM6/DCIM')
        # send2trash('/Volumes/CAM7/DCIM')
        # send2trash('/Volumes/CAM8/DCIM')
        # send2trash('/Volumes/CAM9/DCIM')
        # send2trash('/Volumes/CAM10/DCIM')
        # send2trash('/Volumes/CAM11/DCIM')

        self.status['text'] = "Files have been moved to trash".format(self.status)


    def client_exit(self):
        exit()

    def about_popup(self):
        messagebox.showinfo("About", "This is software used to copy or delete files in bulk from the Absolute Zero VR camera")


    #Creation of init_window
    def init_window(self):

        self.Source=StringVar()
        self.Destination=StringVar()


        # changing the title of our master widget      
        self.master.title("AZ Data Extractor")

        # allowing the widget to take the full space of the root window
        self.pack(fill=BOTH, expand=1)

        #Creating the menu
        self.menubar = Menu(self.master)

        #Creating submenues
        self.filemenu = Menu(self.menubar, tearoff=0)
        self.filemenu.add_command(label="Exit", command=root.quit)
        self.menubar.add_cascade(label="File", menu=self.filemenu)

        self.helpmenu = Menu(self.menubar, tearoff=0)
        self.helpmenu.add_command(label="About", command=self.about_popup)
        self.menubar.add_cascade(label="Help", menu=self.helpmenu)

        #Displaying the menu
        root.config(menu=self.menubar)

        #Creating the  intro label
        l_instruction = Label(self, justify=CENTER, compound=TOP, text="Choose the destination for the copied files \n and press 'Go!' to start copyting", bg="#E6E6E6")
        l_instruction.grid(columnspan=2, ipady=10)

        l_instruction = Label(self, justify=CENTER, compound=TOP, text="Press 'Delete' to move all files \n from the camera to the trash", bg="#E6E6E6")
        l_instruction.grid(row=6, columnspan=2, ipady=10)

        # ttk.Style().configure('green/black.TButton', foreground='green', background='black')
        #Creating the button
        MyDestination=Entry(self, textvariable=self.Destination, bg="#E6E6E6")
        MyDestination.grid(row=2, columnspan=2, ipady=10)
        uploadButton = Button(self, text="Choose destination folder",command=lambda:self.Destination.set(fdialog.askdirectory()))
        uploadButton.grid(row=3, columnspan=2, ipady=10)
        goButton = Button(self, text="Go!",command=self.copyy)
        goButton.grid(row=4, columnspan=2, ipady=10)
        delButton = Button(self, text="Delete",command=self.deletee)
        delButton.grid(row=7, columnspan=2, ipady=10)

        self.p = ttk.Progressbar(self, orient=HORIZONTAL, length=300, mode='indeterminate')
        self.p.grid(row=5)

        self.img = ImageTk.PhotoImage(Image.open("az.png"))
        self.panel = Label(self, image=self.img, bg="#E6E6E6")
        self.display = self.img
        self.panel.grid(row=8)


        #resizing configuration
        self.grid_columnconfigure(0,weight=1)
        self.grid_columnconfigure(1,weight=1)
        self.grid_rowconfigure(0,weight=1)
        self.grid_rowconfigure(1,weight=1)
        self.grid_rowconfigure(2,weight=1)
        self.grid_rowconfigure(3,weight=1)
        self.grid_rowconfigure(4,weight=1)
        self.grid_rowconfigure(5,weight=1)
        self.grid_rowconfigure(6,weight=1)
        self.grid_rowconfigure(7,weight=1)
        self.grid_rowconfigure(8,weight=1)
        self.grid_rowconfigure(9,weight=1)
        self.grid_rowconfigure(10,weight=1)

        #status Bar
        self.status = Label(self.master, text="Waiting for process to start...", bd=1, relief=SUNKEN, anchor=W)
        self.status.pack(side=BOTTOM, fill=X)


# root window created. Here, that would be the only window, but you can later have windows within windows.
root = Tk()
root.resizable(width=False,height=False);
# root.configure(background='black');
# fm = Frame(root, width=300, height=200, bg="blue")
# fm.pack(side=TOP, expand=NO, fill=NONE)  
#root.geometry("230x340")



#creation of an instance
app = Window(root)

#mainloop 
root.mainloop()

编辑: 正如同时出现的其他问题一样,我似乎无法更改按钮的背景颜色和输入字段周围的框架。我查了一下可能是因为使用MacOS平台,可能是这样吗?有什么解决方法吗?

最佳答案

我压缩了在我的旧项目中工作的加载栏。我想出的唯一方法是在新线程中调用进度条处理调用,并将工作密集型函数从该线程调用到另一个新线程中。

使用单独的线程处理 UI 线程以外的 UI 元素(包括启动和停止进度条)是不好的做法;不过它确实有效,而且几个月来我一直在使用这个项目进行大量处理,而且零问题。

这里进度条在一个小脚本中工作,在 W10 64 位上使用 Python 3.5.2

from tkinter import *
import tkinter.ttk as ttk
import threading
import time

class Main_Frame(object):
    def __init__(self, top=None):
        # save root reference
        self.top = top
        # set title bar
        self.top.title("Loading bar example")

        # start button calls the "initialization" function bar_init, you can pass a variable in here if desired
        self.start_button = ttk.Button(top, text='Start bar', command=lambda: self.bar_init(2500))
        self.start_button.pack()

        # the progress bar will be referenced in the "bar handling" and "work" threads
        self.load_bar = ttk.Progressbar(top)
        self.load_bar.pack()

        # run mainloop
        self.top.mainloop()

    def bar_init(self, var):
        # first layer of isolation, note var being passed along to the self.start_bar function
        # target is the function being started on a new thread, so the "bar handler" thread
        self.start_bar_thread = threading.Thread(target=self.start_bar, args=(var,))
        # start the bar handling thread
        self.start_bar_thread.start()

    def start_bar(self, var):
        # the load_bar needs to be configured for indeterminate amount of bouncing
        self.load_bar.config(mode='indeterminate', maximum=100, value=0)
        # 8 here is for speed of bounce
        self.load_bar.start(8)
        # start the work-intensive thread, again a var can be passed in here too if desired
        self.work_thread = threading.Thread(target=self.work_task, args=(var,))
        self.work_thread.start()
        # close the work thread
        self.work_thread.join()
        # stop the indeterminate bouncing
        self.load_bar.stop()
        # reconfigure the bar so it appears reset
        self.load_bar.config(value=0, maximum=0)

    def work_task(self, wait_time):
        for x in range(wait_time):
           time.sleep(0.001)

if __name__ == '__main__':
    # create root window
    root = Tk()
    # call Main_Frame class with reference to root as top
    Main_Frame(top=root)

关于python - tkinter 中的进度条不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39509185/

相关文章:

python - 再次播放按钮 Tkinter Python 3

swift - 为什么我的进度条不起作用? - Swift,Parse,我正在为这个而烦恼

python - 在 Python 中修改和舍入 pandas 数据框中的数字

python - python Tkinter 中带有 strftime 的七段数字时钟与系统时间

python - Tkinter 网格管理器

android - 更改android中进度条背景的颜色

html - 进度条(下载)使用 HTML 5

python - 实时更新 pyplot 图表

python - 如何使用一个类方法来修饰其他类方法?

python - 将十六进制字节字符串列表转换为十六进制整数列表的最简单方法是什么?