python识别快递单出库

1.使用python制作快递单识别出库系统

主要包含以下内容:

  • 调用摄像头实时识别
  • 检测到条形码进行识别条形码内容
  • 根据条形码内容判断订单是否出库
  • 未出库时更新订单状态

2.代码设计

2.1设计窗体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import tkinter as tk
from tkinter import ttk

class ExpressScanner:
def __init__(self, root):
"""初始化应用程序界面"""
self.root = root
# 设置窗口标题和初始尺寸(1200像素宽,600像素高)
self.root.title("智能快递系统的设计与实现")
self.root.geometry("1200x600")

# 创建主框架
# fill=tk.BOTH 表示填充横向和纵向空间
# expand=True 允许控件随窗口缩放
self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True)

# 左侧框架
# side=LEFT 表示靠左排列,fill=BOTH 填满父容器
self.left_frame = ttk.Frame(self.main_frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# 摄像头显示区域
# 空标签先占位,实际使用时可以显示视频画面
self.camera_label = ttk.Label(self.left_frame)
self.camera_label.pack(fill=tk.BOTH, expand=True)


# 右侧信息显示区域
self.info_frame = ttk.Frame(self.main_frame)
# side=RIGHT 靠右排列,与左侧形成左右布局
self.info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

# wraplength=300 设置自动换行宽度为300像素
# justify=LEFT 设置左对齐文本
self.info_label = ttk.Label(self.info_frame, wraplength=300, justify=tk.LEFT)
# padx/pady 设置内边距
self.info_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

# 关闭窗口
def on_closing(self):
self.root.destroy()
if __name__ == '__main__':
"""程序入口"""
# 创建Tkinter根窗口
root = tk.Tk()
# 实例化应用程序
app = ExpressScanner(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
# 启动主事件循环(保持窗口显示)
root.mainloop()

2.1.1增加摄像头调用和视频显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import cv2
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk

class ExpressScanner:
def __init__(self, root):
"""初始化应用程序界面"""
self.root = root
# 设置窗口标题和初始尺寸(1200像素宽,600像素高)
self.root.title("智能快递系统的设计与实现")
self.root.geometry("1200x600")

# 创建主框架
# fill=tk.BOTH 表示填充横向和纵向空间
# expand=True 允许控件随窗口缩放
self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True)

# 左侧框架
# side=LEFT 表示靠左排列,fill=BOTH 填满父容器
self.left_frame = ttk.Frame(self.main_frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# 摄像头显示区域
# 空标签先占位,实际使用时可以显示视频画面
self.camera_label = ttk.Label(self.left_frame)
self.camera_label.pack(fill=tk.BOTH, expand=True)

# 按钮区域
self.button_frame = ttk.Frame(self.left_frame)
self.button_frame.pack(fill=tk.X, pady=5)

# 开始按钮
self.start_button = ttk.Button(self.button_frame, text="开始识别", command=self.start_camera)
self.start_button.pack(side=tk.LEFT, padx=5)

# 停止按钮
self.stop_button = ttk.Button(self.button_frame, text="停止识别", command=self.stop_camera, state=tk.DISABLED)
self.stop_button.pack(side=tk.LEFT, padx=5)

# 右侧信息显示区域
self.info_frame = ttk.Frame(self.main_frame)
# side=RIGHT 靠右排列,与左侧形成左右布局
self.info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

# wraplength=300 设置自动换行宽度为300像素
# justify=LEFT 设置左对齐文本
self.info_label = ttk.Label(self.info_frame, wraplength=300, justify=tk.LEFT)
# padx/pady 设置内边距
self.info_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

# 初始化摄像头和定时器
self.cap = None
self.after_id = None

# 开启摄像头
def start_camera(self):
if self.cap is None:
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
self.info_label.config(text="无法打开摄像头")
return

self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
self.info_label.config(text="摄像头已开启,请将快递单对准摄像头")
self.update_frame()

# 关闭摄像头
def stop_camera(self):
if self.after_id:
self.root.after_cancel(self.after_id)
self.after_id = None
if self.cap is not None:
self.cap.release()
self.cap = None
self.camera_label.config(image='')
self.start_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
self.info_label.config(text="摄像头已关闭")

def update_frame(self):
ret, frame = self.cap.read()
if ret:
# 转换为RGB格式
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

# 转换为PIL图像
image = Image.fromarray(rgb_image)
# 调整图像大小以适应标签
image = image.resize((640, 480), Image.Resampling.LANCZOS)
# 转换为Tkinter图像
photo = ImageTk.PhotoImage(image=image)

# 更新标签
self.camera_label.config(image=photo)
self.camera_label.image = photo # 保持引用

# 继续更新
self.after_id = self.root.after(5, self.update_frame)

# 关闭窗口
def on_closing(self):
self.root.destroy()
if __name__ == '__main__':
"""程序入口"""
# 创建Tkinter根窗口
root = tk.Tk()
# 实例化应用程序
app = ExpressScanner(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
# 启动主事件循环(保持窗口显示)
root.mainloop()

2.1.2检测条形码并识别条形码内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import cv2
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
from pyzbar.pyzbar import decode

class ExpressScanner:
def __init__(self, root):
"""初始化应用程序界面"""
self.root = root
# 设置窗口标题和初始尺寸(1200像素宽,600像素高)
self.root.title("智能快递系统的设计与实现")
self.root.geometry("1200x600")

# 创建主框架
# fill=tk.BOTH 表示填充横向和纵向空间
# expand=True 允许控件随窗口缩放
self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True)

# 左侧框架
# side=LEFT 表示靠左排列,fill=BOTH 填满父容器
self.left_frame = ttk.Frame(self.main_frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# 摄像头显示区域
# 空标签先占位,实际使用时可以显示视频画面
self.camera_label = ttk.Label(self.left_frame)
self.camera_label.pack(fill=tk.BOTH, expand=True)

# 按钮区域
self.button_frame = ttk.Frame(self.left_frame)
self.button_frame.pack(fill=tk.X, pady=5)

# 开始按钮
self.start_button = ttk.Button(self.button_frame, text="开始识别", command=self.start_camera)
self.start_button.pack(side=tk.LEFT, padx=5)

# 停止按钮
self.stop_button = ttk.Button(self.button_frame, text="停止识别", command=self.stop_camera, state=tk.DISABLED)
self.stop_button.pack(side=tk.LEFT, padx=5)

# 右侧信息显示区域
self.info_frame = ttk.Frame(self.main_frame)
# side=RIGHT 靠右排列,与左侧形成左右布局
self.info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

# wraplength=300 设置自动换行宽度为300像素
# justify=LEFT 设置左对齐文本
self.info_label = ttk.Label(self.info_frame, wraplength=300, justify=tk.LEFT)
# padx/pady 设置内边距
self.info_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

# 初始化摄像头和定时器
self.cap = None
self.after_id = None

# 开启摄像头
def start_camera(self):
if self.cap is None:
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
self.info_label.config(text="无法打开摄像头")
return

self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
self.info_label.config(text="摄像头已开启,请将快递单对准摄像头")
self.update_frame()

# 关闭摄像头
def stop_camera(self):
if self.after_id:
self.root.after_cancel(self.after_id)
self.after_id = None
if self.cap is not None:
self.cap.release()
self.cap = None
self.camera_label.config(image='')
self.start_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
self.info_label.config(text="摄像头已关闭")

def update_frame(self):
ret, frame = self.cap.read()
if ret:
# 检测条形码
barcodes = decode(frame)
if barcodes:
for barcode in barcodes:
# 获取条形码的位置
(x, y, w, h) = barcode.rect
# 在图像上绘制条形码区域
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 获取条形码数据
barcode_data = barcode.data.decode("utf-8")
barcode_type = barcode.type

# 显示条形码信息和订单状态
display_text = f"""
快递单号: {barcode_data}
"""

self.info_label.config(text=display_text)

# 转换为RGB格式
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

# 转换为PIL图像
image = Image.fromarray(rgb_image)
# 调整图像大小以适应标签
image = image.resize((640, 480), Image.Resampling.LANCZOS)
# 转换为Tkinter图像
photo = ImageTk.PhotoImage(image=image)

# 更新标签
self.camera_label.config(image=photo)
self.camera_label.image = photo # 保持引用

# 继续更新
self.after_id = self.root.after(5, self.update_frame)

# 关闭窗口
def on_closing(self):
self.root.destroy()
if __name__ == '__main__':
"""程序入口"""
# 创建Tkinter根窗口
root = tk.Tk()
# 实例化应用程序
app = ExpressScanner(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
# 启动主事件循环(保持窗口显示)
root.mainloop()

2.1.3增加数据库查询订单状态并出库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import cv2
import tkinter as tk
from tkinter import ttk, messagebox
from PIL import Image, ImageTk
from pyzbar.pyzbar import decode
import mysql.connector
from datetime import datetime

class ExpressScanner:
def __init__(self, root):
"""初始化应用程序界面"""
self.root = root
# 设置窗口标题和初始尺寸(1200像素宽,600像素高)
self.root.title("智能快递系统的设计与实现")
self.root.geometry("1200x600")

# 初始化数据库连接
self.db_connection = None
self.connect_database()

# 创建主框架
# fill=tk.BOTH 表示填充横向和纵向空间
# expand=True 允许控件随窗口缩放
self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True)

# 左侧框架
# side=LEFT 表示靠左排列,fill=BOTH 填满父容器
self.left_frame = ttk.Frame(self.main_frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# 摄像头显示区域
# 空标签先占位,实际使用时可以显示视频画面
self.camera_label = ttk.Label(self.left_frame)
self.camera_label.pack(fill=tk.BOTH, expand=True)

# 按钮区域
self.button_frame = ttk.Frame(self.left_frame)
self.button_frame.pack(fill=tk.X, pady=5)

# 开始按钮
self.start_button = ttk.Button(self.button_frame, text="开始识别", command=self.start_camera)
self.start_button.pack(side=tk.LEFT, padx=5)

# 停止按钮
self.stop_button = ttk.Button(self.button_frame, text="停止识别", command=self.stop_camera, state=tk.DISABLED)
self.stop_button.pack(side=tk.LEFT, padx=5)

# 右侧信息显示区域
self.info_frame = ttk.Frame(self.main_frame)
# side=RIGHT 靠右排列,与左侧形成左右布局
self.info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

# wraplength=300 设置自动换行宽度为300像素
# justify=LEFT 设置左对齐文本
self.info_label = ttk.Label(self.info_frame, wraplength=300, justify=tk.LEFT)
# padx/pady 设置内边距
self.info_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

# 初始化摄像头和定时器
self.cap = None
self.after_id = None

# 连接数据库
def connect_database(self):
try:
self.db_connection = mysql.connector.connect(
host="localhost",
port="3306",
user="root",
password="******",
database="express_db"
)
print("数据库连接成功")
except mysql.connector.Error as err:
print(f"数据库连接错误: {err}")
messagebox.showerror("错误", "无法连接到数据库")

def check_and_update_order(self, barcode_data):
try:
cursor = self.db_connection.cursor()

# 查询订单状态
query = "SELECT id, status FROM orders WHERE express_number = %s"
print(f"执行SQL查询: {query} with express_number = {barcode_data}")
cursor.execute(query, (barcode_data,))
result = cursor.fetchone()

if result:
express_number, status = result
print(f"查询结果: express_number={express_number}, status={status}")

if status == 0: # 0 表示未出库
# 更新订单状态
update_query = """
UPDATE orders
SET status = 1,
outbound_time = %s
WHERE express_number = %s
"""
print(
f"执行SQL更新: {update_query} with outbound_time = {datetime.now()}, express_number = {barcode_data}")
cursor.execute(update_query, (datetime.now(), barcode_data))
self.db_connection.commit()
print("更新成功")
return "出库成功"
else:
print("订单已出库")
return "已出库"
else:
print("订单不存在")
return "订单不存在"

cursor.close()
except mysql.connector.Error as err:
print(f"数据库操作错误: {err}")
return "数据库操作失败"

# 开启摄像头
def start_camera(self):
if self.cap is None:
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
self.info_label.config(text="无法打开摄像头")
return

self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
self.info_label.config(text="摄像头已开启,请将快递单对准摄像头")
self.update_frame()

# 关闭摄像头
def stop_camera(self):
if self.after_id:
self.root.after_cancel(self.after_id)
self.after_id = None
if self.cap is not None:
self.cap.release()
self.cap = None
self.camera_label.config(image='')
self.start_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
self.info_label.config(text="摄像头已关闭")

def update_frame(self):
ret, frame = self.cap.read()
if ret:
# 检测条形码
barcodes = decode(frame)
if barcodes:
for barcode in barcodes:
# 获取条形码的位置
(x, y, w, h) = barcode.rect
# 在图像上绘制条形码区域
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 获取条形码数据
barcode_data = barcode.data.decode("utf-8")
barcode_type = barcode.type

# 检查并更新订单状态
order_status = self.check_and_update_order(barcode_data)

# 显示条形码信息和订单状态
display_text = f"""
快递单号: {barcode_data}
订单状态: {order_status}
"""

self.info_label.config(text=display_text)

# 转换为RGB格式
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

# 转换为PIL图像
image = Image.fromarray(rgb_image)
# 调整图像大小以适应标签
image = image.resize((640, 480), Image.Resampling.LANCZOS)
# 转换为Tkinter图像
photo = ImageTk.PhotoImage(image=image)

# 更新标签
self.camera_label.config(image=photo)
self.camera_label.image = photo # 保持引用

# 继续更新
self.after_id = self.root.after(5, self.update_frame)

# 关闭窗口
def on_closing(self):
if self.cap is not None:
self.cap.release()
if self.db_connection is not None:
self.db_connection.close()
self.root.destroy()
if __name__ == '__main__':
"""程序入口"""
# 创建Tkinter根窗口
root = tk.Tk()
# 实例化应用程序
app = ExpressScanner(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
# 启动主事件循环(保持窗口显示)
root.mainloop()