20241019165800

This commit is contained in:
zhaoyafan 2024-10-19 16:58:07 +08:00
parent 11b7c0dc2f
commit c2427efb42
2 changed files with 187 additions and 21 deletions

112
main.py
View File

@ -13,12 +13,12 @@ import pywintypes
import hashlib import hashlib
import win32com.client import win32com.client
import win32print import win32print
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QComboBox, QCheckBox, QLineEdit, QAction, QMenu, QMessageBox, QPushButton, QVBoxLayout, QHBoxLayout, QFileDialog from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QComboBox, QCheckBox, QLineEdit, QAction, QMenu, \
from PyQt5.QtCore import Qt, QCoreApplication QMessageBox, QPushButton, QVBoxLayout, QHBoxLayout, QFileDialog, QDialog
from PyQt5.QtCore import Qt, QCoreApplication, QTimer, QPropertyAnimation
from PyQt5.QtGui import QPixmap, QIntValidator from PyQt5.QtGui import QPixmap, QIntValidator
from threading import RLock from threading import RLock
sys.path.append(os.path.dirname(__file__)) sys.path.append(os.path.dirname(__file__))
clr.AddReference('BarTender') clr.AddReference('BarTender')
try: try:
@ -93,12 +93,14 @@ if os.name == 'nt':
PVOID = c_void_p PVOID = c_void_p
class _OFFSET(Structure): class _OFFSET(Structure):
_fields_ = [ _fields_ = [
('Offset', DWORD), ('Offset', DWORD),
('OffsetHigh', DWORD) ('OffsetHigh', DWORD)
] ]
class _OFFSET_UNION(Union): class _OFFSET_UNION(Union):
_fields_ = [ _fields_ = [
('_offset', _OFFSET), ('_offset', _OFFSET),
@ -106,6 +108,7 @@ if os.name == 'nt':
] ]
_anonymous_ = ['_offset'] _anonymous_ = ['_offset']
class OVERLAPPED(Structure): class OVERLAPPED(Structure):
_fields_ = [ _fields_ = [
('Internal', ULONG_PTR), ('Internal', ULONG_PTR),
@ -115,6 +118,7 @@ if os.name == 'nt':
] ]
_anonymous_ = ['_offset_union'] _anonymous_ = ['_offset_union']
LPOVERLAPPED = POINTER(OVERLAPPED) LPOVERLAPPED = POINTER(OVERLAPPED)
LockFileEx = windll.kernel32.LockFileEx LockFileEx = windll.kernel32.LockFileEx
LockFileEx.restype = BOOL LockFileEx.restype = BOOL
@ -148,6 +152,7 @@ if os.name == 'nt':
else: else:
try: try:
import fcntl import fcntl
LOCK_SH = fcntl.LOCK_SH LOCK_SH = fcntl.LOCK_SH
LOCK_NB = fcntl.LOCK_NB LOCK_NB = fcntl.LOCK_NB
LOCK_EX = fcntl.LOCK_EX LOCK_EX = fcntl.LOCK_EX
@ -155,6 +160,7 @@ else:
except (ImportError, AttributeError): except (ImportError, AttributeError):
LOCK_EX = LOCK_SH = LOCK_NB = 0 LOCK_EX = LOCK_SH = LOCK_NB = 0
def flock(f, flags): def flock(f, flags):
return flags == LOCK_UN return flags == LOCK_UN
else: else:
@ -454,10 +460,13 @@ class BarTenderPrint:
self.bt_format.PrintOut(False, False) self.bt_format.PrintOut(False, False)
def generate_preview(self): def generate_preview(self):
path = os.path.abspath(os.path.join(tempfile.gettempdir(), '%s%s.png' % ('preview_', int(round(time.time() * 1000))))) path = os.path.abspath(
os.path.join(tempfile.gettempdir(), '%s%s.png' % ('preview_', int(round(time.time() * 1000)))))
if (self.bt_format is not None) == 1: if (self.bt_format is not None) == 1:
self.logger.i('%s%s' % ('Generate preview to ', path)) self.logger.i('%s%s' % ('Generate preview to ', path))
self.bt_format.ExportToFile(path, 'PNG', BarTender.BtColors.btColors24Bit, BarTender.BtResolution.btResolutionPrinter, BarTender.BtSaveOptions.btDoNotSaveChanges) self.bt_format.ExportToFile(path, 'PNG', BarTender.BtColors.btColors24Bit,
BarTender.BtResolution.btResolutionPrinter,
BarTender.BtSaveOptions.btDoNotSaveChanges)
return path return path
def quit(self): def quit(self):
@ -518,7 +527,8 @@ class CustomPushButton(QPushButton):
class CustomLabel(QLabel): class CustomLabel(QLabel):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #0C0C0C; border: 1px solid #0C0C0C;') self.setStyleSheet(
'font-size: 12px; font-family: \'Microsoft YaHei\'; color: #0C0C0C; border: 1px solid #0C0C0C;')
class CustomComboBox(QComboBox): class CustomComboBox(QComboBox):
@ -549,12 +559,17 @@ class MainWindow(QMainWindow):
def __init__(self, logger: Logger): def __init__(self, logger: Logger):
super().__init__() super().__init__()
self.app_name = '标签打印' self.app_name = '标签打印'
self.app_version = ('1.0.3', '20240810', 'zhaoyafan', 'zhaoyafan@foxmail.com', 'https://www.fanscloud.net/') self.app_version = ('1.0.4', '20241019', 'zhaoyafan', 'zhaoyafan@foxmail.com', 'https://www.fanscloud.net/')
self.logger = logger self.logger = logger
self.setting = Setting(os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.config' % self.md5(__file__)[:16])), {}) self.toast = ToastNotification()
self.setting = Setting(
os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.config' % self.md5(__file__)[:16])), {})
self.bt = BarTenderPrint(logger=self.logger) self.bt = BarTenderPrint(logger=self.logger)
self.sv = ScanVerify(logger=self.logger, rule_file=os.path.abspath(os.path.join(os.path.dirname(__file__), 'rules.json'))) self.sv = ScanVerify(logger=self.logger,
self.ditto = Ditto(db_file=os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.db' % self.md5(__file__)[:16])), class_name='LabelPrint', limit_time=7776000, limit_rows=100000) rule_file=os.path.abspath(os.path.join(os.path.dirname(__file__), 'rules.json')))
self.ditto = Ditto(
db_file=os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.db' % self.md5(__file__)[:16])),
class_name='LabelPrint', limit_time=7776000, limit_rows=100000)
self.last_opened_template = ['', ''] self.last_opened_template = ['', '']
self.input_convert_letter = 0 self.input_convert_letter = 0
self.input_scan_prohibited_enter = 0 self.input_scan_prohibited_enter = 0
@ -666,7 +681,8 @@ class MainWindow(QMainWindow):
r_layout_2.addWidget(self.chkDET) r_layout_2.addWidget(self.chkDET)
# Scan # Scan
self.edtSCN = CustomLineEdit('') self.edtSCN = CustomLineEdit('')
self.edtSCN.setStyleSheet('QLineEdit {font-size: 28px; font-family: \'Microsoft YaHei\'; color: #000000; background-color: #FFFFCC;}') self.edtSCN.setStyleSheet(
'QLineEdit {font-size: 28px; font-family: \'Microsoft YaHei\'; color: #000000; background-color: #FFFFCC;}')
self.edtSCN.setFixedSize(455, 45) self.edtSCN.setFixedSize(455, 45)
r_layout_3.addWidget(self.edtSCN) r_layout_3.addWidget(self.edtSCN)
# Print button # Print button
@ -683,7 +699,8 @@ class MainWindow(QMainWindow):
self.setFixedSize(self.minimumSizeHint()) self.setFixedSize(self.minimumSizeHint())
screen_rect = QApplication.desktop().availableGeometry() screen_rect = QApplication.desktop().availableGeometry()
window_rect = self.geometry() window_rect = self.geometry()
self.move(int((screen_rect.width() - window_rect.width()) * 0.5), int((screen_rect.height() - window_rect.height()) * 0.5)) self.move(int((screen_rect.width() - window_rect.width()) * 0.5),
int((screen_rect.height() - window_rect.height()) * 0.5))
self.edtSCN.setFocus() self.edtSCN.setFocus()
self.show() self.show()
self.load_setting() self.load_setting()
@ -696,7 +713,8 @@ class MainWindow(QMainWindow):
# load blockRepeat # load blockRepeat
self.blockRepeatAction.setChecked(bool(self.setting['blockRepeat'])) self.blockRepeatAction.setChecked(bool(self.setting['blockRepeat']))
# load printer # load printer
self.slcSPT.setCurrentIndex(next((index for index, item in enumerate(self.bt.printer_list) if item == self.setting['printer']), 0)) self.slcSPT.setCurrentIndex(
next((index for index, item in enumerate(self.bt.printer_list) if item == self.setting['printer']), 0))
# load checker # load checker
self.slcSVR.setCurrentIndex(self.setting['checker'] or 0) self.slcSVR.setCurrentIndex(self.setting['checker'] or 0)
# load printCopies # load printCopies
@ -714,7 +732,7 @@ class MainWindow(QMainWindow):
def clearRecordActionFunction(self): def clearRecordActionFunction(self):
count = self.ditto.clear() count = self.ditto.clear()
if (count > 0) == 1: if (count > 0) == 1:
QMessageBox.information(self, '提示', '打印记录已经清空:%s' % (count,)) self.toast.show_toast('已成功清空%s条打印记录' % (count,))
def designStateActionFunction(self, checked): def designStateActionFunction(self, checked):
if (not self.bt.bt_app) == 1: if (not self.bt.bt_app) == 1:
@ -745,7 +763,8 @@ class MainWindow(QMainWindow):
def aboutWindowActionFunction(self): def aboutWindowActionFunction(self):
_c = self.app_version _c = self.app_version
if (not _c) == 0: if (not _c) == 0:
QMessageBox.information(self, '关于', "" 'version: %s, build: %s, author: %s, email: %s, site: %s' % (_c[0], _c[1], _c[2], _c[3], '<a href=\'%s\'>%s</a>' % (_c[4], _c[4]))) QMessageBox.information(self, '关于', "" 'version: %s, build: %s, author: %s, email: %s, site: %s' % (
_c[0], _c[1], _c[2], _c[3], '<a href=\'%s\'>%s</a>' % (_c[4], _c[4])))
@staticmethod @staticmethod
def md5(input_data): def md5(input_data):
@ -792,7 +811,8 @@ class MainWindow(QMainWindow):
def set_preview(self, preview_image: str): def set_preview(self, preview_image: str):
if (preview_image and os.path.exists(preview_image)) == 1: if (preview_image and os.path.exists(preview_image)) == 1:
pixmap = QPixmap(preview_image) pixmap = QPixmap(preview_image)
pixmap = pixmap.scaled(self.labPRV.size(), aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) pixmap = pixmap.scaled(self.labPRV.size(), aspectRatioMode=Qt.KeepAspectRatio,
transformMode=Qt.SmoothTransformation)
self.labPRV.setPixmap(pixmap) self.labPRV.setPixmap(pixmap)
if (tempfile.gettempdir() in preview_image) == 1: if (tempfile.gettempdir() in preview_image) == 1:
rm(preview_image) rm(preview_image)
@ -809,6 +829,7 @@ class MainWindow(QMainWindow):
self.set_preview(self.bt.generate_preview()) self.set_preview(self.bt.generate_preview())
self.last_opened_template[0] = file self.last_opened_template[0] = file
self.last_opened_template[1] = self.md5(open(file, 'rb')) self.last_opened_template[1] = self.md5(open(file, 'rb'))
self.toast.show_toast('模板加载成功')
except pywintypes.com_error as e: except pywintypes.com_error as e:
t = str(e.args[1]) t = str(e.args[1])
m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2])
@ -822,7 +843,8 @@ class MainWindow(QMainWindow):
QMessageBox.critical(self, '错误', '%s' % e) QMessageBox.critical(self, '错误', '%s' % e)
def on_open_template_file(self): def on_open_template_file(self):
filename, _ = QFileDialog.getOpenFileName(self, '打开文件', os.path.dirname(self.last_opened_template[0]), 'BarTender 文档 (*.btw)') filename, _ = QFileDialog.getOpenFileName(self, '打开文件', os.path.dirname(self.last_opened_template[0]),
'BarTender 文档 (*.btw)')
if (filename and os.path.exists(filename)) == 1: if (filename and os.path.exists(filename)) == 1:
self.load_template(filename) self.load_template(filename)
self.setting['template'] = filename self.setting['template'] = filename
@ -833,7 +855,7 @@ class MainWindow(QMainWindow):
self.bt.start_printing_template() self.bt.start_printing_template()
self.set_preview(self.bt.generate_preview()) self.set_preview(self.bt.generate_preview())
return None return None
QMessageBox.critical(self, '错误', '请先加载模板文件') self.toast.show_toast('请先加载模板文件')
except pywintypes.com_error as e: except pywintypes.com_error as e:
t = str(e.args[1]) t = str(e.args[1])
m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2])
@ -903,14 +925,18 @@ class MainWindow(QMainWindow):
def on_convert_letter_changed(self, checked): def on_convert_letter_changed(self, checked):
if (checked == 2) == 1: if (checked == 2) == 1:
self.input_convert_letter = 1 self.input_convert_letter = 1
self.toast.show_toast('强制大写开启')
else: else:
self.input_convert_letter = 0 self.input_convert_letter = 0
self.toast.show_toast('强制大写关闭')
def on_prohibit_enter_changed(self, checked): def on_prohibit_enter_changed(self, checked):
if (checked == 2) == 1: if (checked == 2) == 1:
self.input_scan_prohibited_enter = 1 self.input_scan_prohibited_enter = 1
self.toast.show_toast('禁用回车开启')
else: else:
self.input_scan_prohibited_enter = 0 self.input_scan_prohibited_enter = 0
self.toast.show_toast('禁用回车关闭')
def on_print_scan_enter(self): def on_print_scan_enter(self):
self.input_scan_prohibited_enter or self.on_start_printing() self.input_scan_prohibited_enter or self.on_start_printing()
@ -923,13 +949,15 @@ class MainWindow(QMainWindow):
text = edit.text().strip() text = edit.text().strip()
edit.setText(text) edit.setText(text)
if (self.bt.bt_format is None) == 1: if (self.bt.bt_format is None) == 1:
self.toast.show_toast('未加载模板文件')
edit.selectAll() edit.selectAll()
return None return None
if (text == '') == 1: if (text == '') == 1:
self.toast.show_toast('输入的内容为空')
edit.selectAll() edit.selectAll()
return None return None
if (not self.sv.verify(text)) == 1: if (not self.sv.verify(text)) == 1:
QMessageBox.critical(self, '错误', '打印的内容不符合验证规则:\n\n%s' % (text,)) self.toast.show_toast('输入的内容不符合验证规则')
edit.selectAll() edit.selectAll()
return None return None
if (not self.ditto.addit(text) and self.setting['blockRepeat']) == 1: if (not self.ditto.addit(text) and self.setting['blockRepeat']) == 1:
@ -953,10 +981,52 @@ class MainWindow(QMainWindow):
QMessageBox.critical(self, '错误', '%s' % e) QMessageBox.critical(self, '错误', '%s' % e)
class ToastNotification(QDialog):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowType.Tool)
layout = QVBoxLayout()
layout.setContentsMargins(5, 705, 5, 5)
self.label = QLabel("")
self.label.setStyleSheet("font-family: 'Microsoft YaHei'; font-size: 24px; color: #ffffff;")
layout.addWidget(self.label)
self.setLayout(layout)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setStyleSheet("background-color: rgba(65, 65, 65, 180); border-radius: 5px; padding: 10px 16px 10px 16px")
self.adjustSize()
self.timer = QTimer(self)
self.timer.setInterval(2000)
self.timer.timeout.connect(self.close)
self.fade_animation = QPropertyAnimation(self, b"windowOpacity")
self.fade_animation.setDuration(500)
def show_toast(self, message):
self.close()
self.label.setText(message)
self.timer.isActive() and self.timer.stop()
self.timer.start()
self.adjustSize()
self.fade_animation.setStartValue(0.0)
self.fade_animation.setEndValue(1.0)
self.show()
self.fade_animation.start()
def closeEvent(self, event):
if self.timer.isActive():
self.timer.stop()
super().closeEvent(event)
if __name__ == '__main__': if __name__ == '__main__':
if (os.path.basename(__file__).lower().endswith('.int')) == 1: if (os.path.basename(__file__).lower().endswith('.int')) == 1:
QCoreApplication.addLibraryPath(os.path.abspath(os.path.join(os.path.dirname(__file__), 'site-packages/PyQt5/Qt5/plugins'))) QCoreApplication.addLibraryPath(
f_lock = open(file=os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.lock' % hashlib.md5(bytes(__file__, encoding='utf-8')).hexdigest()[:16])), mode='w', encoding='utf-8') os.path.abspath(os.path.join(os.path.dirname(__file__), 'site-packages/PyQt5/Qt5/plugins')))
f_lock = open(file=os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.lock' % hashlib.md5(
bytes(__file__, encoding='utf-8')).hexdigest()[:16])), mode='w', encoding='utf-8')
if (not flock(f_lock, LOCK_EX | LOCK_NB)) == 1: if (not flock(f_lock, LOCK_EX | LOCK_NB)) == 1:
app = QApplication(sys.argv) app = QApplication(sys.argv)
msg = QMessageBox() msg = QMessageBox()

View File

@ -66,5 +66,101 @@
[ [
"24位长度", "24位长度",
"^.{24}$" "^.{24}$"
],
[
"25位长度",
"^.{25}$"
],
[
"26位长度",
"^.{26}$"
],
[
"27位长度",
"^.{27}$"
],
[
"28位长度",
"^.{28}$"
],
[
"29位长度",
"^.{29}$"
],
[
"30位长度",
"^.{30}$"
],
[
"31位长度",
"^.{31}$"
],
[
"32位长度",
"^.{32}$"
],
[
"33位长度",
"^.{33}$"
],
[
"34位长度",
"^.{34}$"
],
[
"35位长度",
"^.{35}$"
],
[
"36位长度",
"^.{36}$"
],
[
"37位长度",
"^.{37}$"
],
[
"38位长度",
"^.{38}$"
],
[
"39位长度",
"^.{39}$"
],
[
"40位长度",
"^.{40}$"
],
[
"41位长度",
"^.{41}$"
],
[
"42位长度",
"^.{42}$"
],
[
"43位长度",
"^.{43}$"
],
[
"44位长度",
"^.{44}$"
],
[
"45位长度",
"^.{45}$"
],
[
"46位长度",
"^.{46}$"
],
[
"47位长度",
"^.{47}$"
],
[
"48位长度",
"^.{48}$"
] ]
] ]