import os import re import io import sys import clr import time import json import shutil import sqlite3 import logging import tempfile import pywintypes import hashlib import win32com.client import win32print from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QComboBox, QCheckBox, QLineEdit, QAction, QMenu, QMessageBox, QPushButton, QVBoxLayout, QHBoxLayout, QFileDialog from PyQt5.QtCore import Qt, QCoreApplication from PyQt5.QtGui import QPixmap, QIntValidator from threading import RLock sys.path.append(os.path.dirname(__file__)) clr.AddReference('BarTender') try: import BarTender except Exception: pass def cp(src: str, dst: str): """ Support file or directory. """ return shutil.copytree(src, dst) and True if os.path.isdir(src) else shutil.copy(src, dst) and True def mv(src: str, dst: str): """ Support file or directory. """ return shutil.move(src, dst) and True def rm(src: str): """ Support file or directory. """ shutil.rmtree(src) if os.path.isdir(src) else os.remove(src) return True def rename(path: str, filename: str): """ Support file or directory. """ if filename.find('/') != -1: raise Exception('File name, catalog name or scroll grammar incorrect grammar.') return os.rename(path, os.path.dirname(path) + '/' + filename) or True def exists(path: str): """ Support file or directory. """ return os.path.exists(path) def mkdirs(path: str): """ Support multi-level directory. """ return os.makedirs(path) def _fd(f): return f.fileno() if hasattr(f, 'fileno') else f if os.name == 'nt': import msvcrt from ctypes import (sizeof, c_ulong, c_void_p, c_int64, Structure, Union, POINTER, windll, byref) from ctypes.wintypes import BOOL, DWORD, HANDLE LOCK_SH = 0x0 LOCK_NB = 0x1 LOCK_EX = 0x2 LOCK_UN = 0x9 if sizeof(c_ulong) != sizeof(c_void_p): ULONG_PTR = c_int64 else: ULONG_PTR = c_ulong PVOID = c_void_p class _OFFSET(Structure): _fields_ = [ ('Offset', DWORD), ('OffsetHigh', DWORD) ] class _OFFSET_UNION(Union): _fields_ = [ ('_offset', _OFFSET), ('Pointer', PVOID) ] _anonymous_ = ['_offset'] class OVERLAPPED(Structure): _fields_ = [ ('Internal', ULONG_PTR), ('InternalHigh', ULONG_PTR), ('_offset_union', _OFFSET_UNION), ('hEvent', HANDLE) ] _anonymous_ = ['_offset_union'] LPOVERLAPPED = POINTER(OVERLAPPED) LockFileEx = windll.kernel32.LockFileEx LockFileEx.restype = BOOL LockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD, LPOVERLAPPED] UnlockFileEx = windll.kernel32.UnlockFileEx UnlockFileEx.restype = BOOL UnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED] def flock(f, flags): hfile = msvcrt.get_osfhandle(_fd(f)) overlapped = OVERLAPPED() if flags == LOCK_UN: ret = UnlockFileEx( hfile, 0, 0, 0xFFFF0000, byref(overlapped) ) else: ret = LockFileEx( hfile, flags, 0, 0, 0xFFFF0000, byref(overlapped) ) return bool(ret) else: try: import fcntl LOCK_SH = fcntl.LOCK_SH LOCK_NB = fcntl.LOCK_NB LOCK_EX = fcntl.LOCK_EX LOCK_UN = fcntl.LOCK_UN except (ImportError, AttributeError): LOCK_EX = LOCK_SH = LOCK_NB = 0 def flock(f, flags): return flags == LOCK_UN else: def flock(f, flags): return fcntl.flock(_fd(f), flags) == 0 def getJson(file, data=None): if os.path.exists(file): try: return json.loads(open(file=file, mode='r', encoding='utf-8').read()) except json.decoder.JSONDecodeError: return data return data def putJson(file, data=None): with open(file=file, mode='w', encoding='utf-8') as f: flock(f, LOCK_EX) res = f.write(json.dumps(data, indent=4, ensure_ascii=True)) flock(f, LOCK_UN) return res class SQLite3: __conn__ = None __curr__ = None __data__ = None def __init__(self, *args, **kwargs): if args or kwargs: self.connect(*args, **kwargs) def _check_connection(self): if self.__curr__ is None: raise Exception('Connection does not exist.') @staticmethod def _sqlite_dict_factory(cursor, row): d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d def connect(self, database): if self.__conn__: raise Exception('Already Connect.') self.__conn__ = sqlite3.connect(database, timeout=10, check_same_thread=False) self.__conn__.row_factory = self._sqlite_dict_factory self.__curr__ = self.__conn__.cursor() return self def execute(self, *args, **kwargs): self._check_connection() self.__curr__.execute(*args, **kwargs) self.__conn__.commit() return self def rownums(self): self._check_connection() return self.__curr__.rowcount def fetchall(self): self._check_connection() return self.__curr__.fetchall() def fetchone(self): self._check_connection() return self.__curr__.fetchone() def close(self): if self.__curr__ or self.__conn__: self.__curr__.close() self.__conn__.close() self.__curr__ = None self.__conn__ = None return True class Ditto: def __init__(self, db_file, rlock=None, class_name='Ditto-Default', limit_time=None, limit_rows=None): self.rLock = rlock or RLock() if limit_time is not None and not limit_time >= 0: raise Exception('Range error.') if limit_rows is not None and not limit_rows >= 0: raise Exception('Range error.') self.class_name = class_name self.limit_rows = limit_rows self.limit_time = limit_time try: self.database = SQLite3(db_file) self.init_database() except sqlite3.DatabaseError: self.database.close() and os.remove(db_file) raise Exception('The database was damaged and has been reset.') def init_database(self): with self.rLock: self.database.execute(''' CREATE TABLE IF NOT EXISTS "ditto" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "class" TEXT(64) COLLATE NOCASE, "value" TEXT(256) COLLATE NOCASE, "time" integer(12) ); ''') def fresh(self): with self.rLock: if self.limit_time is not None: self.database.execute(''' DELETE FROM ditto WHERE class='%s' AND time < %s; ''' % (self.class_name, (int(time.time()) - self.limit_time))) if self.limit_rows is not None: self.database.execute(''' DELETE FROM ditto WHERE id IN (SELECT id FROM (SELECT id FROM ditto WHERE class='%s' ORDER BY time DESC LIMIT %s,100)); ''' % (self.class_name, self.limit_rows)) def count(self): with self.rLock: self.fresh() self.database.execute(''' SELECT COUNT(1) AS rows FROM ditto WHERE class='%s'; ''' % (self.class_name,)) return self.database.fetchone()['rows'] def clear(self): with self.rLock: self.fresh() self.database.execute(''' DELETE FROM ditto WHERE class='%s'; ''' % (self.class_name,)) return self.database.rownums() def query(self, value): with self.rLock: self.fresh() self.database.execute(''' SELECT COUNT(1) AS rows FROM ditto WHERE class='%s' AND value='%s'; ''' % (self.class_name, value)) return self.database.fetchone()['rows'] def addit(self, value): with self.rLock: if self.query(value) != 0: return False self.database.execute(''' INSERT INTO ditto (\"class\", \"value\", \"time\") VALUES ('%s', '%s', '%s'); ''' % (self.class_name, value, int(time.time()))) self.fresh() return True class LoggerFileHandler: def __init__(self, log_file: str, mode: str = 'a', level: str = None, fmt: str = None): self.log, self.mod = log_file, mode self.lev, self.fmt = level, fmt self.sty = '%' class LoggerConsHandler: def __init__(self, level: str = None, fmt: str = None): self.lev, self.fmt = level, fmt self.sty = '%' class Logger: logger = None levels = { 'CRITICAL': logging.CRITICAL, 'FATAL': logging.FATAL, 'ERROR': logging.ERROR, 'WARNING': logging.WARNING, 'WARN': logging.WARN, 'INFO': logging.INFO, 'DEBUG': logging.DEBUG, 'NOTSET': logging.NOTSET, 'D': logging.DEBUG, 'I': logging.INFO, 'W': logging.WARNING, 'E': logging.ERROR, 'F': logging.FATAL } default_format = '{asctime} - {name} - {levelname[0]}: {message}' default_format_style = '{' handler_list = [] def __init__( self, name: str = 'default', default_level='DEBUG', fh: LoggerFileHandler = None, ch: LoggerConsHandler = None, add_default_handler=False ): ch = LoggerConsHandler() if add_default_handler and not ch else ch if fh and not isinstance(fh, LoggerFileHandler): raise TypeError('The parameter fh must be type.') if ch and not isinstance(ch, LoggerConsHandler): raise TypeError('The parameter ch must be type.') self.logger = logging.getLogger(name) self.logger.setLevel(self.levels[default_level]) if fh: fhandler = logging.FileHandler(filename=fh.log, mode=fh.mod, encoding='utf-8') self.handler_list.append(fhandler) fhandler.setLevel(self.levels[fh.lev or default_level]) fh.fmt = fh.fmt or self.default_format fh.sty = '{' if '%' not in fh.fmt else '%' fhandler.setFormatter(logging.Formatter(fmt=fh.fmt, style=fh.sty)) self.logger.addHandler(fhandler) if ch: chandler = logging.StreamHandler() self.handler_list.append(chandler) chandler.setLevel(self.levels[ch.lev or default_level]) ch.fmt = ch.fmt or self.default_format ch.sty = '{' if '%' not in ch.fmt else '%' chandler.setFormatter(logging.Formatter(fmt=ch.fmt, style=ch.sty)) self.logger.addHandler(chandler) self.d = self.logger.debug self.i = self.logger.info self.w = self.logger.warning self.e = self.logger.error self.f = self.logger.fatal self.c = self.logger.critical class Setting(dict): def __init__(self, setting_file: str, setting_default: dict): self.data_file = os.path.abspath(setting_file) super().__init__(getJson(self.data_file, setting_default)) def __getitem__(self, item): if item in self: return super().__getitem__(item) else: return None def __setitem__(self, key, value): super().__setitem__(key, value) putJson(self.data_file, self) class BarTenderPrint: def __init__(self, logger: Logger): self.logger = logger self.label = None self.print = None self.sheet = None self.bt_app = BarTender.Application() self.bt_app.Visible = False self.bt_format = None self.printer_list = [printer[2] for printer in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL)][::-1] self.printer_current_index = 0 self.template_source_primary = {} self.logger.i('%s%s' % ('Init', '')) def set_label(self, data: str): self.label = os.path.abspath(data) self.logger.i('%s%s' % ('Open the label template ', self.label)) for _ in range(2): if (self.bt_format is not None) == 1: self.bt_format.Close(BarTender.BtSaveOptions.btDoNotSaveChanges) self.bt_format = self.bt_app.Formats.Open(self.label, False, '') self.template_source_primary = {} self.set_print(self.printer_current_index or 0) self.set_sheet(self.sheet or 1) def set_print(self, data: int): self.printer_current_index = data self.print = self.printer_list[self.printer_current_index] self.logger.i('%s%s' % ('Printer set to ', self.print)) if (self.bt_format is not None) == 1: self.bt_format.PrintSetup.Printer = self.print def set_sheet(self, data: int): self.sheet = int(data) self.logger.i('%s%s' % ('Number of copies ', self.sheet)) if (self.bt_format is not None) == 1: self.bt_format.PrintSetup.IdenticalCopiesOfLabel = self.sheet def set_data_source(self, name, value): if name not in self.template_source_primary.keys(): self.template_source_primary[name] = self.bt_format.GetNamedSubStringValue(name) self.logger.i('%s%s' % ('Set data source ', '%s=%s' % (name, value))) self.bt_format.SetNamedSubStringValue(name, value) def start_printing(self, content: str): for k, v in {'A': content}.items(): self.set_data_source(k, v) if (self.bt_format is not None) == 1: self.logger.i('%s%s' % ('Printing ', 'normal')) self.bt_format.PrintOut(False, False) def start_printing_template(self): for k, v in self.template_source_primary.items(): self.set_data_source(k, v) if (self.bt_format is not None) == 1: self.logger.i('%s%s' % ('Printing ', 'template')) self.bt_format.PrintOut(False, False) def generate_preview(self): 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: self.logger.i('%s%s' % ('Generate preview to ', path)) self.bt_format.ExportToFile(path, 'PNG', BarTender.BtColors.btColors24Bit, BarTender.BtResolution.btResolutionPrinter, BarTender.BtSaveOptions.btDoNotSaveChanges) return path def quit(self): try: self.bt_format is not None and self.bt_format.Close(BarTender.BtSaveOptions.btDoNotSaveChanges) self.bt_app is not None and self.bt_app.Quit(BarTender.BtSaveOptions.btDoNotSaveChanges) except Exception: pass self.bt_format = None self.bt_app = None self.logger.i('%s%s' % ('Quit', '')) class ScanVerify: def __init__(self, logger, rule_file): self.logger = logger self.checker_list = [['无', '.*']] self.checker_list.extend(getJson(os.path.abspath(rule_file), [])) self.checker_current_index = 0 def set_check(self, data: int): self.checker_current_index = data self.logger.i('%s%s' % ('Checker set to ', self.checker_list[self.checker_current_index][0])) def verify(self, content: str): return bool(re.match(self.checker_list[self.checker_current_index][1], content)) class CustomLineEdit(QLineEdit): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #000000;') def contextMenuEvent(self, event): menu = QMenu(self) action_c = menu.addAction('复制') action_p = menu.addAction('粘贴') action_c.triggered.connect(self.copy) action_p.triggered.connect(self.paste) menu.popup(self.mapToGlobal(event.pos())) class CustomLineEditNoPopup(QLineEdit): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #656565;') def contextMenuEvent(self, event): pass class CustomPushButton(QPushButton): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #0C0C0C;') class CustomLabel(QLabel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #0C0C0C; border: 1px solid #0C0C0C;') class CustomComboBox(QComboBox): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #0C0C0C;') class CustomCheckBox(QCheckBox): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setStyleSheet('font-size: 12px; font-family: \'Microsoft YaHei\'; color: #0C0C0C;') class CustomHBoxLayout(QHBoxLayout): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setAlignment(Qt.AlignTop | Qt.AlignLeft) class CustomVBoxLayout(QVBoxLayout): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setAlignment(Qt.AlignTop | Qt.AlignLeft) class MainWindow(QMainWindow): def __init__(self, logger: Logger): super().__init__() self.app_name = '标签打印' self.app_version = ('1.0.1', '20240401', 'zhaoyafan', 'zhaoyafan@foxmail.com', 'https://www.fanscloud.net/') self.logger = logger self.setting = Setting(os.path.abspath(os.path.join(os.path.dirname(__file__), 'settings.json')), {}) 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.ditto = Ditto(db_file='%s.db' % (os.path.splitext(__file__)[0],), class_name='LabelPrint', limit_time=7776000, limit_rows=100000) self.last_opened_template = ['', ''] self.input_convert_letter = 0 self.setWindowTitle(self.app_name) self.setGeometry(0, 0, 600, 400) self.statusBar() self.menubar = self.menuBar() filemenu = self.menubar.addMenu('文件') self.blockRepeatAction = QAction('禁止重复打印', self, checkable=True) self.clearRecordAction = QAction('清空打印记录', self) filemenu.addActions( [ self.blockRepeatAction, self.clearRecordAction ] ) moremenu = self.menubar.addMenu('更多') self.designStateAction = QAction('编辑模式', self, checkable=True) self.createShortAction = QAction('发送快捷方式到桌面', self) self.aboutWindowAction = QAction('关于', self) self.blockRepeatAction.triggered.connect(self.blockRepeatActionFunction) self.clearRecordAction.triggered.connect(self.clearRecordActionFunction) self.designStateAction.triggered.connect(self.designStateActionFunction) self.createShortAction.triggered.connect(self.createShortActionFunction) self.aboutWindowAction.triggered.connect(self.aboutWindowActionFunction) moremenu.addActions( [ self.designStateAction, self.createShortAction, self.aboutWindowAction ] ) # 定义布局 m_layout_0 = CustomVBoxLayout() l_layout_0 = CustomVBoxLayout() l_layout_1 = CustomHBoxLayout() l_layout_2 = CustomVBoxLayout() r_layout_0 = CustomVBoxLayout() r_layout_1 = CustomHBoxLayout() r_layout_2 = CustomHBoxLayout() r_layout_3 = CustomVBoxLayout() l_layout_0.addLayout(l_layout_1) l_layout_0.addLayout(l_layout_2) r_layout_0.addLayout(r_layout_1) r_layout_0.addLayout(r_layout_2) r_layout_0.addLayout(r_layout_3) m_layout_0.addLayout(l_layout_0) m_layout_0.addLayout(r_layout_0) # Select file self.butSTP = CustomPushButton('选择文件') self.butSTP.setFixedSize(82, 27) self.butSTP.clicked.connect(self.on_open_template_file) l_layout_1.addWidget(self.butSTP) # Template name self.edtNTP = CustomLineEditNoPopup('') self.edtNTP.setReadOnly(True) self.edtNTP.setFixedSize(280, 27) l_layout_1.addWidget(self.edtNTP) # Test printing self.butTPT = CustomPushButton('测试打印') self.butTPT.setFixedSize(82, 27) self.butTPT.clicked.connect(self.on_test_print) l_layout_1.addWidget(self.butTPT) # Print preview self.labPRV = CustomLabel() self.labPRV.setAlignment(Qt.AlignCenter) self.labPRV.setFixedSize(455, 256) l_layout_2.addWidget(self.labPRV) # Printer self.labNPT = CustomLabel('打印机选择') self.labNPT.setFixedSize(82, 27) r_layout_1.addWidget(self.labNPT) # Select printer self.slcSPT = CustomComboBox() self.slcSPT.addItems(self.bt.printer_list) self.slcSPT.setFixedSize(233, 27) self.slcSPT.currentIndexChanged.connect(self.on_printer_changed) r_layout_1.addWidget(self.slcSPT) # Number of copies self.labPQT = CustomLabel('打印份数') self.labPQT.setFixedSize(82, 27) r_layout_1.addWidget(self.labPQT) self.edtNQT = CustomLineEditNoPopup('1') self.edtNQT.setFixedSize(40, 27) self.edtNQT.textChanged.connect(self.on_copies_editing_changed) validator = QIntValidator(1, 999) self.edtNQT.setValidator(validator) r_layout_1.addWidget(self.edtNQT) # Input verification self.labNVR = CustomLabel('扫描验证') self.labNVR.setFixedSize(82, 27) r_layout_2.addWidget(self.labNVR) # Select input verification self.slcSVR = CustomComboBox() self.slcSVR.addItems([data[0] for data in self.sv.checker_list]) self.slcSVR.setFixedSize(233, 27) self.slcSVR.currentIndexChanged.connect(self.on_checker_changed) r_layout_2.addWidget(self.slcSVR) # Convert letter self.chkCVL = CustomCheckBox('强制转换大写') self.chkCVL.setFixedSize(128, 27) self.chkCVL.stateChanged.connect(self.on_convert_letter_changed) r_layout_2.addWidget(self.chkCVL) # Scan self.edtSCN = CustomLineEdit('') self.edtSCN.setStyleSheet('QLineEdit {font-size: 28px; font-family: \'Microsoft YaHei\'; color: #000000; background-color: #FFFFCC;}') self.edtSCN.setFixedSize(455, 45) r_layout_3.addWidget(self.edtSCN) # Print button self.butSPT = CustomPushButton('打印') self.butSPT.setFixedSize(455, 45) self.butSPT.clicked.connect(self.on_start_printing) self.edtSCN.returnPressed.connect(self.butSPT.click) r_layout_3.addWidget(self.butSPT) # 显示界面 central_widget = QWidget() central_widget.setLayout(m_layout_0) self.setCentralWidget(central_widget) self.setFixedSize(self.minimumSizeHint()) screen_rect = QApplication.desktop().availableGeometry() 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.edtSCN.setFocus() self.show() self.load_setting() def closeEvent(self, event): self.bt.quit() event.accept() def load_setting(self): # load blockRepeat self.blockRepeatAction.setChecked(bool(self.setting['blockRepeat'])) # load printer self.slcSPT.setCurrentIndex(next((index for index, item in enumerate(self.bt.printer_list) if item == self.setting['printer']), 0)) # load checker self.slcSVR.setCurrentIndex(self.setting['checker'] or 0) # load printCopies self.edtNQT.setText(str(self.setting['printCopies'] or 1)) # load template tempfile_last = self.setting['template'] if (tempfile_last and os.path.exists(tempfile_last)) == 1: self.load_template(tempfile_last) else: self.setting['template'] = '' def blockRepeatActionFunction(self, checked): self.setting['blockRepeat'] = bool(checked) def clearRecordActionFunction(self): count = self.ditto.clear() if (count > 0) == 1: QMessageBox.information(self, '提示', '打印记录已经清空:%s' % (count,)) def designStateActionFunction(self, checked): if (not self.bt.bt_app) == 1: return None if (checked is True) == 1: t1 = '1.在编辑模式中您可以动态对模板进行修改;' t2 = '2.保存模板将会覆盖原文件永久生效;' t3 = '3.修改完成后手动关闭编辑模式;' QMessageBox.information(self, '提示', '%s\n%s\n%s' % (t1, t2, t3)) self.bt.bt_app.Visible = bool(1) else: self.bt.bt_app.Visible = bool(0) filename = self.last_opened_template[0] if (filename == '' or not os.path.exists(filename)) == 1: return None last_md5 = self.last_opened_template[1] temp_md5 = self.md5(open(self.last_opened_template[0], 'rb')) QMessageBox.information(self, '提示', '模板文件已被修改:\n\n%s' % filename) if (last_md5 != temp_md5) == 1 else None self.last_opened_template[1] = temp_md5 def createShortActionFunction(self): target = '%s.exe' % (os.path.splitext(__file__)[0],) shortcut_path = self.path_expandvars('%%USERPROFILE%%\\Desktop\\%s.lnk' % (self.app_name,)) if (os.path.exists(target)) == 1: self.create_shortcut(target, shortcut_path, 1) QMessageBox.information(self, '提示', '%s' % ('已成功发送快捷方式到桌面',)) def aboutWindowActionFunction(self): _c = self.app_version 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], '%s' % (_c[4], _c[4]))) @staticmethod def md5(input_data): if isinstance(input_data, bytes): return hashlib.md5(input_data).hexdigest() if isinstance(input_data, str): return hashlib.md5(bytes(input_data, encoding='utf-8')).hexdigest() md5_object = hashlib.md5() while True: data = input_data.read(io.DEFAULT_BUFFER_SIZE) if data: md5_object.update(data) else: return md5_object.hexdigest() @staticmethod def path_expandvars(path): resolve = os.path.expandvars(path) if (re.search('%[A-Za-z0-9_]+%', resolve)) is not None: raise Exception('Variable analysis failed') return resolve @staticmethod def create_shortcut(target, shortcut_path, run_as_admin): shell = win32com.client.Dispatch('WScript.Shell') try: os.remove(shortcut_path) except FileNotFoundError: pass shortcut = shell.CreateShortCut(shortcut_path) shortcut.TargetPath = target if shortcut_path.endswith('.lnk'): shortcut.Arguments = '' shortcut.WorkingDirectory = os.path.dirname(target) if target.startswith('\\\\'): shortcut.WorkingDirectory = '' shortcut.save() if shortcut_path.endswith('.lnk') and run_as_admin: with open(shortcut_path, 'r+b') as f: if os.path.isfile(shortcut_path): f.seek(21, 0) f.write(b'\x22\x00') def set_preview(self, preview_image: str): if (preview_image and os.path.exists(preview_image)) == 1: pixmap = QPixmap(preview_image) pixmap = pixmap.scaled(self.labPRV.size(), aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) self.labPRV.setPixmap(pixmap) if (tempfile.gettempdir() in preview_image) == 1: rm(preview_image) def set_template_name(self, name): if (isinstance(name, str)) == 1: self.edtNTP.setText(name) def load_template(self, file): try: if (file != '' and os.path.exists(file)) == 1: self.bt.set_label(file) self.set_template_name(os.path.splitext(os.path.basename(file))[0]) self.set_preview(self.bt.generate_preview()) self.last_opened_template[0] = file self.last_opened_template[1] = self.md5(open(file, 'rb')) except pywintypes.com_error as e: t = str(e.args[1]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) if (len(t) > 5) == 1: m = '%s\n\n%s' % (t, m) t = '错误' QMessageBox.critical(self, t, m) except AttributeError: QMessageBox.critical(self, '错误', '调用服务失败') except Exception as e: QMessageBox.critical(self, '错误', '%s' % e) def on_open_template_file(self): filename, _ = QFileDialog.getOpenFileName(self, '打开文件', os.path.dirname(self.last_opened_template[0]), 'BarTender 文档 (*.btw)') if (filename and os.path.exists(filename)) == 1: self.load_template(filename) self.setting['template'] = filename def on_test_print(self): try: if (self.bt.bt_format is not None) == 1: self.bt.start_printing_template() self.set_preview(self.bt.generate_preview()) return None QMessageBox.critical(self, '错误', '请先加载模板文件') except pywintypes.com_error as e: t = str(e.args[1]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) if (len(t) > 5) == 1: m = '%s\n\n%s' % (t, m) t = '错误' QMessageBox.critical(self, t, m) except AttributeError: QMessageBox.critical(self, '错误', '调用服务失败') except Exception as e: QMessageBox.critical(self, '错误', '%s' % e) def on_printer_changed(self, index): try: self.bt.set_print(index) self.set_preview(self.bt.generate_preview()) self.setting['printer'] = str(self.bt.print) except pywintypes.com_error as e: t = str(e.args[1]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) if (len(t) > 5) == 1: m = '%s\n\n%s' % (t, m) t = '错误' QMessageBox.critical(self, t, m) except AttributeError: QMessageBox.critical(self, '错误', '调用服务失败') except Exception as e: QMessageBox.critical(self, '错误', '%s' % e) def on_checker_changed(self, index): try: self.sv.set_check(index) self.setting['checker'] = index except pywintypes.com_error as e: t = str(e.args[1]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) if (len(t) > 5) == 1: m = '%s\n\n%s' % (t, m) t = '错误' QMessageBox.critical(self, t, m) except AttributeError: QMessageBox.critical(self, '错误', '调用服务失败') except Exception as e: QMessageBox.critical(self, '错误', '%s' % e) def on_copies_editing_changed(self, text): try: if (text == '' or int(text) == 0) == 1: text = '1' self.edtNQT.setText(text) else: copies = int(text) self.bt.set_sheet(copies) self.setting['printCopies'] = copies except pywintypes.com_error as e: t = str(e.args[1]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) if (len(t) > 5) == 1: m = '%s\n\n%s' % (t, m) t = '错误' QMessageBox.critical(self, t, m) except AttributeError: QMessageBox.critical(self, '错误', '调用服务失败') except Exception as e: QMessageBox.critical(self, '错误', '%s' % e) def on_convert_letter_changed(self, checked): if (checked == 2) == 1: self.input_convert_letter = 1 else: self.input_convert_letter = 0 def on_start_printing(self): edit = self.edtSCN if (self.input_convert_letter == 1) == 1: text = edit.text().strip().upper() else: text = edit.text().strip() edit.setText(text) if (self.bt.bt_format is None) == 1: edit.selectAll() return None if (text == '') == 1: edit.selectAll() return None if (not self.sv.verify(text)) == 1: QMessageBox.critical(self, '错误', '打印的内容不符合验证规则:\n\n%s' % (text,)) edit.selectAll() return None if (not self.ditto.addit(text) and self.setting['blockRepeat']) == 1: QMessageBox.critical(self, '错误', '重复打印:\n\n%s' % (text,)) edit.selectAll() return None try: self.bt.start_printing(text) self.set_preview(self.bt.generate_preview()) edit.clear() except pywintypes.com_error as e: t = str(e.args[1]) m = str(e.args[2][2] if isinstance(e.args[2], tuple) else e.args[2]) if (len(t) > 5) == 1: m = '%s\n\n%s' % (t, m) t = '错误' QMessageBox.critical(self, t, m) except AttributeError: QMessageBox.critical(self, '错误', '调用服务失败') except Exception as e: QMessageBox.critical(self, '错误', '%s' % e) if __name__ == '__main__': 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'))) f_lock = open(file='%s.lock' % (os.path.splitext(__file__)[0],), mode='w', encoding='utf-8') if (not flock(f_lock, LOCK_EX | LOCK_NB)) == 1: app = QApplication(sys.argv) msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText('程序已经在运行中') msg.setWindowTitle('提示') msg.setStandardButtons(QMessageBox.Ok) msg.exec_() app.exit(1) sys.exit(1) try: win32com.client.Dispatch('BarTender.Application') except pywintypes.com_error: app = QApplication(sys.argv) msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText('初始化失败,请检查BarTender是否已安装。') msg.setWindowTitle('错误') msg.setStandardButtons(QMessageBox.Ok) msg.exec_() app.exit(1) sys.exit(1) app = QApplication(sys.argv) window_main = MainWindow(logger=Logger(name='main', fh=LoggerFileHandler(log_file='%s.log' % (os.path.splitext(__file__)[0],), mode='w'), ch=LoggerConsHandler())) sys.exit(app.exec_())