python打印条码,打印出来很糊
import os
import tkinter as tk
from tkinter import messagebox, ttk
from PIL import ImageTk, Image, ImageDraw,ImageFont,ImageWin
import pandas as pd
import qrcode
from typing_extensions import LiteralString
import win32ui
import win32print
# 二维码生成函数
def generate_qr_code(data: str, output_path=None):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=50,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img = img.resize((250, 250), Image.Resampling.LANCZOS)
if output_path is None:
temp_file = "temp_qrcode.png"
img.save(temp_file)
return temp_file
else:
img.save(output_path)
return output_path
def create_labeled_qrcode(data: str, label_text: str):
# 1. 生成基础二维码
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=50,
border=4,
)
qr.add_data(data)
qr_img = qr.make_image(fill_color="black", back_color="white").convert("RGB")
# 2. 调整二维码大小
qr_img = qr_img.resize((4000, 4000), Image.Resampling.LANCZOS)
# 3. 创建新图像,用于放置二维码 + 文本,设定总体尺寸
text_area_width = 3000 # 右侧文字区域宽度
total_width = qr_img.width + text_area_width # 总宽
total_height = max(qr_img.height, 3000) # 总高,确保能容纳下所有内容
new_img = Image.new('RGB', (total_width, total_height), color='white')
# 4. 添加文本
draw = ImageDraw.Draw(new_img)
#font = ImageFont.load_default().font_variant(size=16) # 使用默认字体并设置字号
# 如果需要支持中文,请使用支持中文的字体文件
font = ImageFont.truetype("/path/to/supported/simhei.ttf", size=400)
# 拆分 label_text 为多行显示
lines = label_text.split('\n')
x_start = 450 # 开始x坐标,在二维码右侧留一些空白
y_start = 400 # 开始y坐标
line_height = 450 # 行间距
for line in lines:
draw.text((x_start, y_start), line, fill="black", font=font)
y_start += line_height # 更新y坐标
# 5. 将二维码粘贴到右侧并垂直居中
qr_position_x = text_area_width + 500 # 二维码从文字区域之后开始
qr_position_y = (total_height - qr_img.height) // 2
new_img.paste(qr_img, (qr_position_x, qr_position_y))
return new_img
#文本处理分行
def process_label_text(label_text: object, chars_per_line: object = 10) -> LiteralString:
# 第一步:在每个冒号 : 后插入换行符
lines = label_text.split('\n')
processed_lines = []
for line in lines:
if ':' in line:
parts = line.split(':', 1)
key = parts[0] + ":"
value = parts[1]
# print(key)
#print(value)
# 在冒号后插入换行
processed_lines.append(key)
processed_lines.append(value.strip())
else:
# 没有冒号的行直接保留
processed_lines.append(line)
# 第二步:对每一行进行字符限制,每 chars_per_line 字符换行
final_lines = []
for line in processed_lines:
# 按每10个字符拆分
chunks = [line[i:i + chars_per_line] for i in range(0, len(line), chars_per_line)]
final_lines.extend(chunks)
return '\n'.join(final_lines)
#调用打印
def mm_to_pixel(mm, dpi):
return int(mm / 25.4 * dpi)
def print_fixed_label(qr_img,code):
TARGET_WIDTH_MM = 70
TARGET_HEIGHT_MM = 40
PRINTER_DPI = 300
width_px = mm_to_pixel(TARGET_WIDTH_MM, PRINTER_DPI)
height_px = mm_to_pixel(TARGET_HEIGHT_MM, PRINTER_DPI)
# 调整图像尺寸
resized_img = qr_img.resize((width_px, height_px), Image.Resampling.LANCZOS)
# 获取默认打印机
printer_name = win32print.GetDefaultPrinter()
hDC = win32ui.CreateDC()
hDC.CreatePrinterDC(printer_name)
hDC.StartDoc(code)
hDC.StartPage()
# 获取页面尺寸
page_width = hDC.GetDeviceCaps(110)
page_height = hDC.GetDeviceCaps(111)
print(page_width, page_height)
# 居中绘制图像
#x = (page_width - resized_img.width) // 2
#y = (page_height - resized_img.height) // 2
x = 0
y = 0
dib = ImageWin.Dib(resized_img)
dib.draw(hDC.GetHandleOutput(), (x, y, x + resized_img.width, y + resized_img.height))
hDC.EndPage()
hDC.EndDoc()
hDC.DeleteDC()
# GUI 应用类
class BarcodeApp(tk.Tk):
def __init__(self, csv_path):
super().__init__()
self.title("货位码打印工具")
self.geometry("500x500")
# 尝试加载CSV
self.df = None
self.load_csv(csv_path)
if self.df is None:
self.destroy()
return
# 预览图像区域
self.preview_canvas = tk.Canvas(self, width=210, height=120, bg="white")
self.preview_canvas.pack(side=tk.RIGHT, padx=10, pady=10)
# 工厂选择框
self.factory_var = tk.StringVar()
self.factory_combobox = ttk.Combobox(self, textvariable=self.factory_var)
self.factory_combobox.pack(pady=5)
self.factory_combobox.bind("<<ComboboxSelected>>", self.on_factory_selected) # 绑定事件
# 库区描述选择框
self.zone_var = tk.StringVar()
self.zone_combobox = ttk.Combobox(self, textvariable=self.zone_var)
self.zone_combobox.pack(pady=5)
self.zone_combobox.bind("<<ComboboxSelected>>", self.on_combobox_select) # 绑定事件
# 货位码列表框
self.listbox = tk.Listbox(self, selectmode=tk.MULTIPLE, height=15, width=30)
self.listbox.pack(pady=10) # 不再 expand=True, fill=tk.BOTH
# 打印按钮
self.button_print = tk.Button(self, text="打印选中的货位码", command=self.print_selected_codes)
self.button_print.pack(pady=10)
# 初始化选项
factories = sorted(self.df['工厂'].unique())
zones = sorted(self.df['库区描述'].unique())
self.factory_combobox['values'] = factories
self.zone_combobox['values'] = zones
# 设置默认值
if factories:
#self.factory_var.set(factories[0])
self.factory_var.set('南一')
if zones:
#self.zone_var.set(zones[0])
self.zone_var.set('南一其他物料区')
self.refresh_list() # 初始化时刷新列表
def load_csv(self, csv_path):
try:
self.df = pd.read_csv(csv_path, encoding='utf-8')
# 将所有 NaN 值替换为空字符串
self.df.fillna('', inplace=True)
print("CSV 加载成功")
required_columns = ['工厂', '库区描述', '货位码', 'AGV对照编号', '备注']
for col in required_columns:
if col not in self.df.columns:
raise ValueError(f"CSV 文件缺少必要列:{col}")
except Exception as e:
messagebox.showerror("错误", f"无法加载 CSV 文件:{e}\n请确保文件存在且格式正确。")
self.df = None
def on_factory_selected(self, event=None):
factory = self.factory_var.get()
if self.df is not None:
# 获取该工厂下的所有库区描述
zones = sorted(self.df[self.df['工厂'] == factory]['库区描述'].unique())
self.zone_combobox['values'] = zones
if zones:
self.zone_combobox.current(0)
else:
self.zone_combobox.set('') # 没有库区时清空
# 自动刷新列表
self.refresh_list()
def on_combobox_select(self, event=None):
# 自动刷新列表
self.refresh_list()
def refresh_list(self):
factory = self.factory_var.get()
zone = self.zone_var.get()
if self.df is not None:
filtered_df = self.df[(self.df['工厂'] == factory) & (self.df['库区描述'] == zone)]
# print(f"Filtered DataFrame:\n{filtered_df}")
self.listbox.delete(0, tk.END)
for index, row in filtered_df.iterrows():
remark = row['备注'] if pd.notna(row['备注']) else ""
self.listbox.insert(tk.END, f"{row['货位码']} - {row['AGV对照编号']} - {remark}")
# 绑定点击事件,用于预览
self.listbox.bind("<<ListboxSelect>>", self.on_listbox_select)
def on_listbox_select(self, event):
selected_indices = self.listbox.curselection()
if selected_indices:
self.update_preview(selected_indices[0])
def update_preview(self, index):
item = self.listbox.get(index)
parts = item.split(' - ')
code = parts[0]
label_text = f"库区描述: {self.zone_var.get()}\n货位编码: {parts[0]}\n站点:{parts[1]}\n备注:{parts[2]}"
# 处理后的文本
processed_text = process_label_text(label_text, chars_per_line=15)
label_text = processed_text
# 生成带标签的二维码图片
qr_img = create_labeled_qrcode(code, label_text)
# 清除之前的图像并绘制新图像
self.preview_canvas.delete("all")
# 根据 Canvas 大小调整图像尺寸
display_img = qr_img.resize((198, 113), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(display_img)
# 直接在 Canvas 中心(0, 0)位置创建图像
self.preview_canvas.create_image(0, 0, image=photo, anchor=tk.NW)
self.preview_canvas.image = photo # 保持引用防止被垃圾回收
def print_selected_codes(self):
selected_indices = self.listbox.curselection()
if not selected_indices:
messagebox.showwarning("警告", "请选择至少一个货位码")
return
# 用于存储所有生成的二维码图像
image_list = []
for i in selected_indices:
item = self.listbox.get(i)
parts = item.split(' - ')
code = parts[0]
label_text = f"库区描述: {self.zone_var.get()}\n货位编码: {parts[0]}\nAGV点位:{parts[1]}\n备注:{parts[2]}"
# 处理后的文本
processed_text = process_label_text(label_text, chars_per_line=15)
label_text = processed_text
qr_img = create_labeled_qrcode(code, label_text)
resized_img = qr_img.resize((198, 113), Image.Resampling.LANCZOS) # PIL resize
# 打印图像列表中的图像
print_fixed_label(resized_img,code)
if __name__ == "__main__":
# 指定CSV文件路径(默认为当前目录下的 AGVhuoweima.csv)
current_dir = os.path.dirname(os.path.abspath(__file__))
csv_file_name = "AGVkuweima.csv"
csv_path = os.path.join(current_dir, csv_file_name)
app = BarcodeApp(csv_path)
app.mainloop()
推荐文章: