import traceback
from PIL import Image, ImageTk
import qrcode
from barcode import Code128, Code39, EAN13, EAN8, ISBN13
from barcode.writer import ImageWriter
from pyzbar.pyzbar import decode
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import os
import time
import tempfile
import sys
import platform
# 获取系统字体路径
def get_system_font():
system = platform.system()
if system == "Windows":
font_paths = [
"C:\\Windows\\Fonts\\arial.ttf",
"C:\\Windows\\Fonts\\simhei.ttf", # 黑体
"C:\\Windows\\Fonts\\simsun.ttc", # 宋体
]
else: # Linux/Mac
font_paths = [
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/System/Library/Fonts/Helvetica.ttc",
]
# 尝试找到可用的字体
for font_path in font_paths:
if os.path.exists(font_path):
return font_path
return None
class BarcodeGUI:
def __init__(self, parent):
self.frame = ttk.Frame(parent)
if getattr(sys, 'frozen', False):
# 如果是打包后的exe
app_path = os.path.dirname(sys.executable)
else:
# 如果是python脚本
app_path = os.path.dirname(os.path.abspath(__file__))
self.temp_file = os.path.join(app_path, "temp_barcode.png")
self.barcode_types = {
"CODE128": Code128,
"CODE39": Code39,
"EAN13": EAN13,
"EAN8": EAN8,
"ISBN13": ISBN13
}
self.create_widgets()
def create_widgets(self):
# 上半部分(生成条形码)
top_frame = ttk.LabelFrame(self.frame, text="生成条形码", padding=10)
top_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=5, sticky="nsew")
top_frame.grid_columnconfigure(0, weight=1)
top_frame.grid_columnconfigure(1, weight=1)
# 左侧输入区域
input_frame = ttk.Frame(top_frame)
input_frame.grid(row=0, column=0, padx=5, sticky="nsew")
ttk.Label(input_frame, text="输入内容:").pack(anchor="w")
self.text_input = tk.Text(input_frame, width=40, height=5)
self.text_input.pack(fill="x", pady=5)
# 条形码类型选择
type_frame = ttk.Frame(input_frame)
type_frame.pack(fill="x", pady=2)
ttk.Label(type_frame, text="条形码类型:").pack(side="left")
self.type_var = tk.StringVar(value="CODE128")
type_choices = ["CODE128", "CODE39", "EAN13", "EAN8", "ISBN13"]
type_menu = ttk.OptionMenu(type_frame, self.type_var, "CODE128", *type_choices)
type_menu.pack(side="left", padx=5)
ttk.Button(input_frame, text="生成条形码", command=self.generate_barcode).pack(anchor="w", pady=5)
# 右侧预览区域
preview_frame = ttk.Frame(top_frame)
preview_frame.grid(row=0, column=1, padx=5, sticky="nsew")
ttk.Label(preview_frame, text="条形码预览").pack()
self.preview_label = ttk.Label(preview_frame)
self.preview_label.pack(pady=5)
self.save_button = ttk.Button(preview_frame, text="另存为...", command=self.save_as, state="disabled")
self.save_button.pack(pady=5)
# 下半部分(解析条形码)
bottom_frame = ttk.LabelFrame(self.frame, text="解析条形码", padding=10)
bottom_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=5, sticky="nsew")
bottom_frame.grid_columnconfigure(0, weight=1)
bottom_frame.grid_columnconfigure(1, weight=1)
# 左侧选择文件区域
decode_left_frame = ttk.Frame(bottom_frame)
decode_left_frame.grid(row=0, column=0, padx=5, sticky="nsew")
ttk.Button(decode_left_frame, text="选择条形码图片", command=self.select_and_decode).pack(anchor="w")
self.decode_preview_label = ttk.Label(decode_left_frame)
self.decode_preview_label.pack(pady=5)
# 右侧结果显示区域
decode_right_frame = ttk.Frame(bottom_frame)
decode_right_frame.grid(row=0, column=1, padx=5, sticky="nsew")
ttk.Label(decode_right_frame, text="解析结果").pack(anchor="w")
self.result_text = tk.Text(decode_right_frame, height=10, width=40)
self.result_text.pack(fill="both", expand=True, pady=5)
# 状态栏
status_frame = ttk.Frame(self.frame)
status_frame.grid(row=2, column=0, columnspan=2, padx=10, pady=5, sticky="ew")
self.status_text = ttk.Label(status_frame, text="")
self.status_text.pack(fill="x")
def generate_barcode(self):
data = self.text_input.get("1.0", tk.END).strip()
if not data:
messagebox.showwarning("警告", "请输入要生成的内容!")
return
try:
barcode_class = self.barcode_types[self.type_var.get()]
# 创建 ImageWriter 并设置字体
writer = ImageWriter()
font_path = get_system_font()
if font_path:
writer.set_options({
'font_path': font_path,
'font_size': 13,
'text_distance': 1,
'quiet_zone': 3
})
if self.type_var.get() == "CODE128":
try:
data = data.encode('utf-8').decode('ascii')
except UnicodeEncodeError:
messagebox.showerror("错误", "CODE128 不支持中文字符,请使用英文字母、数字或符号")
return
barcode_instance = barcode_class(data, writer=writer)
else:
if self.type_var.get() in ["EAN13", "EAN8"]:
if not data.isdigit():
messagebox.showerror("错误", "EAN码只能包含数字!")
return
if self.type_var.get() == "EAN13" and len(data) != 12:
messagebox.showerror("错误", "EAN13需要12位数字!")
return
if self.type_var.get() == "EAN8" and len(data) != 7:
messagebox.showerror("错误", "EAN8需要7位数字!")
return
barcode_instance = barcode_class(data, writer=writer)
try:
# 确保文件名不包含 .png 后缀
filename = os.path.splitext(self.temp_file)[0]
# 保存条形码
barcode_instance.save(filename)
# 打开生成的图片
image = Image.open(filename + '.png')
self.show_preview(image)
self.status_text.configure(text=f"条形码已保存为: {filename}.png")
self.save_button.configure(state="normal")
except Exception as e:
traceback.print_exc()
messagebox.showerror("错误", f"保存条形码失败: {str(e)}")
except Exception as e:
traceback.print_exc()
messagebox.showerror("错误", f"生成条形码失败: {str(e)}\n注意:非CODE128类型不支持中文")
def show_preview(self, pil_image, is_decode=False):
# 调整预览图片大小
preview_size = (300, 150) # 条形码预览尺寸
pil_image = pil_image.resize(preview_size, Image.LANCZOS)
if is_decode:
self.decode_preview_image = ImageTk.PhotoImage(pil_image)
self.decode_preview_label.configure(image=self.decode_preview_image)
else:
self.preview_image = ImageTk.PhotoImage(pil_image)
self.preview_label.configure(image=self.preview_image)
def select_and_decode(self):
filename = filedialog.askopenfilename(
filetypes=[("Image files", "*.png *.jpg *.jpeg *.gif *.bmp")],
title="选择条形码图片"
)
if filename:
try:
image = Image.open(filename)
result = decode(image)
if result:
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, result[0].data.decode('utf-8'))
self.show_preview(image, is_decode=True)
self.status_text.configure(text=f"已选择文件: {filename}")
else:
messagebox.showwarning("警告", "未能识别条形码!")
except Exception as e:
messagebox.showerror("错误", f"解析失败: {str(e)}")
def save_as(self):
temp_file_with_ext = self.temp_file.replace('.png', '') + '.png'
if not os.path.exists(temp_file_with_ext):
messagebox.showerror("错误", "请先生成条形码!")
return
filename = filedialog.asksaveasfilename(
defaultextension=".png",
filetypes=[("PNG files", "*.png"),
("JPEG files", "*.jpg;*.jpeg"),
("All files", "*.*")],
title="保存条形码"
)
if filename:
try:
with open(temp_file_with_ext, 'rb') as src, open(filename, 'wb') as dst:
dst.write(src.read())
self.status_text.configure(text=f"条形码已保存到: {filename}")
messagebox.showinfo("成功", "条形码保存成功!")
except Exception as e:
messagebox.showerror("错误", f"保存失败: {str(e)}")
class MainApp:
def __init__(self):
self.window = tk.Tk()
self.window.title("二维码/条形码生成解析器1.01 - by silent 【吾爱破解】")
self.window.resizable(True, True)
# 设置程序图标
icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.ico")
if os.path.exists(icon_path):
self.window.iconbitmap(icon_path)
# 设置窗口大小和位置
window_width = 577
window_height = 743
# 获取屏幕尺寸
screen_width = self.window.winfo_screenwidth()
screen_height = self.window.winfo_screenheight()
# 计算窗口居中位置
center_x = int((screen_width - window_width) / 2)
center_y = int((screen_height - window_height) / 2)
# 设置窗口位置和大小
self.window.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')
# 创建选项卡
self.notebook = ttk.Notebook(self.window)
self.notebook.pack(fill='both', expand=True)
# 创建二维码和条形码标签页
self.qr_gui = QRCodeGUI(self.notebook)
self.barcode_gui = BarcodeGUI(self.notebook)
self.notebook.add(self.qr_gui.frame, text='二维码')
self.notebook.add(self.barcode_gui.frame, text='条形码')
def run(self):
self.window.mainloop()
class QRCodeGUI:
def __init__(self, parent):
self.frame = ttk.Frame(parent)
if getattr(sys, 'frozen', False):
# 如果是打包后的exe
app_path = os.path.dirname(sys.executable)
else:
# 如果是python脚本
app_path = os.path.dirname(os.path.abspath(__file__))
self.temp_file = os.path.join(app_path, "temp_qr.png")
self.create_widgets()
def create_widgets(self):
# 上半部分(生成二维码)
top_frame = ttk.LabelFrame(self.frame, text="生成二维码", padding=10)
top_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=5, sticky="nsew")
top_frame.grid_columnconfigure(0, weight=1)
top_frame.grid_columnconfigure(1, weight=1)
# 左侧输入区域
input_frame = ttk.Frame(top_frame)
input_frame.grid(row=0, column=0, padx=5, sticky="nsew")
ttk.Label(input_frame, text="输入内容:").pack(anchor="w")
self.text_input = tk.Text(input_frame, width=40, height=5)
self.text_input.pack(fill="x", pady=5)
# 参数设置框架
params_frame = ttk.Frame(input_frame)
params_frame.pack(fill="x", pady=5)
# Logo选择和设置
logo_frame = ttk.Frame(params_frame)
logo_frame.pack(fill="x", pady=2)
ttk.Label(logo_frame, text="Logo:").pack(side="left")
self.logo_path = None
self.logo_button = ttk.Button(logo_frame, text="选择Logo", command=self.select_logo)
self.logo_button.pack(side="left", padx=5)
self.remove_logo_button = ttk.Button(logo_frame, text="移除Logo", command=self.remove_logo, state="disabled")
self.remove_logo_button.pack(side="left", padx=5)
# Logo尺寸比例选择
logo_size_frame = ttk.Frame(params_frame)
logo_size_frame.pack(fill="x", pady=2)
ttk.Label(logo_size_frame, text="Logo尺寸:").pack(side="left")
self.logo_size_var = tk.StringVar(value="25%")
logo_size_choices = ["5%", "10%", "15%", "20%", "25%", "30%", "35%",
"40%", "45%", "50%", "55%", "60%", "65%", "70%"]
logo_size_menu = ttk.OptionMenu(logo_size_frame, self.logo_size_var, "25%", *logo_size_choices)
logo_size_menu.pack(side="left", padx=5)
ttk.Label(logo_size_frame, text="(占二维码码点区域的比例)").pack(side="left")
# 容错率选择
error_frame = ttk.Frame(params_frame)
error_frame.pack(fill="x", pady=2)
ttk.Label(error_frame, text="容错率:").pack(side="left")
self.error_var = tk.StringVar(value="30%")
error_choices = ["7%", "15%", "25%", "30%"]
error_menu = ttk.OptionMenu(error_frame, self.error_var, "30%", *error_choices)
error_menu.pack(side="left", padx=5)
# 码边距选择
border_frame = ttk.Frame(params_frame)
border_frame.pack(fill="x", pady=2)
ttk.Label(border_frame, text="码边距:").pack(side="left")
self.border_var = tk.StringVar(value="2个色块")
border_choices = ["1个色块", "2个色块", "3个色块", "4个色块"]
border_menu = ttk.OptionMenu(border_frame, self.border_var, "2个色块", *border_choices)
border_menu.pack(side="left", padx=5)
# 尺寸选择
size_frame = ttk.Frame(params_frame)
size_frame.pack(fill="x", pady=2)
ttk.Label(size_frame, text="尺寸:").pack(side="left")
self.size_var = tk.StringVar(value="400×400px")
size_choices = ["300×300px", "400×400px", "500×500px", "1000×1000px"]
size_menu = ttk.OptionMenu(size_frame, self.size_var, "400×400px", *size_choices)
size_menu.pack(side="left", padx=5)
ttk.Button(input_frame, text="生成二维码", command=self.generate_qr).pack(anchor="w", pady=5)
# 右侧预览区域
preview_frame = ttk.Frame(top_frame)
preview_frame.grid(row=0, column=1, padx=5, sticky="nsew")
ttk.Label(preview_frame, text="二维码预览").pack()
self.preview_label = ttk.Label(preview_frame)
self.preview_label.pack(pady=5)
self.save_button = ttk.Button(preview_frame, text="另存为...", command=self.save_as, state="disabled")
self.save_button.pack(pady=5)
# 下半部分(解析二维码
bottom_frame = ttk.LabelFrame(self.frame, text="解析二维码", padding=10)
bottom_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=5, sticky="nsew")
bottom_frame.grid_columnconfigure(0, weight=1)
bottom_frame.grid_columnconfigure(1, weight=1)
# 左侧选择文件区域
decode_left_frame = ttk.Frame(bottom_frame)
decode_left_frame.grid(row=0, column=0, padx=5, sticky="nsew")
ttk.Button(decode_left_frame, text="选择二维码图片", command=self.select_and_decode).pack(anchor="w")
self.decode_preview_label = ttk.Label(decode_left_frame)
self.decode_preview_label.pack(pady=5)
# 右侧结果显示区域
decode_right_frame = ttk.Frame(bottom_frame)
decode_right_frame.grid(row=0, column=1, padx=5, sticky="nsew")
ttk.Label(decode_right_frame, text="解析结果").pack(anchor="w")
self.result_text = tk.Text(decode_right_frame, height=10, width=40)
self.result_text.pack(fill="both", expand=True, pady=5)
# 状态栏
status_frame = ttk.Frame(self.frame)
status_frame.grid(row=2, column=0, columnspan=2, padx=10, pady=5, sticky="ew")
self.status_text = ttk.Label(status_frame, text="")
self.status_text.pack(fill="x")
def generate_qr(self):
data = self.text_input.get("1.0", tk.END).strip()
# 在 Python 3 中,从 Tkinter 的 Text 小部件获取的文本(例如通过 self.text_input.get("1.0", tk.END).strip())是一个 Unicode 字符串。
# 这意味着 data 是以 Python 内部的 Unicode 格式表示的。
if not data:
messagebox.showwarning("警告", "请输入要生成的内容!")
return
# 获取参数设置
error_level = {
"7%": qrcode.constants.ERROR_CORRECT_L,
"15%": qrcode.constants.ERROR_CORRECT_M,
"25%": qrcode.constants.ERROR_CORRECT_Q,
"30%": qrcode.constants.ERROR_CORRECT_H
}[self.error_var.get()]
# 获取码边距设置
border = int(self.border_var.get().replace("个色块", ""))
# 解析尺寸设置
size_str = self.size_var.get().replace("px", "").split("×")[0]
box_size = int(int(size_str) / 25) # 根据目标尺寸计算box_size
try:
# 获取Logo尺寸比例
logo_size_percent = float(self.logo_size_var.get().replace('%', '')) / 100
qr_image = QRCodeTool.generate_qr(
data.encode('utf-8'),
self.temp_file,
error_correction=error_level,
box_size=box_size,
border=border,
logo_path=self.logo_path,
logo_size_percent=logo_size_percent
)
self.show_preview(qr_image)
self.status_text.configure(text=f"二维码已保存为: {self.temp_file}")
self.save_button.configure(state="normal")
except Exception as e:
messagebox.showerror("错误", f"生成二维码失败: {str(e)}")
def show_preview(self, pil_image, is_decode=False):
# 调整预览图片大小
preview_size = (200, 200)
pil_image = pil_image.resize(preview_size, Image.LANCZOS)
if is_decode:
self.decode_preview_image = ImageTk.PhotoImage(pil_image)
self.decode_preview_label.configure(image=self.decode_preview_image)
else:
self.preview_image = ImageTk.PhotoImage(pil_image)
self.preview_label.configure(image=self.preview_image)
def select_and_decode(self):
filename = filedialog.askopenfilename(
filetypes=[("Image files", "*.png *.jpg *.jpeg *.gif *.bmp")],
title="选择二维码图片"
)
if filename:
try:
image = Image.open(filename)
result = decode(image)
if result:
self.result_text.delete(1.0, tk.END)
print(result[0].data)
self.result_text.insert(tk.END, result[0].data.decode('utf-8'))
self.show_preview(image, is_decode=True)
self.status_text.configure(text=f"已选择文件: {filename}")
else:
messagebox.showwarning("警告", "未能识别二维码!")
except Exception as e:
messagebox.showerror("错误", f"解析失败: {str(e)}")
def save_as(self):
if not os.path.exists(self.temp_file):
messagebox.showerror("错误", "请先生成二维码!")
return
filename = filedialog.asksaveasfilename(
defaultextension=".png",
filetypes=[("PNG files", "*.png"),
("JPEG files", "*.jpg;*.jpeg"),
("All files", "*.*")],
title="保存二维码"
)
if filename:
try:
with open(self.temp_file, 'rb') as src, open(filename, 'wb') as dst:
dst.write(src.read())
self.status_text.configure(text=f"二维码已保存到: {filename}")
messagebox.showinfo("成功", "二维码保存成功!")
except Exception as e:
messagebox.showerror("错误", f"保存失败: {str(e)}")
def select_logo(self):
"""选择Logo图片"""
filename = filedialog.askopenfilename(
filetypes=[("Image files", "*.png *.jpg *.jpeg *.gif *.bmp")],
title="选择Logo图片"
)
if filename:
self.logo_path = filename
self.remove_logo_button.configure(state="normal")
self.status_text.configure(text=f"已选择Logo: {filename}")
def remove_logo(self):
"""移除Logo"""
self.logo_path = None
self.remove_logo_button.configure(state="disabled")
self.status_text.configure(text="已移除Logo")
class QRCodeTool:
@staticmethod
def generate_qr(data, filename="qrcode.png", error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10, border=4, logo_path=None, logo_size_percent=0.25):
"""
生成二维码
:param data: 要编码的数据
:param filename: 保存的文件名
:param error_correction: 容错率
:param box_size: 像素大小
:param border: 码边距(边框宽度)
:param logo_path: Logo图片路径
:param logo_size_percent: Logo尺寸比例(相对于二维码码点区域)
:return: 生成的图片对象
"""
qr = qrcode.QRCode(
version=1,
error_correction=error_correction,
box_size=box_size,
border=border,
)
qr.add_data(data)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
# 如果有Logo,添加到二维码中心
if logo_path:
try:
# 打开Logo图片
logo = Image.open(logo_path)
# 计算二维码码点区域的大小(不包含边距)
qr_width, qr_height = qr_image.size
code_width = qr_width - (2 * border * box_size) # 减去边距后的实际码点区域宽度
# 计算Logo大小(使用码点区域的百分比)
logo_size = int(code_width * logo_size_percent)
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
# 计算Logo放置位置(居中)
pos_x = (qr_width - logo_size) // 2
pos_y = (qr_height - logo_size) // 2
# 将二维码转换为RGBA模式
qr_image = qr_image.convert("RGBA")
# 在二维码上绘制Logo
qr_image.paste(logo, (pos_x, pos_y), logo if logo.mode == 'RGBA' else None)
except Exception as e:
raise Exception(f"添加Logo失败: {str(e)}")
qr_image.save(filename)
return qr_image
if __name__ == "__main__":
app = MainApp()
app.run()