Обновить print-labels-gui.py
переделка под графический интерфейс добавление логики под графический интерфейс мелкие правки передачи данных в методы генерации
This commit is contained in:
parent
a57b7f1ea2
commit
6ac996d6c9
106
barcodegen.py
106
barcodegen.py
@ -1,106 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import pathlib
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from textwrap import dedent
|
|
||||||
|
|
||||||
from PIL import Image, ImageOps, ImageDraw, ImageFont
|
|
||||||
from barcode import EAN13
|
|
||||||
from barcode.writer import ImageWriter
|
|
||||||
|
|
||||||
PRODUCT_TYPES = {
|
|
||||||
"comp": ("21", "инвентарный номер для компьютера"),
|
|
||||||
"mon": ("22", "инвентарный номер для монитора"),
|
|
||||||
"net": ("23", "инвентарный номер для сетевого оборудования"),
|
|
||||||
"token": ("24", "инвентарный номер для токена"),
|
|
||||||
"print": ("25", "инвентарный номер для принтера/МФУ"),
|
|
||||||
"flash": ("26", "инвентарный номер для флешки"),
|
|
||||||
"hid": ("27", "инвентарный номер для HID устройств"),
|
|
||||||
"ups": ("28", "инвентарный номер для UPS устройств"),
|
|
||||||
"other": ("29", "инвентарный номер для других устройств"),
|
|
||||||
}
|
|
||||||
|
|
||||||
BARCODES_PATH = pathlib.Path().absolute() / "barcodes"
|
|
||||||
|
|
||||||
|
|
||||||
def gen_ean13(ptype: str):
|
|
||||||
product_code = PRODUCT_TYPES[ptype][0]
|
|
||||||
digits = product_code + str(round(time.time()))
|
|
||||||
digits = [int(i) for i in digits]
|
|
||||||
checksum = (10 - (sum(digits[1::2]) * 3 + sum(digits[::2])) % 10) % 10
|
|
||||||
return "".join(str(i) for i in (digits + [checksum]))
|
|
||||||
|
|
||||||
|
|
||||||
def create_code_png(filename: str | pathlib.Path, code, text):
|
|
||||||
if not filename.parent.exists():
|
|
||||||
filename.parent.mkdir(parents=True)
|
|
||||||
|
|
||||||
my_code = EAN13(code, writer=ImageWriter())
|
|
||||||
|
|
||||||
image = my_code.render(
|
|
||||||
writer_options={
|
|
||||||
"module_height": 11.0,
|
|
||||||
"module_width": 0.3,
|
|
||||||
"dpi": 169,
|
|
||||||
"font_size": 0,
|
|
||||||
"text_distance": 1,
|
|
||||||
"write_text": None,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
image = image.convert("1", dither=Image.Dither.NONE)
|
|
||||||
image = ImageOps.expand(image, border=17, fill=1)
|
|
||||||
draw = ImageDraw.Draw(image)
|
|
||||||
dst = "terminus.pil"
|
|
||||||
font = ImageFont.load(dst)
|
|
||||||
text_length = draw.textlength(text, 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 - code_text_length) / 2, 100),
|
|
||||||
code,
|
|
||||||
fill="black",
|
|
||||||
font=font,
|
|
||||||
)
|
|
||||||
image.save(filename)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
help_for_text = """\
|
|
||||||
code is must be a 13 digit code
|
|
||||||
or one of the following:
|
|
||||||
|
|
||||||
"""
|
|
||||||
help_for_text = dedent(help_for_text) + "\n".join(
|
|
||||||
f"{code} - {desc[1]}" for code, desc in PRODUCT_TYPES.items()
|
|
||||||
)
|
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
|
|
||||||
parser.add_argument("code", type=str, help=help_for_text)
|
|
||||||
parser.add_argument(
|
|
||||||
"text", type=str, nargs="?", default="", help="Description for barcode"
|
|
||||||
)
|
|
||||||
parser.add_argument("--print", action=argparse.BooleanOptionalAction, default=True)
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parse_args()
|
|
||||||
if args.code not in PRODUCT_TYPES:
|
|
||||||
if args.code.isdigit() and len(args.code) == 13:
|
|
||||||
barcode = args.code
|
|
||||||
else:
|
|
||||||
print("Invalid args")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
barcode = gen_ean13(args.code)
|
|
||||||
|
|
||||||
filename = BARCODES_PATH / f"{barcode}.png"
|
|
||||||
print(f"Generated code: {barcode}")
|
|
||||||
create_code_png(filename, barcode, args.text)
|
|
||||||
if args.print:
|
|
||||||
subprocess.run(["ptouch-print", "--image", filename])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
248
print-labels-gui.py
Executable file
248
print-labels-gui.py
Executable file
@ -0,0 +1,248 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
"""
|
||||||
|
|
||||||
|
Скрипт с графическим интерфейсом
|
||||||
|
для печати этикеток на принтере серий brother p-touch,
|
||||||
|
на основе https://gitea.basealt.ru/polkovnikovaav/ptouch-print-labels
|
||||||
|
|
||||||
|
|
||||||
|
подробности и настройки в README.md
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QApplication
|
||||||
|
from PySide6 import QtGui,QtWidgets
|
||||||
|
from PySide6.QtWidgets import QMainWindow, QWidget, QLabel,QVBoxLayout, QPushButton,QGridLayout,QLineEdit, QComboBox,QMessageBox
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PIL import Image, ImageOps, ImageDraw, ImageFont
|
||||||
|
from barcode import EAN13
|
||||||
|
from barcode.writer import ImageWriter
|
||||||
|
|
||||||
|
# главное окно
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.conf="barcode2-gui.ini"
|
||||||
|
|
||||||
|
self.setWindowTitle('Печать стикеров')
|
||||||
|
self.q_button_start = QPushButton('печать')
|
||||||
|
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.q_label2.setText("Введите SN (не вводить - генерация):")
|
||||||
|
self.q_label3.setText("Выберите тип устройства:")
|
||||||
|
|
||||||
|
self.q_combo = QComboBox(self)
|
||||||
|
|
||||||
|
self.q_combo.addItem("компьютер")
|
||||||
|
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.grid_layout = QGridLayout()
|
||||||
|
self.layout1 = QVBoxLayout()
|
||||||
|
|
||||||
|
self.q_edit1=QLineEdit() # текст (над ШК)
|
||||||
|
self.q_edit2=QLineEdit() # вводимый свой серийник (если не нужна генерациия)
|
||||||
|
self.q_edit3=QLineEdit() # выводимый SN для справки
|
||||||
|
self.q_edit3.setReadOnly(True)
|
||||||
|
|
||||||
|
self.layout1.addWidget(self.q_label1)
|
||||||
|
self.layout1.addWidget(self.q_edit1)
|
||||||
|
self.layout1.addWidget(self.q_label2)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
self.grid_layout.setRowStretch(5,0)
|
||||||
|
self.grid_layout.setRowStretch(20,1)
|
||||||
|
self.grid_layout.setRowStretch(1,2)
|
||||||
|
self.grid_layout.setRowStretch(1,3)
|
||||||
|
self.grid_layout.setRowStretch(1,4)
|
||||||
|
self.grid_layout.setRowStretch(100,5)
|
||||||
|
self.grid_layout.setRowStretch(1,6)
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
|
||||||
|
"компьютер":"21",
|
||||||
|
"монитор":"22",
|
||||||
|
"сетевое об.":"23",
|
||||||
|
"токен":"24",
|
||||||
|
"принтер/МФУ":"25",
|
||||||
|
"флешка":"26",
|
||||||
|
"HID":"27",
|
||||||
|
"UPS":"28",
|
||||||
|
"другое":"29",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# получаем текущий каталог и переходим в него
|
||||||
|
def set_current_location(self):
|
||||||
|
path_there = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
os.chdir(path_there)
|
||||||
|
|
||||||
|
# генерация штрих-кода с контрльной суммой
|
||||||
|
def gen_ean13(self,product_code: str):
|
||||||
|
|
||||||
|
# добавляем псевдослучаное значение (от времени к коду продукта)
|
||||||
|
digits_s= product_code + str(round(time.time()))
|
||||||
|
|
||||||
|
|
||||||
|
# строка продукта в список чисел
|
||||||
|
digits = [int(i) for i in digits_s]
|
||||||
|
|
||||||
|
# контрольная сумма по алгоритму ЛУна
|
||||||
|
checksum = (10 - (sum(digits[1::2]) * 3 + sum(digits[::2])) % 10) % 10
|
||||||
|
return "".join(str(i) for i in (digits + [checksum]))
|
||||||
|
|
||||||
|
# генерация картинки
|
||||||
|
def create_code_png(self,filename, code, text):
|
||||||
|
self.set_current_location()
|
||||||
|
ean13_code = EAN13(code, writer=ImageWriter())
|
||||||
|
|
||||||
|
image = ean13_code.render(
|
||||||
|
writer_options={
|
||||||
|
"module_height": 11.0,
|
||||||
|
"module_width": 0.3,
|
||||||
|
"dpi": 169,
|
||||||
|
"font_size": 0,
|
||||||
|
"text_distance": 1,
|
||||||
|
"write_text": None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
image = image.convert("1", dither=Image.Dither.NONE)
|
||||||
|
image = ImageOps.expand(image, border=17, fill=1)
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
|
# загружаем шрифт
|
||||||
|
dst = "terminus.pil"
|
||||||
|
font = ImageFont.load(dst)
|
||||||
|
|
||||||
|
text_length = draw.textlength(text, 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 - code_text_length) / 2, 100),
|
||||||
|
code,
|
||||||
|
fill="black",
|
||||||
|
font=font,
|
||||||
|
)
|
||||||
|
|
||||||
|
image.save(filename)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#запуск всего процесса (генерация и печать)
|
||||||
|
def start_print(self):
|
||||||
|
|
||||||
|
|
||||||
|
# берем ID по типу оборудования
|
||||||
|
type_device=self.combo_type_items[self.q_combo.currentText()]
|
||||||
|
|
||||||
|
# берем текст подписи
|
||||||
|
text_data=self.q_edit1.text()
|
||||||
|
|
||||||
|
barcode = self.gen_ean13(type_device)
|
||||||
|
|
||||||
|
# если ввели свой SN, передаем его в создание ШК
|
||||||
|
if len(self.q_edit2.text())>0:
|
||||||
|
if len(self.q_edit2.text())==12:
|
||||||
|
barcode=self.q_edit2.text()
|
||||||
|
else:
|
||||||
|
msg = QMessageBox()
|
||||||
|
msg.setWindowTitle("Внимание")
|
||||||
|
msg.setText("Должно быть 12 знаков")
|
||||||
|
msg.setIcon(QMessageBox.Warning)
|
||||||
|
msg.exec_()
|
||||||
|
exit(0)
|
||||||
|
if len(self.q_edit2.text())==0:
|
||||||
|
self.q_edit3.setText(barcode)
|
||||||
|
|
||||||
|
self.create_code_png("temp.png", barcode, text_data)
|
||||||
|
self.start_process()
|
||||||
|
|
||||||
|
# запуск непосредственно печати, с SUDO
|
||||||
|
def start_process(self):
|
||||||
|
password, ok = QtWidgets.QInputDialog.getText(None, "SUDO", "Введите пароль:",QLineEdit.Password)
|
||||||
|
if ok:
|
||||||
|
subprocess.run(["sudo", "-S", "ptouch-print", "--image", "temp.png"], input=password.encode() + b"\n")
|
||||||
|
|
||||||
|
# если без sudo
|
||||||
|
# def start_process(self):
|
||||||
|
# subprocess.run(["sudo","ptouch-print", "--image", "temp.png"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
configparser = ChildProcessError
|
||||||
|
|
||||||
|
# таблица стилей (задаем размер шрифта)
|
||||||
|
style_sheet01 = """
|
||||||
|
* {
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Применение таблицы стилей (размер шрифта) ко всему приложению
|
||||||
|
app.setStyleSheet(style_sheet01)
|
||||||
|
|
||||||
|
window = MainWindow()
|
||||||
|
|
||||||
|
# Установить размер окна
|
||||||
|
window.resize(500, 300)
|
||||||
|
|
||||||
|
# Получить допустимое разрешение
|
||||||
|
SrcSize = QtGui.QScreen.availableGeometry(QApplication.primaryScreen())
|
||||||
|
|
||||||
|
frmX = (SrcSize.width() - window.width())/2
|
||||||
|
frmY = (SrcSize.height() - window.height())/2
|
||||||
|
window.move(frmX, frmY)
|
||||||
|
|
||||||
|
window.show()
|
||||||
|
sys.exit(app.exec_())
|
Loading…
x
Reference in New Issue
Block a user