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()
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!