Интерфейс переработан под возможность пакетной печати

Реализована пакетная печать
Реализована проверка вводимого инв. номера на корректность
Прочие мелкие правки
This commit is contained in:
Юрий Кожушко 2024-10-14 13:55:43 +03:00
parent 9f36f2bbb8
commit acd6ca8db0
4 changed files with 174 additions and 132 deletions

0
README.md Executable file → Normal file
View File

306
print-labels-gui.py Executable file → Normal file
View File

@ -1,122 +1,139 @@
#!/usr/bin/python3 #!/usr/bin/python3
""" """
Скрипт с графическим интерфейсом Скрипт с графическим интерфейсом
для печати этикеток на принтере серий brother p-touch, для печати этикеток на принтере серий brother p-touch
на основе https://gitea.basealt.ru/polkovnikovaav/ptouch-print-labels
(форк) на основе скрипта Анны Полковниковой (https://gitea.basealt.ru/polkovnikovaav/ptouch-print-labels)
подробности и настройки в README.md подробности и настройки в README.md
зи
""" """
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
from PySide6 import QtGui,QtWidgets from PySide6 import QtGui
from PySide6.QtWidgets import QMainWindow, QWidget, QLabel,QVBoxLayout, QPushButton,QGridLayout,QLineEdit, QComboBox,QMessageBox from PySide6.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QPushButton, QComboBox, QTableWidget, QHBoxLayout, QMessageBox, QTableWidgetItem
import subprocess import subprocess
import time import time
import os import os
import sys import sys
import glob
import re
from PIL import Image, ImageOps, ImageDraw, ImageFont from PIL import Image, ImageOps, ImageDraw, ImageFont
from barcode import EAN13 from barcode import EAN13
from barcode.writer import ImageWriter from barcode.writer import ImageWriter
# главное окно
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.version = "v1.3"
self.conf="barcode2-gui.ini"
self.setWindowTitle('Печать стикеров') self.device_names = ['компьютер', 'монитор', 'сетевое об.',
self.q_button_start = QPushButton('печать') 'токен', 'принтер/МФУ', 'флешка', 'HID', 'UPS', 'другое']
self.q_button_start.clicked.connect(self.start_print)
self.q_label1 = QLabel()
self.q_label2 = QLabel()
self.q_label3 = QLabel()
self.q_label1.setText("Введите текст:") self.setWindowTitle(f"Печать стикеров инвентаризации {self.version}")
self.q_label2.setText("Введите SN (не вводить - генерация):") self.resize(700, 500)
self.q_label3.setText("Выберите тип устройства:")
self.q_combo = QComboBox(self) # таблица
self.q_table = QTableWidget()
self.q_combo.addItem("компьютер") self.init_table()
self.q_combo.addItem("монитор")
self.q_combo.addItem("сетевое об.")
self.q_combo.addItem("токен")
self.q_combo.addItem("принтер/МФУ")
self.q_combo.addItem("флешка")
self.q_combo.addItem("HID")
self.q_combo.addItem("UPS")
self.q_combo.addItem("другое")
self.q_button_start=QPushButton("печать")
self.q_button_start.setMinimumSize(20,80)
self.q_button_start.clicked.connect(self.start_print) # кнопка печати
self.q_print_button = QPushButton()
self.q_print_button.setText("печать")
self.q_print_button.clicked.connect(self.start_print)
self.q_print_button.setMinimumSize(15, 50)
self.grid_layout = QGridLayout() # кнопка очистки
self.layout1 = QVBoxLayout() self.q_clear_button = QPushButton()
self.q_clear_button.setText("очистка")
self.q_edit1=QLineEdit() # текст (над ШК) self.q_clear_button.clicked.connect(self.init_table)
self.q_edit2=QLineEdit() # вводимый свой серийник (если не нужна генерациия) self.q_clear_button.setMinimumSize(15, 50)
self.q_edit3=QLineEdit() # выводимый SN для справки
self.q_edit3.setReadOnly(True)
self.layout1.addWidget(self.q_label1) self.qh_layout2 = QHBoxLayout()
self.layout1.addWidget(self.q_edit1) self.qh_layout2.addWidget(self.q_print_button)
self.layout1.addWidget(self.q_label2) self.qh_layout2.addWidget(self.q_clear_button)
self.layout1.addWidget(self.q_edit2)
self.layout1.addWidget(self.q_label3)
self.layout1.addWidget(self.q_combo)
self.layout1.addWidget(self.q_button_start)
self.layout1.addWidget(self.q_edit3)
# компоновщик1 вертикальный
self.qv_layout1 = QVBoxLayout()
self.qv_layout1.setContentsMargins(15, 15, 15, 15)
self.qv_layout1.setSpacing(15)
self.qv_layout1.addWidget(self.q_table)
self.qv_layout1.addLayout(self.qh_layout2)
self.grid_layout.setRowStretch(5,0) # компоновщик горизонтальный
self.grid_layout.setRowStretch(20,1) self.qh_layout = QHBoxLayout()
self.grid_layout.setRowStretch(1,2) self.qh_layout.addLayout(self.qv_layout1)
self.grid_layout.setRowStretch(1,3)
self.grid_layout.setRowStretch(1,4) # обобщающий виджет
self.grid_layout.setRowStretch(100,5) self.q_central_widget = QWidget()
self.grid_layout.setRowStretch(1,6) self.q_central_widget.setLayout(self.qh_layout)
self.setCentralWidget(self.q_central_widget)
self.grid_layout.addWidget(self.q_label1, 0,0)
self.grid_layout.addWidget(self.q_edit1, 1,0)
self.grid_layout.addWidget(self.q_label2, 2,0)
self.grid_layout.addWidget(self.q_edit2, 3,0)
self.grid_layout.addWidget(self.q_label3, 4,0)
self.grid_layout.addWidget(self.q_combo, 5,0)
self.grid_layout.addWidget(self.q_button_start,6,0)
self.grid_layout.addWidget(self.q_edit3,7,0) # шк-код текстом
self.content_widget = QWidget()
self.content_widget.setLayout(self.grid_layout)
self.setCentralWidget(self.content_widget)
# выпадающий список типов и кодовые соответствия # выпадающий список типов и кодовые соответствия
self.combo_type_items = { self.combo_type_items = {
"компьютер":"21",
"монитор":"22",
"сетевое об.":"23",
"токен":"24",
"принтер/МФУ":"25",
"флешка":"26",
"HID":"27",
"UPS":"28",
"другое":"29",
}
"компьютер": "21",
"монитор": "22",
"сетевое об.": "23",
"токен": "24",
"принтер/МФУ": "25",
"флешка": "26",
"HID": "27",
"UPS": "28",
"другое": "29",
}
def gen_message_box(self, message_str):
msg_box = QMessageBox()
msg_box.setWindowTitle("Внимание")
msg_box.setText(message_str)
msg_box.setIcon(QMessageBox.Information)
msg_box.exec()
# проверка строки на соответствие строго 12 цифр
def check_inventory_number(self, inventoryN):
pattern = r'^\d{12}$'
if re.match(pattern, inventoryN):
return True
else:
return False
# инициализация таблицы
def init_table(self):
self.q_table.clear()
self.q_table.setColumnCount(3)
self.q_table.setHorizontalHeaderLabels(
['Текст', 'Тип', 'Инв.Н (пустое - генерация)'])
self.q_table.setRowCount(10)
self.q_table.setColumnWidth(0, 200)
self.q_table.setColumnWidth(1, 110)
self.q_table.setColumnWidth(2, 200)
# кладем комбобоксы в ячейки
for i in range(0, 10):
self.q_combo = QComboBox(self)
self.q_combo.addItems(self.device_names)
self.q_table.setCellWidget(i, 1, self.q_combo)
# удаляем временные Png файлы
def clear_temp_png_files(self):
self.set_current_location()
fileList = glob.glob('*.png')
# Iterate over the list of filepaths & remove each file.
for filePath in fileList:
try:
os.remove(filePath)
except Exception:
print("Error while deleting file : ", filePath)
# получаем текущий каталог и переходим в него # получаем текущий каталог и переходим в него
def set_current_location(self): def set_current_location(self):
@ -124,21 +141,22 @@ class MainWindow(QMainWindow):
os.chdir(path_there) os.chdir(path_there)
# генерация штрих-кода с контрльной суммой # генерация штрих-кода с контрльной суммой
def gen_ean13(self,product_code: str): def gen_ean13(self, product_code: str):
# добавляем псевдослучаное значение (от времени к коду продукта) # добавляем псевдослучаное значение (от времени к коду продукта)
digits_s= product_code + str(round(time.time())) # при пакетной генерации между генерациями необходим интервал
time.sleep(1)
digits_s = product_code + str(round(time.time()))
# строка продукта в список чисел # строка продукта в список чисел
digits = [int(i) for i in digits_s] digits = [int(i) for i in digits_s]
# контрольная сумма по алгоритму ЛУна # контрольная сумма по алгоритму ЛУна
checksum = (10 - (sum(digits[1::2]) * 3 + sum(digits[::2])) % 10) % 10 checksum = (10 - (sum(digits[1::2]) * 3 + sum(digits[::2])) % 10) % 10
return "".join(str(i) for i in (digits + [checksum])) return "".join(str(i) for i in (digits + [checksum]))
# генерация картинки # генерация картинки
def create_code_png(self,filename, code, text): def create_code_png(self, filename, code, text):
self.set_current_location() self.set_current_location()
ean13_code = EAN13(code, writer=ImageWriter()) ean13_code = EAN13(code, writer=ImageWriter())
@ -156,14 +174,15 @@ class MainWindow(QMainWindow):
image = image.convert("1", dither=Image.Dither.NONE) image = image.convert("1", dither=Image.Dither.NONE)
image = ImageOps.expand(image, border=17, fill=1) image = ImageOps.expand(image, border=17, fill=1)
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
# загружаем шрифт # загружаем шрифт
dst = "terminus.pil" dst = "terminus.pil"
font = ImageFont.load(dst) font = ImageFont.load(dst)
text_length = draw.textlength(text, font=font) text_length = draw.textlength(text, font=font)
code_text_length = draw.textlength(code, font=font) code_text_length = draw.textlength(code, font=font)
draw.text(((image.width - text_length) / 2, 3), text, fill="black", font=font) draw.text(((image.width - text_length) / 2, 3),
text, fill="black", font=font)
draw.text( draw.text(
((image.width - code_text_length) / 2, 100), ((image.width - code_text_length) / 2, 100),
code, code,
@ -172,44 +191,67 @@ class MainWindow(QMainWindow):
) )
image.save(filename) image.save(filename)
# запуск всего процесса (генерация и печать)
#запуск всего процесса (генерация и печать)
def start_print(self): def start_print(self):
# удаляем старые png-файлы
# берем ID по типу оборудования self.clear_temp_png_files()
type_device=self.combo_type_items[self.q_combo.currentText()]
for row in range(0, self.q_table.rowCount()):
# берем текст подписи print(row)
text_data=self.q_edit1.text()
# проверка Инв. Номера на корректность
barcode = self.gen_ean13(type_device) if self.q_table.item(row, 2) is not None and self.check_inventory_number(self.q_table.item(row, 2).text()) is False:
self.gen_message_box(
# если ввели свой SN, передаем его в создание ШК "инвентарный номер должен содержать 12 цифр")
if len(self.q_edit2.text())>0: self.q_table.setItem(row, 2, QTableWidgetItem(""))
if len(self.q_edit2.text())==12: return None
barcode=self.q_edit2.text()
else: # если ТЕКСТ и ИН заполнены
msg = QMessageBox() if self.q_table.item(row, 0) is not None and self.q_table.item(row, 2) is not None:
msg.setWindowTitle("Внимание") print(self.q_table.item(row, 0).text())
msg.setText("Должно быть 12 знаков") combo_widget = self.q_table.cellWidget(row, 1)
msg.setIcon(QMessageBox.Warning) type_device = self.combo_type_items[combo_widget.currentText()]
msg.exec_() barcode = self.q_table.item(row, 2).text()
exit(0) self.create_code_png(
f"temp-{row}.png", barcode, self.q_table.item(row, 0).text())
self.q_edit3.setText(barcode)
# если только ТЕКСТ заполнен
self.create_code_png("temp.png", barcode, text_data) if self.q_table.item(row, 0) is not None and self.q_table.item(row, 2) is None:
print(self.q_table.item(row, 0).text())
combo_widget = self.q_table.cellWidget(row, 1)
type_device = self.combo_type_items[combo_widget.currentText()]
barcode = self.gen_ean13(type_device)
self.create_code_png(
f"temp-{row}.png", barcode, self.q_table.item(row, 0).text())
# запуск утилиты печати сфомированного изображения
self.start_process() self.start_process()
# удаляем временные файлы
self.clear_temp_png_files()
# запуск утилиты печати сфомированного изображения
def start_process(self): def start_process(self):
subprocess.run(["ptouch-print", "--image", "temp.png"])
# получаем список файлов
file_list_to_print = glob.glob('*.png')
# подготовка строки параметров для запуска
prepare_cmd = ""
for file_name in file_list_to_print:
prepare_cmd = prepare_cmd+" --image "+file_name+" --pad 5"
prepare_cmd=prepare_cmd[:-8]
print(prepare_cmd)
subprocess.run("ptouch-print "+prepare_cmd,shell=True)
if __name__ == '__main__': if __name__ == '__main__':
app = QApplication(sys.argv) app = QApplication(sys.argv)
@ -218,7 +260,7 @@ if __name__ == '__main__':
# таблица стилей (задаем размер шрифта) # таблица стилей (задаем размер шрифта)
style_sheet01 = """ style_sheet01 = """
* { * {
font-size: 11pt; font-size: 10pt;
} }
""" """
@ -228,14 +270,14 @@ if __name__ == '__main__':
window = MainWindow() window = MainWindow()
# Установить размер окна # Установить размер окна
window.resize(500, 300) window.resize(600, 500)
# Получить допустимое разрешение # Получить допустимое разрешение
SrcSize = QtGui.QScreen.availableGeometry(QApplication.primaryScreen()) SrcSize = QtGui.QScreen.availableGeometry(QApplication.primaryScreen())
frmX = (SrcSize.width() - window.width())/2 frmX = (SrcSize.width() - window.width())/2
frmY = (SrcSize.height() - window.height())/2 frmY = (SrcSize.height() - window.height())/2
window.move(frmX, frmY) window.move(frmX, frmY)
window.show() window.show()
sys.exit(app.exec_()) sys.exit(app.exec_())

0
terminus.pbm Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

0
terminus.pil Executable file → Normal file
View File