From fed6c361a5c7ef0c21ce20840e150ccab2f35da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=A6=82=E5=A8=81?= Date: Wed, 22 Oct 2025 10:50:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 91 +++++++++++++++++++++++++++++ .python-version | 1 + README.md | 11 ++++ app.ico | Bin 0 -> 270398 bytes app.py | 147 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 6 files changed, 251 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 app.ico create mode 100644 app.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7eb2b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,91 @@ +# Byte-compiled / optimized / DLL files[1](@ref)[3](@ref)[8](@ref) +__pycache__/ +*.py[cod] +*$py.class +*.pyc +*.so + +# PyInstaller distribution / packaging[1](@ref)[3](@ref)[8](@ref) +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller specific files[1](@ref)[3](@ref)[8](@ref) +*.manifest +*.spec + +# Installer logs[1](@ref)[3](@ref)[8](@ref) +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports[1](@ref)[3](@ref)[8](@ref) +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Virtual environments[1](@ref)[2](@ref)[7](@ref) +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# macOS specific files[1](@ref)[2](@ref)[6](@ref) +.DS_Store +.AppleDouble +.LSOverride +Icon +._* +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# IDE and editor files[1](@ref)[4](@ref)[6](@ref) +.idea/ +.vscode/ +*.swp +*.swo +*~ +.ropeproject +.spyderproject +.spyproject + +# Other common files +*.log +*.tmp +*.bak diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..4eba2a6 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13.0 diff --git a/README.md b/README.md new file mode 100644 index 0000000..bb04e67 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +## 打包 + +在 windows 中打开 powersehll 执行下面命令 + +```shell +# 安装依赖 +pip3 install -r requirements.txt +# 打包 +python3 -m PyInstaller --onefile --windowed --icon=app.ico --name=ShutdownTimer app.py +``` + diff --git a/app.ico b/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2ebfeacc8315105d80e4efa2aac44f920e81074 GIT binary patch literal 270398 zcmeI5F^C+=6^2K;^XP&Fg94*uR|*sy!Wf~zse}-U#0HKWZEy%<6dXdBLn!b`ff0vL zU>sd=IJiIvV-5!ni~?P9I5>n+psl$;NN~a7K*%73Pn_-btC`)=ZqMu9_olmgs%F14 z&)Vs(sj2?!ef3`TRM!j!hva|pze9r)gZEFoJNSwG{r=>@U~qVHJiZ(Z-ktoz|1Yfq z@0|#z#LgwMQ%frx7lr~$paA?YK__wvC@`l2@IR+;ybT4GKmqt)f==WTP+(34;D1ix zcpC~VfdcTq1f9qwpun68!2g`W@ir7#0tMiI2|AHWK!G_Gfd4s#<83Ig1PZ|a5_BS$ zfC6(W0RM9e$J3hnc0Q`40-oQUN6{i6F!+&v3OM^R80RG{>GiRcr;uL^?_%F_B zX>ex>z(4$V=1f#noC5F<|HU~i4em?<_=o?_oQaBxQvm+qzc{C*!JR1p|M1_LGf`1- z3cx@77w5DzxKROFo4YRS^JVS2#lkfy|HxrahO6PJ+1A1fsr#kPMz0E8`y1CROWUp2 z=`Q!o(Lb>-rB5w=AIpUGMrYz=aSBMEUJw5P$S<8J-{N<^`-elxoIjwb}Eo(DlZ}b(1Z-sjnpWRhv zC0`O3(=yTfsAtXfcK_hJv%ZTa{cG~n>AOc4Ls`C`3jZ@P2#)*bcwWunzva*Z|L`w9 zFfV_Lejoh9|Dt`6&tKg50srt1e~aU9(b~g5{4d%E`TWI=KkyI#@V7Yr7Og$}!~ddv zkk4P-_yhm&4}Xi}Z_(PrKm0G+2l@QPjX&@Y|M0gs{uZr0{44y+8vS*#D@KcZuE#yP ztyb`Q{_$S)pNcLPyCdtyFI!FOJTG=Sk)5};BbJjk+!3=}V=PnhZ!WxX_^tT&__rvm zT&yc-6>(^|wz(nKddq0?+Z@HN|8mBk@$XPrb-C)?TmD!o=a6SKk9pc6{eIQh_fZS_ zUu9);BUOK$@DKmiyqNgEeDebMhky95_yGBzn*Rmn z|4VmY$TR*E|B3&>{JW^}9$l*YI^6&3YF-KdmDtbyUp@9mU5Nk0|5QVI(t5f7Ul%Vt z<3Ic#?EH`X|6un&{2%Q8hyR1!|L}jX`#&H4W$o8c?g6b>P3ErG#7bQM`Csul>pJAP zp7T2~*)Q5`zmc-m^`9TeTG~qvJ0opBnZl$EKNtI@)#MsKlsTZR>$X_a^8ad{U-z2) zzk~6D_)q*#6wkFBzv);$@n61kJtKD8YO-$iSqCgGpdZ3_lkb<~8q@tvu2a^zkN3Nd zdtO`Y6VWB^ceKB)hR>wVzdP(3srTDFEc{K{ecx!Z|F=oI9eqEv^vzBgsXIO|umE3b z`_*XTDfA~+z zUE5r#tM|h{{CDL`ZR;WatG<|Y9D;vUbK*boKPh)@$I7nW5C8Drl`plehxo7hV$yL4 z{#DJ1|HS{K+_fDmyLvzT!+%%4)V3btzv_!g$07JvH7EWP|C4gpcC75`{qPU}UHMYm zdWiq3FD4y_;9u38_}^9he=PcbWwY?-ruDwnbF#KHu3eAoSFP67H7!v+L9WHDU)1>% z+3Py~9)-!IeEiJ?dEUZg(w}W(NLTLpYx}bc;eM6zrq|Lp@{Ei2 znH)=~%UzG3OCQDGTd}UAm*1?(rSHN^yWfr;O1m$5bn%ppb^F?q)AK*@pN~&>!WR51 z&uGJcoo~TE{1g8@@xEG?_^V52ljizbKf^!#YkpC4jQl^N*j>{{t}z7u;osp$@DKmY|2^~lYFXz0%>OeQ zLx}&q$Nz9i`aiCtwc4wE{3U!LdqIC@wBJj)Usz4P|NBcm*Xhck9r;sc^~QbCSHnrO zt%VPypROA1PDcG18M5*w<-(NB%F8WHj{TVj8;=NiA{MU{N@ZbA*1ONIM1^=2Z#Q$Z>PvBqk z^BAM6al5P6#DC(y?sJP6Ti{>s3;5S`A^tCd(UhMF|C*l@|8<{(e_a>dhe{|5|9amM z|219UzXYZ|ZG?Z#&x!xK55d2#i|#`ul!br2?}-1JF7RIh)1EfMzvkz}f8B@RU)M$V zp%Ti%zutGme@z$oFM(-K8{uE`b8q~Yb&sd4CUsxeKhx81iz*w=%U;pPEGA>c8M#*3 z)7Na@WTeY?(w1Pi@P%Bb)gI+@ok27=_s{*$}d;Nqhf?(-~c->p7Rm zsFi1LIrtwv)O=2S1^)XV7vNvj9RBsOI}rcX=f#Qt)x1FB9PwY76A=H2|N7XPEXQxk zeTo0X|0e&aoFo40a~I-2@t^yDyL~`n9QXh9{ZF|6r^eic zujSX;*u^pUhkxgIR5wobecUtuhyR+q0RG`0{%3P$;(z~KF#lIIC;un^cg^WN^8(_( z%3R;MY}4n#Km5ag^|8I)1IiDl`6aR_-#D9Gak0;(& z%M$-Kr1Y_#f`q^j`?cI>K~>ubk}|0;9Mxu@oj>5jp_rY-!#zja{aBX#U(*Ku;h+3p&+Dy6$^UB^BLD9_E|ULi zagO+}=S#%@+OexH*Vm4d|Mx!5>He&E5bC+)uI(Zy3X*6nKx{P*ui z#s2;W{Ht@Q0RO5k-aIN0|J%UE`+t1@uSYQ3QR2U@6Y;+`rhCS7;(zw>KU|O)^1xv)gbj}m zBrF>~mv+m2I+J}gT-#i++jwp9YpL66DX+88`~TsMtj+za!+s_Ec@M27eIxxp`pRf= zy=}ri^37w0kRc$E0r_rSo%-hRMr5RraTNSa(b`iF@4}zyI-;E0urW{@sBfm@0sOb` zBjUfRA^fMCD^K}tJ3hd_>bov|;a}Ad{^8%l5%>QJ@E1qCf&T*fllZU3F3-5d{eRuh z;a}IqR94RebBA?~-nVl`R+d(E*vJMPb(n$uMEzmc}bXL_ubsvS~qvH5@0#HwE` z7d20)W6HY6zeQnvZsD4hw@JA$^sMPkSyuK7*bslLdj_t0Tz;D)rRE2v?xOiGlxMGR zjemot>i;#bH#br;nzC;A_xeQ_<>5d7I0FAlUO@csB>#bb_)i(t?R69XyNKuBI^ZAv z?Tn@@3IASpyC@I;@Sl=ud)@Hg1<&3(;2-|&jHWCJ|6X>xC=dVepOR{O-SFQ9&)z!V zAO7u(rYs5nUUs`E5C8C=l4^V1@ZSZ`-a6nP{_TvWEGggduZvwV+DFoF2W0)HmuY+X zAFYLlqPKNzsjJ+iZhKqu=diPDiEEJ^z8l2X&0MUfWHgt$MZcqU`(Z+i`JTnB8ixJJ8F|DxUf_FeQ9i%)Hr z3HPjiyIpRjB`!<%cGbj?lp31Ftn7sy`NPSp9ZV zZZn%b`g0jb;GeN6qwyL3dmb3zAO7LLXXrBeIQWNu_|FK)o|^*y@DKkzLzmIV!9V=N ze?~y|+!XkSfB5eix{N*!{^1|~GXk>brocb^!++1vW%P0I5C8C=5s*DM1^(e5{(FWl zqmPqyk%uQ^KwJmBzsdUF74es&b{p@xepj9wu-NAr`J!dLDrNRv*BqozO?@BzBmNeJ z*|(xET733xnQ%BG^bSN*q)+4TklZqw{4NIZc)2tf|0asH|F0YfC=Sp?!v8@Z&*1+6 z;XZSz@DKmS+%M(`#QzzB%02-9@DKlGGu)>2@DKm+--e*F-wXfn5C3H|+@|&L5C8Dr zhM=$GJ(w`(u*+x2@x+xIBf z>JauH7tH@-?V7Y>!si1q>-yYS?y{$kVxN}nm9)Mu1>nE4xdQz6<+OZGX9~bS{CDO| zR8*V-@DKmRIV}zDOab_Z|IVC=ii%SJ{^7qkr=`K2DFFZQ-{uKl~Txv^2Og z1>hh4J98!~Doz3ThyUW7mIimG0Q|#$XU;@L#VH``O5+~n$E+s%KcA5`x);+}xFl;` z<+%m3U6-=A6Ira=(-vvFRhMu<*5{wLTR3U$pGmneEY1;WFbX&okg@BOZ(Y3oIfq!c zvpEz;0Tk#_0r>AxY(5GFGE)HlGwTqVg91G&0RKIT%}1d?W(vT6W*tIvP@qQz;J-() z`6v{~Oab`MtV3uH3iPM|{P!p}AB6&$DFFYObqLKtfgTlr{~pEWqfj6-1>irk4#~VZ zTm09f0^bh?$Ll`s%eS6#NGrqnsX%k+^PlOP$A6ryDExfy>A~R0-U6Jj2;|b|f0Z|n zUw-^nVa_W8IrOPG_oTA3Q+=N+wacMt7jj+|$gj`1%cjn}^ZvJ$PkMycI|}&p*wJ(A z`JVUh=q=*kj3*>G>^ijP1?TZ%R^V5QVi} zrq9k~r~2;M`vH4PaDJk1YyGy9k-h2;&TiK(|Lojv*;C?U&hIGDR-c_&&MtkY&OP~z z=etgg|K$9R_~U=~)2HgFTF97(0X{{c9m BUabHC literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100644 index 0000000..193203b --- /dev/null +++ b/app.py @@ -0,0 +1,147 @@ +import tkinter as tk +from tkinter import ttk, messagebox +import threading +import os +import time + + +class ShutdownTimerApp: + def __init__(self, root): + self.root = root + self.root.title("定时关机程序") + self.root.geometry("400x280") + self.root.minsize(400, 280) # 设置最小尺寸防止窗口过小 + self.root.resizable(False, False) + + # 倒计时线程控制 + self.is_running = False + self.timer_thread = None + + self.create_widgets() + + def create_widgets(self): + # 主框架 + main_frame = ttk.Frame(self.root, padding="20") + main_frame.pack(fill=tk.BOTH, expand=True) + + # 时间输入框架 + time_frame = ttk.LabelFrame(main_frame, text="设置关机时间") + time_frame.pack(fill=tk.X, pady=10) + + # 时间输入和单位选择 + input_frame = ttk.Frame(time_frame) + input_frame.pack(fill=tk.X, pady=10) + + # 时间输入框 + ttk.Label(input_frame, text="时间:").grid(row=0, column=0, padx=5) + self.time_var = tk.StringVar(value="10") + self.time_entry = ttk.Entry(input_frame, textvariable=self.time_var, width=10) + self.time_entry.grid(row=0, column=1, padx=5) + + # 单位选择 + ttk.Label(input_frame, text="单位:").grid(row=0, column=2, padx=5) + self.unit_var = tk.StringVar(value="分钟") + self.unit_combo = ttk.Combobox(input_frame, textvariable=self.unit_var, values=["秒", "分钟", "小时"], state="readonly", width=8) + self.unit_combo.grid(row=0, column=3, padx=5) + + # 状态显示 + self.status_var = tk.StringVar(value="等待设置时间...") + self.status_label = ttk.Label(main_frame, textvariable=self.status_var, font=("Arial", 10)) + self.status_label.pack(pady=10) + + # 倒计时显示 + self.countdown_var = tk.StringVar(value="00:00:00") + self.countdown_label = ttk.Label(main_frame, textvariable=self.countdown_var, font=("Arial", 16, "bold")) + self.countdown_label.pack(pady=5) + + # 按钮框架 + button_frame = ttk.Frame(main_frame) + button_frame.pack(pady=15) + + # 启动按钮 + self.start_button = ttk.Button(button_frame, text="启动关机定时", command=self.start_shutdown_timer) + self.start_button.pack(side=tk.LEFT, padx=5) + + # 取消按钮 + self.cancel_button = ttk.Button(button_frame, text="取消定时", command=self.cancel_shutdown_timer, state="disabled") + self.cancel_button.pack(side=tk.LEFT, padx=5) + + def start_shutdown_timer(self): + if self.is_running: + return + + try: + # 获取输入的时间值 + time_value = float(self.time_var.get()) + if time_value <= 0: + raise ValueError("时间必须大于0") + + # 根据单位转换为秒 + unit = self.unit_var.get() + if unit == "秒": + total_seconds = time_value + elif unit == "分钟": + total_seconds = time_value * 60 + else: # 小时 + total_seconds = time_value * 3600 + + # 更新状态 + self.is_running = True + self.start_button.config(state="disabled") + self.cancel_button.config(state="normal") + self.status_var.set(f"定时已启动 - {time_value} {unit}后关机") + + # 启动倒计时线程 + self.timer_thread = threading.Thread(target=self.countdown_thread, args=(total_seconds,)) + self.timer_thread.daemon = True + self.timer_thread.start() + + except ValueError as e: + messagebox.showerror("输入错误", f"请输入有效的数字: {str(e)}") + + def cancel_shutdown_timer(self): + self.is_running = False + self.start_button.config(state="normal") + self.cancel_button.config(state="disabled") + self.status_var.set("定时已取消") + self.countdown_var.set("00:00:00") + + def countdown_thread(self, total_seconds): + remaining_seconds = total_seconds + + while remaining_seconds > 0 and self.is_running: + # 更新倒计时显示 + hours = int(remaining_seconds // 3600) + minutes = int((remaining_seconds % 3600) // 60) + seconds = int(remaining_seconds % 60) + + countdown_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + self.countdown_var.set(countdown_text) + + # 等待1秒 + time.sleep(1) + remaining_seconds -= 1 + + if self.is_running and remaining_seconds <= 0: + # 时间到,执行关机命令 + self.execute_shutdown() + + def execute_shutdown(self): + # 执行Windows关机命令(立即关机,无等待时间) + try: + # 使用/s参数表示关机,/t 0表示立即执行,/f强制关闭所有应用程序 + os.system("shutdown /s /f /t 0") + self.status_var.set("正在关机...") + except Exception as e: + messagebox.showerror("错误", f"关机命令执行失败: {str(e)}") + self.cancel_shutdown_timer() + + +def main(): + root = tk.Tk() + app = ShutdownTimerApp(root) + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..241259f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyinstaller \ No newline at end of file