【问题标题】:Building tkinter GUI to track time构建 tkinter GUI 以跟踪时间
【发布时间】:2022-01-18 04:13:04
【问题描述】:

我正在尝试构建一个 GUI 来跟踪我在不同项目上工作的时间。我一直在使用 tkinter,并且希望每个具有启动/停止/重置按钮的项目都有一个不同的框架。除非我理解错了,要准确地按项目跟踪时间,每个项目框架都需要自己独立的功能。

由于有很多重复的代码,我认为我需要遍历项目列表来定义与每个帧相关的启动、停止和重置函数,以及定义每个帧的代码。这是正确的还是有更好的方法来做到这一点?

这是我的代码,它正在努力获得我想要的输出和 GUI:

## defining projects and color palette
project_list = ['Project 0', 'Project 1']
color_palette = ['#1e2839','#283347','#434e61','#647187','#c4c5c9']

## defining functions

#### functions for project 0
def start_func_0():
    global starttime_0
    starttime_0 = datetime.now()

def stop_func_0():
    global starttime_0
    stoptime_0 = datetime.now()
    diff_0 = stoptime_0 - starttime_0
    time_entry = [date,project_list[0],starttime_0,stoptime_0,diff_0]
    df.loc[len(df)] = time_entry

def reset_func_0():
    df.drop(df[df['Project'] == project_list[0]].index, inplace = True)

#### functions for project 1
def start_func_1():
    global starttime_1
    starttime_1 = datetime.now()

def stop_func_1():
    global starttime_1
    stoptime_1 = datetime.now()
    diff_1 = stoptime_1 - starttime_1
    time_entry = [date,project_list[1],starttime_1,stoptime_1,diff_1]
    df.loc[len(df)] = time_entry

def reset_func_1():
    df.drop(df[df['Project'] == project_list[1]].index, inplace = True)

## building window

#### creating new time tracking df
df = pd.DataFrame(columns=(['Date', 'Project', 'Start Time', 'Stop Time', 'Time Difference']))

#### defining date
date = date.today()

#### instantiating window
window = tk.Tk()
window.title("Time Tracking ")
length = 100+(100*len(project_list))
window.geometry("250x"+str(length))
 
#### frame 0
frame_0 = tk.Frame(master=window, height=100, bg=color_palette[0])

label_0 = tk.Label(master=frame_0, text=project_list[0], justify='left', bg=color_palette[0], fg='white')
label_0.place(x=10, y=10, width=60, height=20)

start_0 = tk.Button(frame_0, text="Start", command=lambda : start_func_0(), bg=color_palette[0])
start_0.place(x=10, y=45, width=70, height=20)

stop_0 = tk.Button(frame_0, text="Stop", command=lambda : stop_func_0(), bg=color_palette[0])
stop_0.place(x=90, y=45, width=70, height=20)

reset_0 = tk.Button(frame_0, text="Reset", command=lambda : reset_func_0(), bg=color_palette[0])
reset_0.place(x=170, y=45, width=70, height=20)

frame_0.pack(fill=tk.X)

#### frame 1
frame_1 = tk.Frame(master=window, height=100, bg=color_palette[1])

label_1 = tk.Label(master=frame_1, text=project_list[1], justify='left', bg=color_palette[1], fg='white')
label_1.place(x=10, y=10, width=60, height=20)

start_1 = tk.Button(frame_1, text="Start", command=lambda : start_func_1(), bg=color_palette[1])
start_1.place(x=10, y=45, width=70, height=20)

stop_1 = tk.Button(frame_1, text="Stop", command=lambda : stop_func_1(), bg=color_palette[1])
stop_1.place(x=90, y=45, width=70, height=20)

reset_1 = tk.Button(frame_1, text="Reset", command=lambda : reset_func_1(), bg=color_palette[1])
reset_1.place(x=170, y=45, width=70, height=20)

frame_1.pack(fill=tk.X)

df

这是我编写的一些代码,试图循环创建函数和框架,但只有最后一个项目被捕获,我不知道为什么。

#### defining functions
for i in range(len(project_list)-1):
    def start_func_i():
        global starttime_i
        starttime_i = datetime.now()

    def stop_func_i():
        global starttime_i
        stoptime_i = datetime.now()
        diff_i = stoptime_i - starttime_i
        time_entry = [date,project_list[0],starttime_i,stoptime_i,diff_i]
        df.loc[len(df)] = time_entry

    def reset_func_i():
        df.drop(df[df['Project'] == project_list[0]].index, inplace = True)

#### defining frames
for i in range(len(project_list)-1):
    frame_i = tk.Frame(master=window, height=100, bg=color_palette[i])

    label_i = tk.Label(master=frame_i, text=project_list[0], justify='left', bg=color_palette[i], fg='white')
    label_i.place(x=10, y=10, width=60, height=20)

    start_i = tk.Button(frame_i, text="Start", command=lambda : start_func_i(), bg=color_palette[i])
    start_i.place(x=10, y=45, width=70, height=20)

    stop_i = tk.Button(frame_i, text="Stop", command=lambda : stop_func_i(), bg=color_palette[i])
    stop_i.place(x=90, y=45, width=70, height=20)

    reset_i = tk.Button(frame_i, text="Reset", command=lambda : reset_func_i(), bg=color_palette[i])
    reset_i.place(x=170, y=45, width=70, height=20)

    frame_i.pack(fill=tk.X)

第一部分的代码可以按照我想要的方式与当前代码一起工作,但我知道有一种更 Python 的方法可以做到这一点。

【问题讨论】:

  • 定义一个包含您想要的功能的类,然后将该类实例化为包含该类实例的列表中的列表项。
  • 嗨,吉姆,感谢您的洞察力!我对类不熟悉,您能指导我吗?或者您有任何可以构建的初步代码吗?

标签: python pandas loops tkinter


【解决方案1】:

我很高兴看到您的问题。我采用了您的代码和完全不同的方法。我相信类是 python 编程的一个最重要的方面,尤其是在处理 tkinter gui 时。 python类的描述有几种,比如 this,那么,我提供给你以下。

    import tkinter as tk
from  datetime import datetime
from datetime import timedelta
from collections import defaultdict
''' Thre are a couple of of problems with the code you revealed.
    1.  You didn't include your import statements so I had to 
        assume that the variable df is a pandas data frame. 
        since I have never used this function, I have chosen in this
        answer to use a simpler approach to saving the information from the windows.
    2.  In your  loop creating the frames, you create several variables ending in ..._i 
        Be aware that the control variable i is not substitited in the variable name as 
        you wish.   e.g. label_i is label_i, not label_0, label_1 as you wish.  This is the 
        reason you build the same frame each iteration of the loop
    
    In my example, I will create a project class to collect the information.  This class will have 
    a controller parameter which is represents a function which accumulates the information
    which is being collected.  
        
'''   
class Project:
    def __init__(self,parent,controller,project_number):
        # the parent is the "master window" into which the project windows
        # are placed.
        self.controller = controller
        self.parent = parent
        self.project_number = project_number
        self.make_frame()
        
    def make_frame(self):
        con = self.controller
        dex = self.project_number
        
        frame = tk.Frame(master=self.parent, height=100, bg=con.color_palette[dex])
        label = tk.Label(master=frame, text=con.project_list[dex], 
                         justify='left', bg=con.color_palette[dex], fg='white')
        label.place(x=10, y=10, width=60, height=20)
        start_button = tk.Button(frame, text="Start", 
                                 command=lambda : self.start_func(),
                                 bg=con.color_palette[dex])
        start_button.place(x=10, y=45, width=70, height=20)
        stop_button = tk.Button(frame, text="Stop", 
                                command=lambda : self.stop_func(), 
                                bg=con.color_palette[dex])
        stop_button.place(x=90, y=45, width=70, height=20)
        reset_button = tk.Button(frame, text="Reset", 
                                 command=lambda : self.reset_func(), 
                                 bg=con.color_palette[dex])
        reset_button.place(x=170, y=45, width=70, height=20)
        frame.pack(fill=tk.X)
    def start_func(self):
        self.starttime = datetime.now()
        
    def stop_func(self):
        stoptime = datetime.now()
        diff = stoptime - self.starttime
        time_entry = [datetime.today,self.starttime,stoptime,diff]
        self.controller.df[self.project_number].append(time_entry)
        # using a dictionary to hold a list of the start and stop times 

        def reset_func():
            self.controller.df.pop(self.project_num)
            
class display(tk.Tk):
    #This class adds function to the tkinter Tk class
    def __init__(self,project_list):
        self.df = defaultdict(list)
        tk.Tk.__init__(self)   # initialize the window
        self.title("Time Tracking ")
        length = 100+(100*len(project_list))
        self.geometry("250x"+str(length))
        self.frames = []
        self.color_palette = color_palette
        self.project_list = project_list
        for i,fr in enumerate(project_list) :
            self.frames.append(Project(self,  # master of frame
                                       self,  # controller function of frame (happens to be same)
                                       i))
        
if __name__=="__main__" :
    project_list = ['Project 0', 'Project 1']
    color_palette = ['#1e2839','#283347','#434e61','#647187','#c4c5c9']
    root = display(project_list)
    root.mainloop()
                
                

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    相关资源
    最近更新 更多