【Mind+python】人人都会制作的个性签名生成器

项目起源

字写不好没关系,自己的名字一定要写好。这是我们常听到的一句话。文件、合同、会议签到等场合都要频繁地使用签名,有一手潇洒、漂亮、大方的签名除了给人以美的享受以外,还能体现个人文化品味和艺术修养。

谈到签名,不得不追溯一下历史,艺术签名,它不是今人的发明,古已有之,我们的前人称其为“花押”。另有“画押”、“签押”、“签字”、“署名”、“签名”等多种称法,这些称法意思大同小异,它们都表示认可、负责和示信于人的作用。由于这些词没有明显的界限,往往也会混用。

未来世界里也许什么都能被机器替代,但代表商业信誉和个人意愿真实性的签名是不能被取代的。经过设计的签名简洁、美观、更便于书写,并且别人还不容易模仿,有一手好的签名它会给每天的生活带来自信和精彩。

本期教程我们将带大家从零开始制作一款个性签名生成器,让我们将美好的签名留在别人的印象中。下面我们就一起来看看个性签名生成器具体是如何实现的

20261021144012843-image

本次案例中我们使用Mind+Python制作个性签名,再结合图形化界面库制作一款实用好看的个性签名生成器

下面我们先看一下演示效果

效果展示

【Mind+python】人人都会制作的个性签名生成器图36

程序设计

下面是本次程序设计的思路

编程思路

20261021144034797-image

准备编程环境

本次个性签名生成器的制作我们还是使用简单易入门的 Mind+图形化编程环境 。Mind+编程环境可以在电脑浏览器登录 mind+网站,选择与自己电脑匹配的安装包下载软件 Mind+图形化编程环境下载地址:http://www.yishuzi.com/b/13.htm

20261021145011462-image

想要用爬虫的方式访问这个网站,我们得知道该网站的API地址。我们在浏览器中输入个性签名网站的地址,在出现的页面单击鼠标右键即可看到如下图所示的界面,界面的右侧就是该网页后台运行的一些代码

20261021144138807-image

随后,我们按照下图所示的顺序,先点击“Network”选项,然后在左侧的页面中输入内容并点击“给我设计”,之后再点击右侧“Name”栏中出现的内容,这样就可以看到该网页对应的API地址了,也就是图中标注的“Request URL”,同时我们也可以知道它的请求方式为requests.post

20261021144207790-image

Post请求方式不同于get,它们的共同特点是都属于http请求,在访问时都可以携带一些参数。不同的是,get请求是直接将参数放在url链接里的,一次完成请求访问。而Post请求是先发送请求头,等待服务器相响应后,再发送数据,两次完成请求访问。

理解了它们的不同之处后,现在我们就需要获取Post访问时携带的数据了。我们可以点击“Payload”一栏查看这些数据,这些数据中可以修改的内容包括:文字内容、字体、背景颜色、文字描边颜色、文字颜色等。

20261021144233567-image

理解了API地址和携带的参数,现在来尝试访问这个地址 我们在 Mind+中输入如下程序:

import requests

#将个性签名网站的API地址放入“startUrl”变量中

startUrl = 'http://www.yishuzi.com/b/re13.php'

#将Post请求时的数据放入data字典中

data={

        'id':"张三",

        'zhenbi':'20191123',

        'id1':901,

        'id2':'#FFFFFF',

        'id4':'#FF0000',

        'id6':'#0000FF'

        }

#向个性签名网站的API地址发送网络请求,并将返回数据以text的格式放入“html”变量中

html = requests.post(startUrl, data=data).text

print(html)

在程序之中,复制刚才看到的API地址以及“Payload”中的参数, 使用request.post的方式向网站发送请求,之后点击运行按钮运行程序

然后会看到如下图所示的运行结果,从返回的数据发现访问并没有成功

20261021144251730-image

这是什么原因造成的呢?原来是该网站的服务器有一定的反爬限制,就是如果我们不是通过常规的浏览器访问该网站的话,它的服务器是拒绝访问的。那么该如何解决这个问题呢?

通常情况下一些网站的服务器会检测对应请求载体的身份标识。也就是服务器会检测每次访问的请求是浏览器发出的还是一个爬虫程序发出的。

如果检测到请求的载体身份标识为某一款浏览器,说明是一个正常的请求(用户通过浏览器发出的请求)。但是,如果检测到的请求载体身份标识不是基于某一款浏览器的,则表示该请求为一个不正常的请求(是一个爬虫请求)。则服务器端很有可能拒绝该次请求。

理解了这点,我们就可以伪装成浏览器去访问,这样的伪装通常称为UA伪装。UA指的是User-Agent,是一个请求头信息,我们可以在网页后台的“Headers”中查看到。

20261021144308643-image

方法:将对应的User-Agent封装到一个字典中。然后在psot请求时携带这个字典数据进行访问

UA = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36'}

html = requests.post(startUrl,headers = UA, data=data).text

我们在之前的程序中加入UA伪装数据,运行程序后就可以看到如下结果

20261021144328262-image

通过观察返回的数据,可以看出其中包含了一个网址连接。我们尝试将链接复制到浏览器中并打开,链接打开后得到的是一张图片,接下来只需要将这张图片保存下来即可

20261021144344552-image

解析处理数据

要保存图片,首先要做的是把返回数据中的链接提取出来,这里就需要用到正则表达式来完成此项任务

import re #导入正则表达式库

imgXpath=r'<img src="(.*?)">'

imgurls =re.findall(imgXpath,html)

print(imgurls)

通过运行上述程序,可以看到,网址连接已经成功存放到一个列表中了

20261021144358141-image

链接成功提取出来后就可以继续使用requests访问链接,并保存图片为gif格式(因为gif格式方便后面调用展示)

程序如下

imgdata = requests.get(url = imgurls[0],headers = UA).content

with open("{}.gif".format('张三'),'wb') as f:

     f.write(imgdata)

     print("下载成功!")

运行程序后看到如下结果表示图片下载完成

20261021144415124-image

我们打开程序所在的文件夹,可以看到文件夹中多出了一张文件名为“张三”的gif格式图片

20261021144427756-image

至此,我们已经完整地生成了一个个性签名。不过,有的朋友可能会有疑问,这些姓名和字体都是固定的,该如何修改呢?

API地址参数

如果要做到这一步,我们需要对程序稍加调整,这里需要从个性签名网站中选取一些比较喜欢的字体,选择的字体方法如下,从图中可以看出每个字体对应一个数字编号。

20261021144444943-image

我们可以将这些字体以及对应的数字编号放入一个字典中,也就是字体作为字典的“键”,数字作为字典的“值”,之后再通过input的方法输入姓名和字体

#字体列表

option = {'1.艺术体':'364',  '2.连笔手写':'396',  '3.明星手写':'5',  '4.情书常规体':'342',

          '5.一笔艺术签':'901',  '6.连笔商务签':'904',  '7.一笔商务签':'905',  '8.签名字体':'6',

            '9.娃娃纂':'3' , '10.太极文':'306'}

print(option)

font = input("请在上述字体列表选择一款字体")

name = input("请输入姓名或其他文字")

#将Post请求时的数据放入data字典中

data={

        'id':name,

        'zhenbi':'20191123',

        'id1':font,

        'id2':'#FFFFFF',

        'id4':'#FF0000',

        'id6':'#0000FF'

        }

这样就可以由用户来选择字体和任意输入文字内容了,下面是修改后的程序运行的结果

20261021144501582-image

为了方便程序调用,我们可以将上述程序封装在一个名为“getName()”函数中,程序修改后如下

option = {'1.艺术体':'364',  '2.连笔手写':'396',  '3.明星手写':'5',  '4.情书常规体':'342',

          '5.一笔艺术签':'901',  '6.连笔商务签':'904',  '7.一笔商务签':'905',  '8.签名字体':'6',

            '9.娃娃纂':'3' , '10.太极文':'306'}

def getName(font,name):

    #将个性签名网站的API地址放入“startUrl”变量中

    startUrl = 'http://www.yishuzi.com/b/re13.php'

    UA = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36'}

    #将Post请求时的数据放入data字典中

    data={

            'id':name,

            'zhenbi':'20191123',

            'id1':font,

            'id2':'#FFFFFF',

            'id4':'#FF0000',

            'id6':'#0000FF'

            }

    #向个性签名网站的API地址发送网络请求,并将返回数据以text的格式放入“html”变量中

    html = requests.post(startUrl,headers = UA, data=data).text

    print(html)

    imgXpath=r'<img src="(.*?)">'

    imgurls =re.findall(imgXpath,html)

    print(imgurls)

    imgdata = requests.get(url = imgurls[0],headers = UA).content

    with open("{}.gif".format(name),'wb') as f:

        f.write(imgdata)

        print("下载成功!")

if name == 'main':

    font = input("请在上述字体列表选择一款字体")

    name = input("请输入姓名或其他文字")

    getName(font,name)

通过以上程序的测试,我们已经实现了输入任意内容之后输出对应的个性签名, 个性签名生成器还需要一个人性化的操作界面,接下来我们为个性签名制作一个可视化的操作界面

使用 Tkinter 库设计图形化界面

本次作品我们还是延续以往的惯例,使用Python自带的tkinter 库来创建图形化的应用界面,为了不增加难度,我们重新建一个新程序来学习使用 tkinter 库,基本用法掌握后再与个性签名的程序结合

20261021144525972-image

第一步,创建图形化界面

创建窗口

在 Mind+软件中编写如下程序

from tkinter import *

if name == 'main':

    root = Tk()

    root.title("个性签名生成器")#设置窗口标题

    #设置窗口大小(窗口横向长度X窗口纵向长度+距离屏幕左边框的横向距离+距离屏幕上边框的纵向距离)

    root.geometry('400x400+200+200')

    root.mainloop()

运行上述程序,会看到下图 33 所示的结果,在电脑屏幕中显示出了一个 400 像素宽,400像素高的空白窗口

20261021144540249-image

我们再来看刚才的程序,在窗口界面设置中,root.geometry('400x400+200+200') 里的参数可以来调整窗口的尺寸和显示的位置,参数中第 1 个数字为窗口的宽度,第2 个数字为窗口的高度,第3 个数字为窗口的左上角距离屏幕左边框的横向距离,第4 个数字为窗口的左上角距离屏幕上边框的纵向距离。当然,如果这里不填任何数据,那么窗口会自适应界面,也就是根据设定的组件布局位置灵活调整窗口大小。窗口新建完成,我们就可以在窗口内布置各种组件了 首先使用 Label 标签为界面添加一个标题和LOGO图片, Label 标签它是一个用于在指定的窗口中显示文本和图像的组件

设置 Label 标签

在编程环境中我们增加如下程序

photo1=PhotoImage(file="logo.gif")#导入图片

Label(root,image=photo1).grid(row = 0,column=0)#显示图片

label1 = Label(root,text="个性签名生成器",font=('微软雅黑',15),fg='black')#标签文字

label1.grid(row=0,column=1,)#显示标签

root.mainloop()

程序运行后,我们会看到如下图34所示的运行结果,由 Label 标签设置的“个性签名生成器”几个汉字和LOGO图片会显示在窗口的左上方,图片显示在第 0 行,第 0 列的位置 ,汉字显示在第0行,第1列的位置,其中 “row”和“column”为行和列的参数,这里需要强调的是 tkinter 库只支持 gif 格式的图片,我们可以使用电脑中自带的画图工具将其他格式的图片转变成 gif 格式 ,并且还要将LOGO图片与py程序放在同一路径下

20261021144554504-image

Label标签显示的效果还可以通过修改程序中Label的文字内容“text=”,文字颜色“fg=” 或背景颜色“bg=”,字体和字体大小“font=()”以及标签的尺寸“width= ,height= ” 来调整 Label 在窗口中的显示效果,如果不设置文字或背景颜色,则会默认为黑色文字,白色背景

以上,我们成功放置了 Label 标签,除 Label 标签外本次个性签名生成器程序还会用到 tkinter 中的Entry输入框、Text 文本框、Button 按钮和Listbox选择列表框,接下来我们布局 Entry输入框

Entry输入框

Entry 输入框可以用来获取键盘输入的内容也可以用来显示单行文本。在这里我们为个性签名生成器增加一个 Entry 输入框,用来获取键盘输入的内容

在程序中增加如下程序

#定义输入框

    entry = Entry(root,width=50)#输入框

    entry.grid(row = 1,column=1, padx = 20, pady = 20)#调整位置

    root.mainloop()#窗口循环运行

程序运行后会看到Label标签的下方出现了Entry 输入框,也就是第2行,第1列的位置增加了一个Entry输入框(程序中行列序号是从0开始计数的) ,并通过“padx”和“pady”设置了输入框距离上下左右的边距,其中 “ columnspan=”参数的含义是对列进行合并,大家可以尝试修改参数,看看会有什么样的效果

20261021144608115-image

如果想要在 Entry 输入框中添加或删除内容,可以采用下面的方法

enter.delete(0,END)#清空输入框

enter.insert(0,'张三')#输入框中添加文本

获取输入框中的内容可以使用指令

enter.get()#获取输入框中内容

只有输入框显得有些单调, 如果能够在输入框前面添加一些说明文字就完美了,下面我们使用label标签来添加一些说明文字,程序如下

label2 = Label(root,text='请输入你的姓名:',font=('微软雅黑',13))#

label2.grid(row=1,column=0,sticky = E,pady = 10)

运行程序后可以看到如下图所示的结果,在输入框前面出现了一些文字内容

20261021144622173-image

输入框设置完成,下面我们需要设置“生成”、“清空”和“退出”的按钮

Button按钮设置

“生成”、“清空”和“退出”三个按钮放置在同一行,程序如下

Button(root, text='生  成',width=10, command=print_selection).grid(row = 2, column=0,sticky = E, pady = 10)  

将"生成"Button放置在窗口

Button(root, text = '清  空',width=10).grid(row = 2, column = 1,  pady = 10)# 将清空Button放置在窗口中

Button(root, text = '退  出',width=10).grid(row = 2, column = 2,  sticky = W,pady = 10)# 将清空Button放置在窗口中

程序运行后会看到如下图所示结果

20261021144638764-image

在程序中,我们增加了三个 Button 按钮组件,一个用来生成个性签名,一个用来清空文本框的内容,如果对生成的签名不满意,还可以重新选择字体再次点击按钮来生成签名

其中 command 参数用来设置按钮被点击后执行的命令,比如添加点击“生成”按钮就可以执行生成个性签名的函数;点击“清空”按钮执行清空文本框的动作;,只不过现在函数中还没有程序。

#获取个性签名

def getName():

    pass

#清空显示

def clear():

    pass

#退出

def close():

    pass

Button 按钮组件布局完成,还需要为Button 按钮关联程序,由于Button按钮与Text文本显示框和字体选择框有着密切的联系,所以现在并不急着为按钮添加程序,我们先来布局字体选择框和Text文本显示框

Listbox 选择列表框

按钮布局完成,接着就需要选择字体了,这里我们需要使用Listbox选择列表框

Listbox组件用于显示一个选择列表。Listbox 只能包含文本项目,并且所有的项目都需要使用相同的字体和颜色。用户可以从列表中选择一个或多个选项。

Listbox 组件和前面我们学习过的 Radiobutton 组件类似,不过 Listbox 是以列表的形式来提供选项而后者是通过按钮的形式

创建Listbox

lb = Listbox(root, listvariable='')

lb.grid(row = 3,column=0, padx = 20, pady = 20)

程序运行后,会看到如下图所示的结果,在按钮下方出现了一个文本列表框,可以选择文本内容

20261021144704889-image

当你创建一个 Listbox 组件的时候,它是空的,所以首先要做就是添加一行或多行文本进去。我们使用 insert() 方法添加文本,该方法有两个参数:第一个参数是插入的索引号,第二个参数是插入的字符串。索引号通常是项目的序号(0 是列表中第一项的序号)。

不过你也可以使用一些特殊的索引号:比如 END表示 Listbox 的最后一行,所以当要插入一个项目到列表时可以使用 END:

使用一个for语句将字典中的字体全部添加到列表中,程序如下

#字体列表

option = {'1.艺术体':'364',  '2.连笔手写':'396',  '3.明星手写':'5',  '4.情书常规体':'342',

          '5.一笔艺术签':'901',  '6.连笔商务签':'904',  '7.一笔商务签':'905',  '8.签名字体':'6',

            '9.娃娃纂':'3' , '10.太极文':'306'}

创建一个list并将值循环添加到Listbox控件中

    for key in option:

        lb.insert('end', key)  # 从最后一个位置开始加入值

运行上述程序,可以看到如下结果,我们选择的10种字体都显示在了窗口中了

20261021144721977-image

Text 文本显示框

Text 组件用于显示文本文档,包含纯文本或格式化文本比如可以插入图片、文本、链接等内容,本次我们需要将生成的个性签名图片显示在Text文本框中。

我们添加如下程序 ,其中参数padx和pady可以用来调节文本显示框与边框的距离

text = Text(root,width=80, height=25)

text.grid(row = 3,column=1,columnspan = 2, padx = 20, pady = 5)#调整位置

root.mainloop()

程序运行后会看到在三个Button按钮下方出现了文本显示区域

20261021144739539-image

在文本框中添加或删除文本的指令是

text.insert('end', "显示内容")#在text显示框中添加内容 text.delete('0.0','end')#清空text显示框

获取文本框中的内容的指令是

text.get("1.0","end")    #获取文本输入框的内容

Button 按钮关联程序

以上组件布置完成后,个性签名生成器的界面就初具轮廓了,最后我们还需要将之前的个性签名的程序与图形化界面的程序关联起来。

首先我们设置点击按钮获取字体内容,这里可以使用lb.get()的方法获取Listbox列表选择框中的内容,程序如下:

创建一个方法用于按钮的点击事件

def print_selection():

    value = lb.get(lb.curselection())   # 获取当前选中的文本

    print(value)

Button(root, text='生  成',width=10,command=print_selection).grid(row = 2, column=0,sticky = E, pady = 10)  # 将"生成"Button放置在窗口

程序运行后,当我们选择不同的字体后会打印出不同的结果,运行结果如下图

20261021144753881-image

获取字体的函数设置完成,我们将前文中测试好的获取签名的程序放在getName()函数中,在获取字体之后就可以调用了。同时将程序中从键盘获取内容修改为从Entry输入框获取内容,程序修改后如下

将input换成get,然后调用该函数

创建一个方法用于按钮的点击事件

def print_selection():

    value = lb.get(lb.curselection())   # 获取当前选中的文本

    font = option[value]

    getName(font)

#生成签名

def getName(font):

    name = entry.get()

最后,将获取到的图片显示在文本框中,这里需要用到Pillow库中的“ImageTk”指令,程序如下

from PIL import Image, ImageTk

photo = ImageTk.PhotoImage(file='{}.gif'.format(name))

    text.delete('1.0','end')#清空text显示框

    text.insert(END,'         ')

    text.image_create('2.0',image=photo)

    root.mainloop()

退出和清空

最后,将退出和清空按钮与相对应的函数关联起来,程序如下:

#退出

def close():

    tk_quit = messagebox.askokcancel('提示','真的要退出吗?')

    if tk_quit == True:

        root.destroy()#程序终止运行

#清空显示

def clear():

    text.delete('1.0','end')#清空text显示框

entry.delete(0,END)#清空entry输入框

Button(root, text = '清  空',width=10,command=clear).grid(row = 2, column = 1,  pady = 10)# 将清空Button放置在窗口中

    Button(root, text = '退  出',width=10, command=close).grid(row = 2, column = 2,  sticky = W,pady = 10)# 将清空Button放置在窗口中

优化程序

通过上述程序,我们已经实现了个性签名的制作,不过还有提升的空间,可以将程序进行优化,适当调整各个组件的布局位置以及窗口的大小,可以使用“columnspan=” 合并居中的方式,调节logo和标题的位置,程序如下

Label(root,image=photo1).grid(row = 0,columnspan=2,column=0,pady = 10)#显示图片

label1.grid(row=0,column=1,columnspan=3,pady = 10)#显示标签

完整程序

最后,附上本次个性签名生成器的完整程序,当然此程序在其他编程环境同样适用,大家可以自行编译运行

import re
import os
import threading
from pathlib import Path
from urllib.parse import urljoin

import requests
import tkinter as tk
from tkinter import messagebox, filedialog
from PIL import Image, ImageTk


# ==================== 基础配置 ====================

APP_TITLE = "个性签名生成器"
API_URL = "http://www.yishuzi.com/b/re13.php"
SAVE_DIR = Path("signatures")

SAVE_DIR.mkdir(exist_ok=True)

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/101.0.4951.67 Safari/537.36"
    )
}

# 签名字体列表
FONTS = {
    "1. 艺术体": "364",
    "2. 连笔手写": "396",
    "3. 明星手写": "5",
    "4. 情书常规体": "342",
    "5. 一笔艺术签": "901",
    "6. 连笔商务签": "904",
    "7. 一笔商务签": "905",
    "8. 签名字体": "6",
    "9. 娃娃篆": "3",
    "10. 太极文": "306",
}


# ==================== 签名生成器 ====================

class SignatureGenerator:

    def __init__(self, root):
        self.root = root
        self.root.title(APP_TITLE)
        self.root.geometry("680x620")
        self.root.minsize(600, 540)

        # 保持图片引用,防止 Tkinter 图片被回收
        self.signature_photo = None
        self.logo_photo = None

        self.create_widgets()

    # 创建界面
    def create_widgets(self):
        # 标题区域
        title = tk.Label(
            self.root,
            text=APP_TITLE,
            font=("微软雅黑", 20, "bold"),
            fg="#333333"
        )
        title.pack(pady=15)

        # 姓名输入区域
        input_frame = tk.Frame(self.root)
        input_frame.pack(pady=8)

        tk.Label(
            input_frame,
            text="请输入你的姓名:",
            font=("微软雅黑", 12)
        ).pack(side=tk.LEFT)

        self.name_entry = tk.Entry(
            input_frame,
            font=("微软雅黑", 12),
            width=22
        )
        self.name_entry.pack(side=tk.LEFT, padx=8)

        # 字体选择区域
        tk.Label(
            self.root,
            text="请选择签名字体:",
            font=("微软雅黑", 12)
        ).pack(pady=(12, 5))

        self.font_list = tk.Listbox(
            self.root,
            height=6,
            width=28,
            font=("微软雅黑", 11),
            exportselection=False
        )
        self.font_list.pack()

        for font_name in FONTS:
            self.font_list.insert(tk.END, font_name)

        self.font_list.selection_set(0)

        # 操作按钮
        button_frame = tk.Frame(self.root)
        button_frame.pack(pady=15)

        tk.Button(
            button_frame,
            text="生成签名",
            width=12,
            command=self.generate_signature
        ).pack(side=tk.LEFT, padx=5)

        tk.Button(
            button_frame,
            text="保存图片",
            width=12,
            command=self.save_image
        ).pack(side=tk.LEFT, padx=5)

        tk.Button(
            button_frame,
            text="清空",
            width=10,
            command=self.clear
        ).pack(side=tk.LEFT, padx=5)

        tk.Button(
            button_frame,
            text="退出",
            width=10,
            command=self.close
        ).pack(side=tk.LEFT, padx=5)

        # 签名展示区域
        tk.Label(
            self.root,
            text="签名预览",
            font=("微软雅黑", 12)
        ).pack(pady=(5, 3))

        self.preview = tk.Label(
            self.root,
            text="生成后的签名将在这里显示",
            bg="#F5F5F5",
            width=55,
            height=12,
            relief=tk.GROOVE
        )
        self.preview.pack(
            padx=20,
            pady=5,
            fill=tk.BOTH,
            expand=True
        )

        # 状态栏
        self.status = tk.StringVar(value="就绪")
        tk.Label(
            self.root,
            textvariable=self.status,
            anchor="w",
            relief=tk.SUNKEN
        ).pack(side=tk.BOTTOM, fill=tk.X)

        # 回车生成签名
        self.name_entry.bind(
            "<Return>",
            lambda event: self.generate_signature()
        )

    # 获取安全的文件名
    @staticmethod
    def safe_filename(name):
        name = re.sub(r'[\\/:*?"<>|]', "_", name)
        return name.strip(" .") or "signature"

    # 按钮点击事件:生成签名
    def generate_signature(self):
        name = self.name_entry.get().strip()
        selection = self.font_list.curselection()

        if not name:
            messagebox.showwarning("提示", "请输入你的姓名!")
            return

        if not selection:
            messagebox.showwarning("提示", "请先选择一种字体!")
            return

        font_name = self.font_list.get(selection[0])
        font_id = FONTS[font_name]

        self.status.set("正在生成签名,请稍候……")
        self.preview.config(text="正在生成……", image="")
        self.signature_photo = None

        # 网络请求放到后台线程,避免界面卡死
        thread = threading.Thread(
            target=self._download_signature,
            args=(name, font_id),
            daemon=True
        )
        thread.start()

    # 请求 API 并下载签名图片
    def _download_signature(self, name, font_id):
        try:
            data = {
                "id": name,
                "zhenbi": "20191123",
                "id1": font_id,
                "id2": "#FFFFFF",
                "id4": "#FF0000",
                "id6": "#0000FF",
            }

            response = requests.post(
                API_URL,
                headers=HEADERS,
                data=data,
                timeout=20
            )
            response.raise_for_status()
            html = response.text

            # 兼容 img 标签中的空格、单引号和双引号
            image_pattern = (
                r"<img\b[^>]*?\bsrc\s*=\s*"
                r"""(['"])(.*?)\1"""
            )
            match = re.search(
                image_pattern,
                html,
                flags=re.IGNORECASE | re.DOTALL
            )

            if not match:
                raise ValueError(
                    "没有在网页返回内容中找到签名图片,"
                    "可能是接口已变更或请求失败。"
                )

            image_url = urljoin(API_URL, match.group(2))

            image_response = requests.get(
                image_url,
                headers=HEADERS,
                timeout=20
            )
            image_response.raise_for_status()

            # 检查返回内容是否为有效图片
            image = Image.open(
                __import__("io").BytesIO(image_response.content)
            )
            image.load()

            # 统一保存为 GIF,兼容原程序的输出习惯
            filename = self.safe_filename(name) + ".gif"
            save_path = SAVE_DIR / filename

            image.save(save_path, format="GIF")

            # 将结果交回主线程更新界面
            self.root.after(
                0,
                lambda: self._show_signature(save_path)
            )

        except Exception as error:
            error_message = str(error)
            self.root.after(
                0,
                lambda msg=error_message: self._show_error(msg)
            )

    # 显示签名图片
    def _show_signature(self, image_path):
        try:
            image = Image.open(image_path).convert("RGBA")
            image.thumbnail((520, 280))

            self.signature_photo = ImageTk.PhotoImage(image)

            self.preview.config(
                image=self.signature_photo,
                text=""
            )
            self.status.set(f"生成成功:{image_path}")

        except Exception as error:
            self._show_error(f"图片显示失败:{error}")

    # 显示错误信息
    def _show_error(self, error):
        self.status.set("生成失败")
        self.preview.config(
            image="",
            text="签名生成失败,请检查网络或接口状态。"
        )
        self.signature_photo = None

        messagebox.showerror(
            "生成失败",
            f"无法生成签名:\n{error}"
        )

    # 保存图片到指定位置
    def save_image(self):
        selection = self.font_list.curselection()
        name = self.name_entry.get().strip()

        if not name or self.signature_photo is None:
            messagebox.showwarning("提示", "请先生成签名!")
            return

        source_path = SAVE_DIR / (
            self.safe_filename(name) + ".gif"
        )

        if not source_path.exists():
            messagebox.showwarning("提示", "找不到签名图片,请重新生成。")
            return

        save_path = filedialog.asksaveasfilename(
            title="保存签名图片",
            initialfile=self.safe_filename(name) + ".gif",
            defaultextension=".gif",
            filetypes=[
                ("GIF 图片", "*.gif"),
                ("PNG 图片", "*.png"),
                ("JPEG 图片", "*.jpg;*.jpeg"),
            ]
        )

        if not save_path:
            return

        try:
            image = Image.open(source_path)
            suffix = Path(save_path).suffix.lower()

            if suffix in (".jpg", ".jpeg"):
                image = image.convert("RGB")
                image.save(save_path, format="JPEG")
            elif suffix == ".png":
                image.save(save_path, format="PNG")
            else:
                image.save(save_path, format="GIF")

            self.status.set(f"图片已保存:{save_path}")
            messagebox.showinfo("成功", "签名图片保存成功!")

        except Exception as error:
            messagebox.showerror(
                "保存失败",
                f"图片保存失败:\n{error}"
            )

    # 清空输入和预览
    def clear(self):
        self.name_entry.delete(0, tk.END)
        self.preview.config(
            image="",
            text="生成后的签名将在这里显示"
        )
        self.signature_photo = None
        self.status.set("已清空")

    # 退出程序
    def close(self):
        if messagebox.askokcancel("提示", "真的要退出吗?"):
            self.root.destroy()


# ==================== 程序入口 ====================

def main():
    root = tk.Tk()
    app = SignatureGenerator(root)
    root.mainloop()


if __name__ == "__main__":
    main()

# ==================== 输入框 ====================

label2 = Label(
    root,
    text="请输入你的姓名:",
    font=("微软雅黑", 13)
)
label2.grid(row=1, column=0, sticky=E, pady=10)

entry = Entry(root, width=50)
entry.grid(row=1, column=1, padx=20, pady=20)


# ==================== 操作按钮 ====================

# 生成签名
Button(
    root,
    text="生  成",
    width=10,
    command=print_selection
).grid(row=2, column=0, sticky=E, pady=10)

# 清空输入和显示内容
Button(
    root,
    text="清  空",
    width=10,
    command=clear
).grid(row=2, column=1, pady=10)

# 退出程序
Button(
    root,
    text="退  出",
    width=10,
    command=close
).grid(row=2, column=2, sticky=W, pady=10)


# ==================== 字体列表 ====================

lb = Listbox(root, height=10, exportselection=False)
lb.grid(row=3, column=0, padx=20, pady=20)

# 将字体名称添加到列表
for key in option:
    lb.insert(END, key)

# 默认选中第一种字体
if option:
    lb.selection_set(0)


# ==================== 签名显示区域 ====================

text = Text(root, width=80, height=25)
text.grid(
    row=3,
    column=1,
    columnspan=2,
    padx=20,
    pady=5
)


# ==================== 启动程序 ====================

root.mainloop()

总结

这样,个性签名生成器就制作完成了,这次我们深入使用字典对数据进行数据整理,使用requests的post请求携带数据并访问,同时还知道了如何设置UA身份标识,最关键的是我们学会了使用tkinter库中的listbox功能,学会了选择文本作为输入内容,本次我们只是将文字内容和字体内容做了修改,你还可以通过修改字体颜色和、描边颜色和背景颜色搭配出更加有趣的效果来。相信你还会有更多精彩的创意,赶快去试试吧 造物让生活更美好,我们下期再见

下载附件个性签名生成器.rar

20261021144818870-image

© 版权声明
THE END


微信扫描下方的二维码免费下载资源

喜欢就支持一下吧
点赞31 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情图片快捷回复

    暂无评论内容