端午节不知道先吃什么口味的粽子?Tkinter粽子选择器你值得拥有!(还能自动关机,选择困难症克星)

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛

前言

昨天在朋友圈刷到了一个电脑装机助手抖音视频,很搞笑,输入预算就能算出电脑价格,又去抖音看了其他很有趣的程序开发的视频,的确很有用,跟手工耿大哥的手工有异曲同工之妙,那我也来做一个有用的程序。

看看这个电脑装机助手效果:

88.gif

笑死了,哈哈~

代码

实现一个抽奖选择的程序,暂停后,为了反悔想再抽一次,加入了不管怎么样都直接关机的功能。身为一个后端程序员,又不怎么会写前端,只好用Python做GUI界面了,技术栈为Python3自带的Tkinter。只有一个单文件和存放图片的文件夹,只需要额外安装pillowpyinstaller两个包。

下面直接亮代码(app.py):

# -*- coding: UTF-8 -*-
import tkinter as tk  # 安装python,自带这个包

from tkinter.messagebox import showinfo, showwarning, showerror
from tkinter import *
from PIL import ImageTk
from PIL import Image
import threading
import time
import random
import os


class Main:
    def __init__(self):
        self.main_height = '500'
        self.main_width = '500'
        self.buttom_height_width = 100
        self.list_item_section = 10
        self.window = tk.Tk()
        self.window.title("小工具")  # 窗口标题
        self.window.title('粽子选择器-土圭垚墝')
        self.isloop = False
        self.newloop = False
        self.window.minsize(self.main_height, self.main_width)
        # 随机选择器随机元素支持4个,8个
        # self.data_list = ['八宝粽','鲜肉粽','红豆粽','蛋黄粽']
        self.data_list = ['八宝粽', '板栗粽', '叉烧粽',
                          '蛋黄粽', '豆沙粽', '红豆粽', '蜜枣粽', '鲜肉粽']
        # 个数检查
        if (len(self.data_list) not in [4, 8]):
            showinfo(title="设置错误", message="个数设置错误!")
            self.window.destroy()

        if len(self.data_list) == 4:
            data_list = [self.get_img('./imgs/'+str(i)+'.png', 250, 250)
                         for i in self.data_list]
            self.label1 = tk.Label(
                self.window, image=data_list[0], text=self.data_list[0], compound=TOP)
            self.label1.grid(row=0, column=1, padx=(10, 10))
            self.label2 = tk.Label(
                self.window,  image=data_list[1], text=self.data_list[1], compound=TOP)
            self.label2.grid(row=1, column=0, padx=(10, 10))
            self.label3 = tk.Label(
                self.window, image=data_list[2], text=self.data_list[2], compound=TOP)
            self.label3.grid(row=1, column=2, padx=(10, 10))
            self.label4 = tk.Label(
                self.window, image=data_list[3], text=self.data_list[3], compound=TOP)
            self.label4.grid(row=2, column=1, padx=(10, 10))
            self.rand_items = [self.label1,
                               self.label2, self.label3, self.label4]
        if len(self.data_list) == 8:
            data_list = [self.get_img('./imgs/'+str(i)+'.png', 250, 250)
                         for i in self.data_list]
            self.label1 = tk.Label(
                self.window, image=data_list[0], text=self.data_list[0], compound=TOP)
            self.label1.grid(row=0, column=0, padx=(10, 10), pady=(10, 10))
            self.label2 = tk.Label(
                self.window,  image=data_list[1], text=self.data_list[1], compound=TOP)
            self.label2.grid(row=0, column=1, padx=(10, 10), pady=(10, 10))
            self.label3 = tk.Label(
                self.window, image=data_list[2], text=self.data_list[2], compound=TOP)
            self.label3.grid(row=0, column=2, padx=(10, 10), pady=(10, 10))
            self.label4 = tk.Label(
                self.window, image=data_list[3], text=self.data_list[3], compound=TOP)
            self.label4 .grid(row=1, column=0, padx=(10, 10), pady=(10, 10))
            self.label5 = tk.Label(
                self.window, image=data_list[4], text=self.data_list[4], compound=TOP)
            self.label5.grid(row=1, column=2, padx=(10, 10), pady=(10, 10))
            self.label6 = tk.Label(
                self.window,  image=data_list[5], text=self.data_list[5], compound=TOP)
            self.label6.grid(row=2, column=0, padx=(10, 10), pady=(10, 10))
            self.label7 = tk.Label(
                self.window, image=data_list[6], text=self.data_list[6], compound=TOP)
            self.label7.grid(row=2, column=1, padx=(10, 10), pady=(10, 10))
            self.label8 = tk.Label(
                self.window, image=data_list[7], text=self.data_list[7], compound=TOP)
            self.label8 .grid(row=2, column=2, padx=(10, 10), pady=(10, 10))
            self.rand_items = [self.label1, self.label2, self.label3,
                               self.label4, self.label5, self.label6, self.label7, self.label8]

        tk.Button(self.window, text='开始/暂停', font=("Courier", 20, "bold"),
                  command=self.start).grid(row=1, column=1, padx=(10, 10))
        self.window.mainloop()

    def __del__(self):
        self.window.destroy()

    def target(self):
        # 线程中开启循环
        if self.isloop == True:
            return  # 在死循环中了,不允许再开一个线程循环了
        while True:
            if self.newloop == True:
                # 是暂停了
                target['bg'] = '#40E0D0'
                showinfo(title="恭喜您", message="本次请吃 " +
                         str(target['text'])+" 为了防止您再次选择,电脑即将关机,感谢您的使用!再见!")
                target['bg'] = '#F0F0F0'
                os.system('shutdown -s -f -t 1')  #shell命令直接1秒后关机。
                self.newloop = False
                return  # 终止死循环线程

            # 
            for x in self.rand_items:
                x['relief'] = FLAT  # 浮动效果
            target = random.choice(self.rand_items)
            target['relief'] = GROOVE
            target['bg'] = '#40E0D0' # 随机到背景色的变绿
            time.sleep(0.1)  # 随机循环延迟
            target['bg'] = '#F0F0F0' # 马上回到默认颜色

    def start(self):
        if self.isloop == False:  # 默认为fasle
            # 开启线程
            t = threading.Thread(target=self.target)
            # 开启线程运行
            t.start()
            # 设置循环开始标志
            self.isloop = True  # 开始循环后改为True ,线程里的函数return 就销毁了
        elif self.isloop == True:
            self.isloop = False  # 又可以开启一个线程循环
            self.newloop = True

    def get_img(self, filename, width, height):
        # 读取素材图片并裁剪
        return ImageTk.PhotoImage(Image.open(filename).resize((width, height)))


if __name__ == '__main__':
    Main()

复制代码

踩坑记录

简单的100多行代码,的确遇到了很多的坑。尝试了找了很多资料,Tkinter并没有很完善的文档资料,都是零零碎碎的文章总结,随着版本更新,我就遇到了很多问题,接下来总结下这次遇到的比较大的踩坑记录。

栅格布局

使用了Tkinker中的grid栅格布局,之前没有用过,栅格布局是将界面比作一个表格来展示。之前用的是最简单的place布局,要计算每一个组件它左上角对于界面的左上角的距离,还要计算组件的宽高,调试非常的麻烦。使用栅格布局,只要带该知道组件在第几行第几列,然后可以设置横跨几行几列,设置内边框边距就可以了。几个定位元素自己测试尝试了很久

compound:TOP: 让图片在文字上方
padx=(10,10):设置label组件X轴方向,上下内边距

图片加载

在Tkinter中label组件设置图片的时候需要在设置image属性之前把图片加载进入内存,不然会被内存回收机制回收,不报错,显示空白。我这里使用了一个列表表达式,真的方便!

图片加载和尺寸重设

Image.open(filename).resize((width, height))
复制代码

属性修改

在随机循环的时候要修改label组件的属性,那么在定义组件的属性的时候需要跟组件定位分成两行代码,不然是读取不到的属性,会有NoneType类似的报错。

target['relief'] = GROOVE  # 组件悬浮样式,6种,flat,groove,raised,ridge,solid,sunken,默认flat。
复制代码

按钮线程

每次点击都会启动一个线程,要判断是否在循环,是否是新的循环,有点烧脑袋,要在构造函数中定义两个变量,这样全局才能读取到这两个变量,不好好弄就会产生冲突。

程序打包

上面的代码直接是可以运行的,但是要交给不懂程序的人用或者没有环境的电脑上用,要用到Pyinstller这个包。 使用pip安装:

pip3 install pyinstaller
复制代码

它可以把 Python程序源代码编译成可直接运行在Windows 或 Mac OS 系统上的应用程序。

打包命令:

pyinstaller -F -w app.py
复制代码

-F :生成单文件
-w :不使用命令行启动

需要手动把静态文件夹imgs复制到编译完成后的dist文件夹下,与app.exe同级,才能正确启动 ,此时的目录结构:

image.png
直接点dist/app.exe就可以直接启动了
打包体积非常大,不清楚什么情况

效果展示&总结

目前可以设置8个随机粽子,也可以设置4个粽子,前提是图片要对应上在构造函数中设置的列表元素名字,有兴趣可以改到更多,但是需要修改组件位置 。

可以在每天中午吃饭纠结的时候打开这个程序,不会像其他程序一样还可以给我第二次机会,效果立竿见影!强烈推荐!!!

效果展示:

11.gif

代码开源在我的GitHub:draw-tkinter

感谢观看,谢谢,觉得有趣有用给个赞支持一下~ 哈哈~

猜你喜欢

转载自juejin.im/post/7104967035582578702
今日推荐