当前位置:   article > 正文

Python中 tkinter 进度条的实现 不同python文件之间传递进度信息_tkinter文件拷贝进度条

tkinter文件拷贝进度条

1.前情提要

一直想用python的 tkinter模块实现类似QT中信号和槽的功能,但是在网上没有找到合适的方法(也可能是自己没有找到)。尝试着将界面代码中的 self 作为参数传入功能代码中,最终实现了。本人使用python3.7.4, 32位版本。

2. 1单个进度条

为了展示tkinter进度条的实现,写了两个python文件,一个是UICode.py,该文件是UI界面代码。一个是WorkCode.py,该文件是功能代码。

2.1.1 UICode.py代码

代码中起主要作用的代码:

def run(self):
        self.progressbarOne['value'] = 0
        WorkCode.progressbar_func(self.num, self)      # 需要将self作为参数传入,才可以实时更改UI实例中进度条的值
  • 1
  • 2
  • 3

整体代码如下:

# -*- coding:utf-8 -*-
import tkinter as tk
import tkinter.ttk
import WorkCode

class GuiSample(object):
    def __init__(self):
        self.root = tk.Tk()
        # 设置GUI界面属性
        self.root.title('tkinter 进度条测试')  # 设置GUI标题
        self.root.wm_attributes("-alpha", 1.0)  # 设置GUI透明度(0.0~1.0)
        self.root.wm_attributes("-topmost", True)  # 设置GUI置顶
        # self.root.wm_attributes("-toolwindow", True)  # 设置为工具窗口(没有放大和缩小按钮)
        # self.root.overrideredirect(-1)  # 去除GUI边框(GUI标题、放大缩小和关闭按钮都会消失)
        # self.bind_window_move_events()  # 如果去除GUI边框了,就要绑定窗口移动事件,否则GUI无法移动
        self.width = 350
        self.height = 150
        ws = self.root.winfo_screenwidth()
        hs = self.root.winfo_screenheight()
        x = (ws / 2) - (self.width / 2)
        y = (hs / 2) - (self.height / 2)
        self.root.geometry('%dx%d+%d+%d' % (self.width, self.height, x, y))

        # 设置类中的全局变量
        self.num = 30

        # 设置所有窗口部件
        self.build_label()
        self.build_button()
        self.build_progressbarOne()

        # 执行所有窗口部件
        self.label1.place(x=115, y=30, anchor=tk.W)
        self.progressbarOne.place(x=55, y=60, anchor=tk.W)
        self.test_button.place(x=155, y=100, anchor=tk.W)

    def run(self):
        self.progressbarOne['value'] = 0
        WorkCode.progressbar_func(self.num, self)      # 需要将self作为参数传入,才可以实时更改UI实例中进度条的值
        
    def build_progressbarOne(self):
        self.progressbarOne = tk.ttk.Progressbar(self.root, length=240)
        # 进度值最大值
        self.progressbarOne['maximum'] = 100
        # 进度值初始值
        self.progressbarOne['value'] = 0

    def build_label(self):
        self.label1 = tk.Label(self.root, text="这是一个测试进度条")

    def build_button(self):
        self.test_button = tk.Button(self.root, text='开 始', command=self.run)

if __name__ == '__main__':
    progressbarSample = GuiSample()
    progressbarSample.root.mainloop()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

2.1.2 WorkCode.py代码

起到实时传递任务进度的代码:

def get_progress(num, leng, self):     # 该函数起到实时更新进度条的作用,可以看做是一个信号函数
    progressNum = 0
    if leng != 0:
        progressNum = num/leng
    self.progressbarOne['value'] = progressNum * 100    # 在这里设置进度条的值
    self.root.update()   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

整体代码如下:

import time

def progressbar_func(num, self):       # 该函数是实际的功能函数,在该函数中调用get_progress(),实时显示任务进度
    for i in range(0, num):
        time.sleep(0.1)
        get_progress(i, num-1, self)

def get_progress(num, leng, self):     # 该函数起到实时更新进度条的作用,可以看做是一个信号函数
    progressNum = 0
    if leng != 0:
        progressNum = num/leng
    self.progressbarOne['value'] = progressNum * 100    # 在这里设置进度条的值
    self.root.update()                                  # 在这里更新进度条的值

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.1.3. 实际效果

在这里插入图片描述

2.2 两个进度条

要实现两个进度条相互不受影响,需要采用线程,每一个进度条是一个线程。还是两个python文件,一个是UICode.py,该文件是UI界面代码。一个是WorkCode.py,该文件是功能代码。

2.2.1UICode.py代码

“self”参数传递过程:使用GuiSample类run函数开启线程,将“self”传入线程,在线程中调用功能代码时,“self”参数通过线程中的run函数传入功能代码。

GuiSample类run函数:

def run(self, barNum):
    t = MyThread(self.num, self, barNum)
    t.start()
  • 1
  • 2
  • 3

线程中的run函数:

def run(self):
	while self.__running.isSet():
       self.__flag.wait() # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
       WorkCode.progressbar_func(self.num, self.handle, self.barNum)
       self.stop()
  • 1
  • 2
  • 3
  • 4
  • 5

def build_button(self):代码不能按照如下形式来写,因为“command=self.run(1)”会在没有点击按钮时直接调用调用run函数,需要通过lambda表达式调用run函数。

def build_button(self):
        self.test_button = tk.Button(self.root, text='开 始', command=self.run(1))
        self.test_button2 = tk.Button(self.root, text='开 始', command=self.run(2))
  • 1
  • 2
  • 3

整体代码:

# -*- coding:utf-8 -*-
import tkinter as tk
import tkinter.ttk
import WorkCode
import threading

class MyThread(threading.Thread):
    def __init__(self, num, handle, barNum):
        super().__init__()
        self.__flag = threading.Event()  # 用于暂停线程的标识
        self.__flag.set()  # 设置为True
        self.__running = threading.Event()  # 用于停止线程的标识
        self.__running.set()  # 将running设置为True
        self.num = num
        self.handle = handle
        self.barNum = barNum

    def run(self):
        while self.__running.isSet():
            self.__flag.wait() # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
            WorkCode.progressbar_func(self.num, self.handle, self.barNum)
            self.stop()

    def pause(self):
        self.__flag.clear() # 设置为False, 让线程阻塞

    def resume(self):
        self.__flag.set() # 设置为True, 让线程停止阻塞

    def stop(self):
        self.__flag.set() # 将线程从暂停状态恢复, 如何已经暂停的话
        self.__running.clear() # 设置为False

class GuiSample(object):
    def __init__(self):
        super().__init__()
        self.root = tk.Tk()

        # 设置GUI界面属性
        self.root.title('tkinter 进度条测试')  # 设置GUI标题
        self.root.wm_attributes("-alpha", 1.0)  # 设置GUI透明度(0.0~1.0)
        self.root.wm_attributes("-topmost", True)  # 设置GUI置顶
        # self.root.wm_attributes("-toolwindow", True)  # 设置为工具窗口(没有放大和缩小按钮)
        # self.root.overrideredirect(-1)  # 去除GUI边框(GUI标题、放大缩小和关闭按钮都会消失)
        # self.bind_window_move_events()  # 如果去除GUI边框了,就要绑定窗口移动事件,否则GUI无法移动

        self.width = 350
        self.height = 280
        ws = self.root.winfo_screenwidth()
        hs = self.root.winfo_screenheight()
        x = (ws / 2) - (self.width / 2)
        y = (hs / 2) - (self.height / 2)
        self.root.geometry('%dx%d+%d+%d' % (self.width, self.height, x, y))

        # 设置类中的全局变量
        self.num = 30
        self.barNum = 0

        # 设置所有窗口部件
        self.build_label()
        self.build_button()
        self.build_progressbarOne()

        # 执行所有窗口部件
        self.label1.place(x=115, y=30, anchor=tk.W)
        self.progressbarOne.place(x=55, y=60, anchor=tk.W)
        self.test_button.place(x=155, y=100, anchor=tk.W)

        self.label2.place(x=115, y=160, anchor=tk.W)
        self.progressbarTwo.place(x=55, y=190, anchor=tk.W)
        self.test_button2.place(x=155, y=230, anchor=tk.W)

    def run(self, barNum):
        t = MyThread(self.num, self, barNum)
        t.start()

    def build_progressbarOne(self):
        self.progressbarOne = tk.ttk.Progressbar(self.root, length=240)
        # 进度值最大值
        self.progressbarOne['maximum'] = 100
        # 进度值初始值
        self.progressbarOne['value'] = 0

        self.progressbarTwo = tk.ttk.Progressbar(self.root, length=240)
        # 进度值最大值
        self.progressbarTwo['maximum'] = 100
        # 进度值初始值
        self.progressbarTwo['value'] = 0

    def build_label(self):
        self.label1 = tk.Label(self.root, text="这是第一个测试进度条")
        self.label2 = tk.Label(self.root, text="这是第二个测试进度条")

    def build_button(self):
        self.test_button = tk.Button(self.root, text='开 始', command=lambda :self.run(1))
        self.test_button2 = tk.Button(self.root, text='开 始', command=lambda :self.run(2))

if __name__ == '__main__':
    progressbarSample = GuiSample()
    progressbarSample.root.mainloop()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

2.2.2 WorkCode.py代码

import time


def progressbar_func(num, handle,barNum):       # 该函数是实际的功能函数,在该函数中调用get_progress(),实时显示任务进度
    for i in range(0, num):
        time.sleep(0.1)
        get_progress(i, num-1, self, barNum)


def get_progress(num, leng, self, barNum):     # 该函数起到实时更新进度条的作用,可以看做是一个信号函数
    progressNum = 0
    if leng != 0:
        progressNum = num/leng
    if barNum == 1:
        self.progressbarOne['value'] = progressNum * 100    # 在这里设置进度条的值
    if barNum == 2:
        self.progressbarTwo['value'] = progressNum * 100  # 在这里设置进度条的值
    self.root.update()                               # 在这里更新进度条的值

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2.2.3 实际效果

在这里插入图片描述

3. 代码存在的问题

在进度条没有完成进度之前,再次点击“开始”按钮,会出现显示不顺畅问题(如下动画所示),其实是两个进度重叠显示造成的。因为点击一次“开始”按钮,就会进行一次进度条的显示。代码没有能实现线程的暂停功能。

在这里插入图片描述

4. 参考资料

Python之Tkinter使用详解
Python之tkinter 进度条 Progressbar

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/761223
推荐阅读
相关标签
  

闽ICP备14008679号