我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛
前言
昨天在朋友圈刷到了一个电脑装机助手
抖音视频,很搞笑,输入预算就能算出电脑价格,又去抖音看了其他很有趣的程序开发的视频,的确很有用
,跟手工耿大哥的手工有异曲同工之妙,那我也来做一个有用
的程序。
看看这个电脑装机助手
效果:
笑死了,哈哈~
代码
实现一个抽奖选择的程序,暂停后,为了反悔想再抽一次,加入了不管怎么样都直接关机的功能。身为一个后端程序员,又不怎么会写前端,只好用Python做GUI界面了,技术栈为Python3自带的Tkinter。只有一个单文件和存放图片的文件夹,只需要额外安装pillow
和pyinstaller
两个包。
下面直接亮代码(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
同级,才能正确启动 ,此时的目录结构:
直接点dist/app.exe
就可以直接启动了
打包体积非常大,不清楚什么情况
效果展示&总结
目前可以设置8个随机粽子,也可以设置4个粽子,前提是图片要对应上在构造函数中设置的列表元素名字,有兴趣可以改到更多,但是需要修改组件位置 。
可以在每天中午吃饭纠结的时候打开这个程序,不会像其他程序一样还可以给我第二次机会,效果立竿见影!强烈推荐!!!
效果展示:
代码开源在我的GitHub:draw-tkinter
感谢观看,谢谢,觉得有趣有用给个赞支持一下~ 哈哈~