开源一个个人博客后台
自己想搞个个人博客,用GitHub Pages来做,但不怎么会HTML和JS,CSS一点儿也不会,只能通过本地的博客后台来实现发博客。
开源一下,底下是代码
代码
import requests import tkinter as tk import time from tkinter import ttk # Normal Tkinter.* widgets are not themed! import markdown # 读取文件 def open_file(file_path): with open(file_path, 'wb+') as f: return f.read() def writeToken(): token=token_enter.get() user=user_enter.get() email=email_enter.get() with open("./settings/token.set",'w') as f: f.write(token) with open("./settings/user.set",'w') as f: f.write(user) with open("./settings/email.set",'w') as f: f.write(email) token_win.destroy() #读取设置 with open("./settings/token.set",'r') as f: token=f.read() with open("./settings/user.set",'r') as f: user=f.read() with open("./settings/email.set",'r') as f: email=f.read() with open("./settings/post_formwork.set",'r') as f: post_formwork=f.read() def post(content,name): global user upload_file(content,name+".html",user,user+".github.io","post/") def md_post(content,name,title): html_cont=markdown.Markdown(content) html=post_formwork.format(title=title,name=name,content=content) post(html,name) # 将文件转换为base64编码,上传文件必须将文件以base64格式上传 def file_base64(data): data_b64 = base64.b64encode(data).decode('utf-8') return data_b64 # 上传文件 def upload_file(file_data,file_name,user,repo,path=""): global token,email url = "https://api.github.com/repos/"+user+"/"+repo+"/contents/"+path+file_name # 用户名、库名、路径 headers = {"Authorization": "token " + token} content = file_base64(file_data) data = { "message": "message", "committer": { "name": user, "email": email }, "content": file_data } data = json.dumps(data) req = requests.put(url=url, data=data, headers=headers) req.encoding = "utf-8" re_data = json.loads(req.text) print(re_data) print(re_data['content']['sha']) print("https://cdn.jsdelivr.net/gh/[user]/[repo]/[path]"+file_name) def md(): md_win=tk.Tk() md_win.title('MarkDown编辑器') tk.Label(md_win,text='博客名称').pack() name_enter=ttk.Entry(md_win) name_enter.pack(fill=tk.X) tk.Label(md_win,text='博客标题').pack() title_enter=ttk.Entry(md_win) title_enter.pack(fill=tk.X) tk.Label(md_win,text='博客内容').pack() scroll = tk.Scrollbar(md_win) md_enter=tk.Text(md_win) # 将滚动条填充 scroll.pack(side=tk.RIGHT,fill=tk.BOTH) # side是滚动条放置的位置,上下左右。fill是将滚动条沿着y轴填充 md_enter.pack(fill=tk.Y) # 将文本框填充进窗口 # 将滚动条与文本框关联 scroll.config(command=md_enter.yview) # 将文本框关联到滚动条上,滚动条滑动,文本框跟随滑动 md_enter.config(yscrollcommand=scroll.set) # 将滚动条关联到文本框 post_btn=ttk.Button(md_win,text='发布',command=lambda:md_post(md_enter.get(),name_enter.get(),title_enter.get())).pack(side=tk.BOTTOM,fill=tk.X) md_win.mainloop() s_win=tk.Tk() s_win.title('One Blogger') screenWidth = s_win.winfo_screenwidth() # 获取显示区域的宽度 screenHeight = s_win.winfo_screenheight() # 获取显示区域的高度 width = 300 # 设定窗口宽度 height = 165 # 设定窗口高度 left = (screenWidth - width) / 2 top = (screenHeight - height) / 2 s_win.geometry("%dx%d+%d+%d" % (width, height, left, top)) s_win.resizable(0,0) s_win.overrideredirect(1) titleA=tk.Label(s_win,text='One Blogger',font=('幼圆',25)) titleA.pack(pady=20) titleB=tk.Label(s_win,text='博客后台',font=('幼圆',15)) titleB.pack(pady=10) s_win.update() time.sleep(2) s_win.destroy() if token=='' or user=='' or email=='': token_win=tk.Tk() token_win.geometry('300x500') token_win.title('One Blogger') token_win.resizable(0,0) s = ttk.Style() print(s.theme_names()) #ttk所有主题风格[windows Python 3.8.3 tk 8.6.9] #output:('winnative','clam','alt','default','classic','vista','xpnative') s.theme_use('vista') tk.Label(token_win,text='输入一些必需的信息以继续',font=('幼圆',15)).pack(pady=20) tk.Label(token_win,text='GitHub Token',font=('幼圆',10)).pack(pady=10) token_enter=ttk.Entry(token_win) token_enter.pack(padx=30,fill=tk.X) tk.Label(token_win,text='GitHub用户名',font=('幼圆',10)).pack(pady=10) user_enter=ttk.Entry(token_win) user_enter.pack(padx=30,fill=tk.X) tk.Label(token_win,text='GitHub账户邮箱',font=('幼圆',10)).pack(pady=10) email_enter=ttk.Entry(token_win) email_enter.pack(padx=30,fill=tk.X) ttk.Button(token_win,text='继续',command=writeToken).pack(pady=20) token_win.mainloop() win=tk.Tk() win.geometry('800x450') win.title('One Blogger') s = ttk.Style() print(s.theme_names()) #ttk所有主题风格[windows Python 3.8.3 tk 8.6.9] #output:('winnative','clam','alt','default','classic','vista','xpnative') s.theme_use('clam') ttk.Button(win,text="MarkDown编辑器",command=md).pack() win.mainloop()
GitHub Pages的存储库里应该放什么?
必需
一个主页 | /index.html |
一个文件夹,用于存放你发的博客 | /post/ |
可选
错误页面,例如404.html
其他你想要添加的页面,但记得留下入口!
其他你想要添加的任何东西,例如CSS和JS代码
本地的后台路径内应该放什么?
后台程序,代码已公开
settings文件夹,用于存放设置,里面必须有以下文件:
- email.set,用于存放你的GitHub账户邮箱,不用手动更改除非是已经填写,需要第二次更改。
- post_formwork.set,用于存放你的博客页面模板,需要手动更改,更改后不会影响以前的内容。以下是几个参数:{title}博客标题,{name}博客名称(页面名称),{content}博客内容(HTML),就算你不想改你至少得有这些东西吧:
1 <html> 2 <head> 3 </head> 4 <body> 5 <h1>{title}</h1> 6 {content} 7 </body> 8 </html>
- token.set,用于存放你的GitHub Token,不用手动更改,除非是已经填写,需要第二次更改,若为空则无法调用API(反正你每次启动是它都会检测,如果为空就会让你填写而不会进入主页面)。
- user.set,用于存放你的GitHub用户名,不用手动更改,除非是已经填写,需要第二次更改。
后期计划更新
可视化设置界面
主题设置
界面细节优化
增加init.py初始化程序,并在主界面增加入口(有可能是增加一个函数,视情况而定)
所需的第三方库
markdown
requests

1 pip install markdown -i https://pypi.douban.com/simple/ 2 pip install requests -i https://pypi.douban.com/simple/
注意
我因为没有腾出存储库,所有还没测试,欢迎大家把测试结果、报错和BUG发到评论区,我如果看见了就会改良。
待存储库腾出来以后,我会用最简陋的网站来测试(HTML自学的,CSS不会,在网上用工具生成的,能力限制,只能用最简陋的了)。
求助
上面说了,我制作网站的能力不行,所以我希望有人能给我提供一个能使用这个博客后台的个人博客网站(或者模板),请提供链接,谢谢!
2.0
增加了初始化功能
增加了链接存储库功能,在博客页面模板中的参数为{linked_repo}
发现并修复了一些设计上的BUG
代码
1 import requests 2 import tkinter as tk 3 import time 4 from tkinter import ttk # Normal Tkinter.* widgets are not themed! 5 from ttkthemes import ThemedTk 6 import markdown 7 import tkinter.messagebox as msgbox 8 import tkinter.filedialog as filebox 9 10 # 读取文件 11 def open_file(file_path): 12 with open(file_path, 'wb+') as f: 13 return f.read() 14 15 def writeToken(): 16 token=token_enter.get() 17 user=user_enter.get() 18 email=email_enter.get() 19 with open("./settings/token.set",'w') as f: 20 f.write(token) 21 with open("./settings/user.set",'w') as f: 22 f.write(user) 23 with open("./settings/email.set",'w') as f: 24 f.write(email) 25 token_win.destroy() 26 27 #读取设置 28 with open("./settings/token.set",'r') as f: 29 token=f.read() 30 with open("./settings/user.set",'r') as f: 31 user=f.read() 32 with open("./settings/email.set",'r') as f: 33 email=f.read() 34 with open("./settings/post_formwork.set",'r') as f: 35 post_formwork=f.read() 36 37 38 def post(content,name): 39 global user 40 upload_file(content,name+".html",user,user+".github.io","post/") 41 42 def md_post(content,name,title,linkedRepo): 43 html_cont=markdown.Markdown(content) 44 html=post_formwork.format(title=title,name=name,content=content,linked_repo=linkedRepo) 45 post(html,name) 46 47 # 将文件转换为base64编码,上传文件必须将文件以base64格式上传 48 def file_base64(data): 49 data_b64 = base64.b64encode(data).decode('utf-8') 50 return data_b64 51 52 53 # 上传文件 54 def upload_file(file_data,file_name,user,repo,path=""): 55 global token,email 56 url = "https://api.github.com/repos/"+user+"/"+repo+"/contents/"+path+file_name # 用户名、库名、路径 57 headers = {"Authorization": "token " + token} 58 content = file_base64(file_data) 59 data = { 60 "message": "message", 61 "committer": { 62 "name": user, 63 "email": email 64 }, 65 "content": file_data 66 } 67 data = json.dumps(data) 68 req = requests.put(url=url, data=data, headers=headers) 69 req.encoding = "utf-8" 70 re_data = json.loads(req.text) 71 print(re_data) 72 print(re_data['content']['sha']) 73 print("https://cdn.jsdelivr.net/gh/[user]/[repo]/[path]"+file_name) 74 75 def md(): 76 md_win=tk.Tk() 77 md_win.title('MarkDown编辑器') 78 tk.Label(md_win,text='博客名称').pack() 79 name_enter=ttk.Entry(md_win) 80 name_enter.pack(fill=tk.X) 81 tk.Label(md_win,text='博客标题').pack() 82 title_enter=ttk.Entry(md_win) 83 title_enter.pack(fill=tk.X) 84 tk.Label(md_win,text='链接的存储库(可选)').pack() 85 linkedRepo_enter=ttk.Entry(md_win) 86 linkedRepo_enter.pack(fill=tk.X) 87 tk.Label(md_win,text='博客内容').pack() 88 scroll = tk.Scrollbar(md_win) 89 md_enter=tk.Text(md_win) 90 # 将滚动条填充 91 scroll.pack(side=tk.RIGHT,fill=tk.BOTH) # side是滚动条放置的位置,上下左右。fill是将滚动条沿着y轴填充 92 md_enter.pack(fill=tk.Y) # 将文本框填充进窗口 93 # 将滚动条与文本框关联 94 scroll.config(command=md_enter.yview) # 将文本框关联到滚动条上,滚动条滑动,文本框跟随滑动 95 md_enter.config(yscrollcommand=scroll.set) # 将滚动条关联到文本框 96 post_btn=ttk.Button(md_win,text='发布',command=lambda:md_post(md_enter.get(),name_enter.get(),title_enter.get(),linkedRepo_enter.get())).pack(side=tk.BOTTOM,fill=tk.X) 97 md_win.mainloop() 98 99 def init(): 100 global user 101 c=msgbox.askokcancel(title = '要继续吗?',message='初始化博客站点将会覆盖以前的所有数据,要继续吗?') 102 if c: 103 site_dir=filebox.askdirectory(title='选择本地博客站点备份并上传') 104 print(site_dir) 105 root,dirs,files=os.walk(file) 106 msgbox.showinfo('进行以下操作','请手动清空(或创建)名为“你的用户名.github.io”的存储库,然后单击确定以继续。') 107 for path in files: 108 with open(site_dir+path,'rb') as f: 109 content=f.read() 110 upload_file(content,path,user,user+".github.io","/") 111 112 s_win=tk.Tk() 113 s_win.title('One Blogger') 114 115 screenWidth = s_win.winfo_screenwidth() # 获取显示区域的宽度 116 screenHeight = s_win.winfo_screenheight() # 获取显示区域的高度 117 width = 300 # 设定窗口宽度 118 height = 165 # 设定窗口高度 119 left = (screenWidth - width) / 2 120 top = (screenHeight - height) / 2 121 s_win.geometry("%dx%d+%d+%d" % (width, height, left, top)) 122 s_win.resizable(0,0) 123 s_win.overrideredirect(1) 124 125 titleA=tk.Label(s_win,text='One Blogger 2',font=('幼圆',25)) 126 titleA.pack(pady=20) 127 128 titleB=tk.Label(s_win,text='博客后台',font=('幼圆',15)) 129 titleB.pack(pady=10) 130 131 ver=tk.Label(s_win,text='后台版本 V2.0',font=('幼圆',10)) 132 ver.pack(pady=10) 133 134 s_win.update() 135 136 time.sleep(2) 137 s_win.destroy() 138 139 if token=='' or user=='' or email=='': 140 token_win=tk.Tk() 141 token_win.geometry('300x500') 142 token_win.title('One Blogger') 143 token_win.resizable(0,0) 144 s = ttk.Style() 145 print(s.theme_names()) #ttk所有主题风格[windows Python 3.8.3 tk 8.6.9] 146 #output:('winnative','clam','alt','default','classic','vista','xpnative') 147 s.theme_use('vista') 148 tk.Label(token_win,text='输入一些必需的信息以继续',font=('幼圆',15)).pack(pady=20) 149 tk.Label(token_win,text='GitHub Token',font=('幼圆',10)).pack(pady=10) 150 token_enter=ttk.Entry(token_win) 151 token_enter.pack(padx=30,fill=tk.X) 152 tk.Label(token_win,text='GitHub用户名',font=('幼圆',10)).pack(pady=10) 153 user_enter=ttk.Entry(token_win) 154 user_enter.pack(padx=30,fill=tk.X) 155 tk.Label(token_win,text='GitHub账户邮箱',font=('幼圆',10)).pack(pady=10) 156 email_enter=ttk.Entry(token_win) 157 email_enter.pack(padx=30,fill=tk.X) 158 ttk.Button(token_win,text='继续',command=writeToken).pack(pady=20) 159 token_win.mainloop() 160 161 162 win=tk.Tk() 163 win.geometry('800x450') 164 win.title('One Blogger') 165 166 s = ttk.Style() 167 print(s.theme_names()) #ttk所有主题风格[windows Python 3.8.3 tk 8.6.9] 168 #output:('winnative','clam','alt','default','classic','vista','xpnative') 169 s.theme_use('clam') 170 171 ttk.Separator(win,orient=tk.HORIZONTAL).pack(pady=7,fill=tk.X) 172 ttk.Button(win,text="MarkDown编辑器",command=md).pack(padx=10,fill=tk.X) 173 ttk.Separator(win,orient=tk.HORIZONTAL).pack(pady=7,fill=tk.X) 174 ttk.Button(win,text="初始化博客站点",command=init).pack(padx=10,fill=tk.X) 175 176 win.mainloop()
V 2.1.0
增加了设置界面
对部分细节做了更改
因为外观设置需要增加存储设置的文件,所以计划在下一个版本(V 2.1.1)加上
1 import requests 2 import tkinter as tk 3 import time 4 from tkinter import ttk # Normal Tkinter.* widgets are not themed! 5 from ttkthemes import ThemedTk 6 import markdown 7 import tkinter.messagebox as msgbox 8 import tkinter.filedialog as filebox 9 import webbrowser 10 11 # 读取文件 12 def open_file(file_path): 13 with open(file_path, 'wb+') as f: 14 return f.read() 15 16 def writeToken(): 17 token=token_enter.get() 18 user=user_enter.get() 19 email=email_enter.get() 20 with open("./settings/token.set",'w') as f: 21 f.write(token) 22 with open("./settings/user.set",'w') as f: 23 f.write(user) 24 with open("./settings/email.set",'w') as f: 25 f.write(email) 26 token_win.destroy() 27 28 #读取设置 29 with open("./settings/token.set",'r') as f: 30 token=f.read() 31 with open("./settings/user.set",'r') as f: 32 user=f.read() 33 with open("./settings/email.set",'r') as f: 34 email=f.read() 35 with open("./settings/post_formwork.set",'r') as f: 36 post_formwork=f.read() 37 38 39 def post(content,name): 40 global user 41 upload_file(content,name+".html",user,user+".github.io","post/") 42 43 def md_post(content,name,title,linkedRepo): 44 html_cont=markdown.Markdown(content) 45 html=post_formwork.format(title=title,name=name,content=content,linked_repo=linkedRepo) 46 post(html,name) 47 48 # 将文件转换为base64编码,上传文件必须将文件以base64格式上传 49 def file_base64(data): 50 data_b64 = base64.b64encode(data).decode('utf-8') 51 return data_b64 52 53 54 # 上传文件 55 def upload_file(file_data,file_name,user,repo,path=""): 56 global token,email 57 url = "https://api.github.com/repos/"+user+"/"+repo+"/contents/"+path+file_name # 用户名、库名、路径 58 headers = {"Authorization": "token " + token} 59 content = file_base64(file_data) 60 data = { 61 "message": "message", 62 "committer": { 63 "name": user, 64 "email": email 65 }, 66 "content": file_data 67 } 68 data = json.dumps(data) 69 req = requests.put(url=url, data=data, headers=headers) 70 req.encoding = "utf-8" 71 re_data = json.loads(req.text) 72 print(re_data) 73 print(re_data['content']['sha']) 74 print("https://cdn.jsdelivr.net/gh/[user]/[repo]/[path]"+file_name) 75 76 def md(): 77 md_win=tk.Tk() 78 md_win.title('MarkDown编辑器') 79 tk.Label(md_win,text='博客名称').pack() 80 name_enter=ttk.Entry(md_win) 81 name_enter.pack(fill=tk.X) 82 tk.Label(md_win,text='博客标题').pack() 83 title_enter=ttk.Entry(md_win) 84 title_enter.pack(fill=tk.X) 85 tk.Label(md_win,text='链接的存储库(可选)').pack() 86 linkedRepo_enter=ttk.Entry(md_win) 87 linkedRepo_enter.pack(fill=tk.X) 88 tk.Label(md_win,text='博客内容').pack() 89 scroll = tk.Scrollbar(md_win) 90 md_enter=tk.Text(md_win) 91 # 将滚动条填充 92 scroll.pack(side=tk.RIGHT,fill=tk.BOTH) # side是滚动条放置的位置,上下左右。fill是将滚动条沿着y轴填充 93 md_enter.pack(fill=tk.Y) # 将文本框填充进窗口 94 # 将滚动条与文本框关联 95 scroll.config(command=md_enter.yview) # 将文本框关联到滚动条上,滚动条滑动,文本框跟随滑动 96 md_enter.config(yscrollcommand=scroll.set) # 将滚动条关联到文本框 97 post_btn=ttk.Button(md_win,text='发布',command=lambda:md_post(md_enter.get(),name_enter.get(),title_enter.get(),linkedRepo_enter.get())).pack(side=tk.BOTTOM,fill=tk.X) 98 md_win.mainloop() 99 100 def init(): 101 global user 102 c=msgbox.askokcancel(title = '要继续吗?',message='初始化博客站点将会覆盖以前的所有数据,要继续吗?') 103 if c: 104 site_dir=filebox.askdirectory(title='选择本地博客站点备份并上传') 105 print(site_dir) 106 root,dirs,files=os.walk(file) 107 msgbox.showinfo('进行以下操作','手动清空(或创建)名为“你的用户名.github.io”的存储库,然后单击确定以继续。') 108 for path in files: 109 with open(site_dir+path,'rb') as f: 110 content=f.read() 111 upload_file(content,path,user,user+".github.io","/") 112 113 def settings(): 114 settings=tk.Tk() 115 settings.title('设置') 116 settings.state("zoomed") 117 # 创建画布 118 about=ttk.LabelFrame(settings,text='关于') 119 about.pack(padx=10,pady=10,fill=tk.X) 120 tk.Label(about,text='One Blogger 2',font=('幼圆',12)).pack(pady=5) 121 tk.Label(about,text='2020 By 人工智障',font=('幼圆',12)).pack(pady=5) 122 tk.Label(about,text='V 2.1.0',font=('幼圆',12)).pack(pady=5) 123 ttk.Button(about,text='作者博客',command=lambda:webbrowser.open("https://www.cnblogs.com/TotoWang/")).pack(pady=5,padx=5) 124 api=ttk.LabelFrame(settings,text='API参数设置') 125 api.pack(padx=10,pady=10,fill=tk.X) 126 ttk.Label(api,text='GitHub Token').pack(pady=10) 127 token_enter=ttk.Entry(api) 128 token_enter.pack(padx=30,fill=tk.X) 129 ttk.Label(api,text='GitHub用户名').pack(pady=10) 130 user_enter=ttk.Entry(api) 131 user_enter.pack(padx=30,fill=tk.X) 132 ttk.Label(api,text='GitHub账户邮箱').pack(pady=10) 133 email_enter=ttk.Entry(api) 134 email_enter.pack(padx=30,fill=tk.X) 135 ttk.Button(api,text='保存',command=writeToken).pack(pady=20) 136 ui=ttk.LabelFrame(settings,text='外观') 137 ui.pack(padx=10,pady=10,fill=tk.X) 138 ttk.Label(ui,text='敬请期待').pack(pady=10) 139 site=ttk.LabelFrame(settings,text='站点设置') 140 site.pack(padx=10,pady=10,fill=tk.X) 141 ttk.Button(site,text="初始化博客站点",command=init).pack(padx=10,pady=10,fill=tk.X) 142 settings.mainloop() 143 144 s_win=tk.Tk() 145 s_win.title('One Blogger') 146 147 screenWidth = s_win.winfo_screenwidth() # 获取显示区域的宽度 148 screenHeight = s_win.winfo_screenheight() # 获取显示区域的高度 149 width = 300 # 设定窗口宽度 150 height = 165 # 设定窗口高度 151 left = (screenWidth - width) / 2 152 top = (screenHeight - height) / 2 153 s_win.geometry("%dx%d+%d+%d" % (width, height, left, top)) 154 s_win.resizable(0,0) 155 s_win.overrideredirect(1) 156 157 titleA=tk.Label(s_win,text='One Blogger 2',font=('courier new',25)) 158 titleA.pack(pady=20) 159 160 titleB=tk.Label(s_win,text='博客后台',font=('幼圆',15)) 161 titleB.pack(pady=10) 162 163 ver=tk.Label(s_win,text='后台版本 V2.1.0',font=('幼圆',10)) 164 ver.pack(pady=10) 165 166 s_win.update() 167 168 time.sleep(2) 169 s_win.destroy() 170 171 if token=='' or user=='' or email=='': 172 token_win=tk.Tk() 173 token_win.geometry('300x500') 174 token_win.title('One Blogger') 175 token_win.resizable(0,0) 176 s = ttk.Style() 177 print(s.theme_names()) #ttk所有主题风格[windows Python 3.8.3 tk 8.6.9] 178 #output:('winnative','clam','alt','default','classic','vista','xpnative') 179 s.theme_use('vista') 180 tk.Label(token_win,text='输入一些必需的信息以继续',font=('幼圆',15)).pack(pady=20) 181 tk.Label(token_win,text='GitHub Token',font=('幼圆',10)).pack(pady=10) 182 token_enter=ttk.Entry(token_win) 183 token_enter.pack(padx=30,fill=tk.X) 184 tk.Label(token_win,text='GitHub用户名',font=('幼圆',10)).pack(pady=10) 185 user_enter=ttk.Entry(token_win) 186 user_enter.pack(padx=30,fill=tk.X) 187 tk.Label(token_win,text='GitHub账户邮箱',font=('幼圆',10)).pack(pady=10) 188 email_enter=ttk.Entry(token_win) 189 email_enter.pack(padx=30,fill=tk.X) 190 ttk.Button(token_win,text='继续',command=writeToken).pack(pady=20) 191 token_win.mainloop() 192 193 194 win=tk.Tk() 195 win.geometry('800x450') 196 win.title('One Blogger') 197 198 s = ttk.Style() 199 print(s.theme_names()) #ttk所有主题风格[windows Python 3.8.3 tk 8.6.9] 200 #output:('winnative','clam','alt','default','classic','vista','xpnative') 201 s.theme_use('clam') 202 203 ttk.Separator(win,orient=tk.HORIZONTAL).pack(pady=7,fill=tk.X) 204 ttk.Button(win,text="MarkDown编辑器",command=md).pack(padx=10,fill=tk.X) 205 ttk.Separator(win,orient=tk.HORIZONTAL).pack(pady=7,fill=tk.X) 206 ttk.Button(win,text="初始化博客站点",command=init).pack(padx=10,fill=tk.X) 207 ttk.Button(win,text="设置",command=settings).pack(padx=10,fill=tk.X) 208 209 win.mainloop()
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现