You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python条码打印脚本IDLE运行正常,PyInstaller打包后无法生成条码

问题:PyInstaller打包条码工具后无法生成条码(OSError: cannot open resource)

我几乎没编程经验,靠GPT4写了个库存管理用的条码打印工具,功能是生成Code39条码,每次打印编号自动加1。脚本在IDLE里运行正常,但用PyInstaller打包后没法生成条码图片,CMD里抛出「OSError: cannot open resource」错误。脚本能正常生成last_number.txt和barcodes文件夹,但条码生成失败,以下是完整代码和报错栈信息:

完整代码

import tkinter as tk
from barcode import Code39
from barcode.writer import ImageWriter
from PIL import Image, ImageFont, ImageDraw
import sys
import os
import time
import threading

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

font_path = resource_path('resources/ARLRDBD.ttf')


def get_next_number(filename):
    try:
        with open(filename, 'r') as f:
            lines = f.read().splitlines()  # splitlines method strips newline characters
        last_number = int(lines[-1])
    except FileNotFoundError:
        last_number = 54999  # start from 55000
    next_number = last_number + 1
    with open(filename, 'a') as f:
        f.write(f"{next_number}\n")  # append the next number to the file
    return last_number, next_number

def generate_barcode(number):
    code39 = Code39(str(number), writer=ImageWriter(), add_checksum=False)

    # Create the directory if it does not exist
    directory = 'barcodes'
    if not os.path.exists(directory):
        os.makedirs(directory)

    filename = os.path.join(directory, f"barcode_{number}.jpeg")  # Save the file in the directory

    barcode_img = code39.save(filename)

    img = Image.open(barcode_img)

    width_mm = 62
    height_mm = 29
    width_pixel = int(width_mm * (300 / 25.4))  
    height_pixel = int(height_mm * (300 / 25.4)) 
    img_resized = img.resize((width_pixel, height_pixel))

    draw = ImageDraw.Draw(img_resized)
    font = ImageFont.load_default()

    text_bbox = draw.textbbox((0, 0), str(number), font=font)
    text_width = text_bbox[2]
    x_position = (img_resized.width - text_width) / 2

    draw.text((x_position, img_resized.height - 50), str(number), font=font, fill=255)

    img_resized.save(barcode_img)
    return barcode_img


def print_file(barcode_img):
    time.sleep(1)  # let the system recognize the new file
    os.startfile(barcode_img, 'print')
    threading.Timer(60, os.remove, [barcode_img]).start()  # delay before deleting the file

def main():
    last_number, next_number = get_next_number("last_number.txt") 
    barcode_img = generate_barcode(next_number)
    print_file(barcode_img)

    # Adding 1 to the numbers displayed in the labels
    last_label.config(text="Last: " + str(last_number + 1))
    next_label.config(text="Next: " + str(next_number + 1))


root = tk.Tk()
root.geometry('400x400')  # Set the window size to 400x400
root.title("Auction Barcode Print Button")  # Set the window title

# Create the labels with default text
last_label = tk.Label(root, text="Last: ", font=('Arial', 14))
next_label = tk.Label(root, text="Next: ", font=('Arial', 14))

# Place the labels in the window
last_label.pack(pady=(50, 10))
next_label.pack(pady=(10, 20))

button_frame = tk.Frame(root, width=200, height=200)  # Frame to hold the button
button_frame.pack_propagate(0)  # prevents the frame to shrink
button_frame.pack(pady=(20,20))

button = tk.Button(button_frame, text="Print Barcode", command=main, relief=tk.RAISED)  
button.pack(fill=tk.BOTH, expand=1)  # Button fills the entire frame

root.mainloop()

报错信息

Exception in Tkinter callback
Traceback (most recent call last):
  File "tkinter\__init__.py", line 1948, in __call__
  File "Auction_Label_4.py", line 72, in main
  File "Auction_Label_4.py", line 42, in generate_barcode
  File "barcode\base.py", line 65, in save
  File "barcode\codex.py", line 74, in render
  File "barcode\base.py", line 105, in render
  File "barcode\writer.py", line 265, in render
  File "barcode\writer.py", line 439, in _paint_text
  File "PIL\ImageFont.py", line 1008, in truetype
  File "PIL\ImageFont.py", line 1005, in freetype
  File "PIL\ImageFont.py", line 255, in __init__
OSError: cannot open resource

解决办法

1. 给条码生成器指定字体路径

报错根源是barcode库生成条码文本时找不到字体。你已经定义了font_path但没在条码生成环节使用,修改generate_barcode函数,给ImageWriter配置字体:

def generate_barcode(number):
    # 添加字体配置
    writer_options = {
        'font_path': font_path,
        'font_size': 12  # 可根据需求调整字号
    }
    code39 = Code39(str(number), writer=ImageWriter(), add_checksum=False)
    code39.writer.set_options(writer_options)  # 应用配置
    
    # 剩余代码保持不变...

2. 确保PyInstaller打包时包含字体文件

打包时需要把resources/ARLRDBD.ttf一起打包进程序,执行PyInstaller命令时加上--add-data参数:

Windows系统:

pyinstaller --onefile --add-data "resources/ARLRDBD.ttf;resources" Auction_Label_4.py

macOS/Linux系统:

pyinstaller --onefile --add-data "resources/ARLRDBD.ttf:resources" Auction_Label_4.py

3. 备选方案:使用系统默认字体(兼容性较差)

如果不想打包字体,也可以指定系统已有的字体名称(比如Arial),但不同系统的字体名称可能不一致,容易出问题:

writer_options = {
    'font': 'Arial',
    'font_size': 12
}

为什么IDLE运行正常?

IDLE运行时用的是本地系统环境,barcode库能自动找到系统默认字体;但PyInstaller打包后会创建一个独立的临时运行环境,无法访问系统默认字体路径,必须显式指定字体文件位置。

内容的提问来源于stack exchange,提问作者Shaken89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 10:40:03