Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tomer fixes #108

Merged
merged 17 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ARG pip_deps="\
pre-commit \
ruff \
tox \
mypy \
"
RUN pip install --upgrade pip \
&& pip install ${pip_deps}
Expand Down
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"eamodio.gitlens",
"ms-azuretools.vscode-docker",
"tamasfe.even-better-toml",
"zhoufeng.pyqt-integration"
"zhoufeng.pyqt-integration",
"charliermarsh.ruff",
"ms-python.mypy-type-checker"
]
}
},
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
.Python
env/
bin/
lib/
lib64/
parts/
var/
Expand Down
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ repos:
rev: v1.0.0
hooks:
- id: check-json5

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ source = "https://github.com/computerlyrik/dymoprint"
tracker = "https://github.com/computerlyrik/dymoprint/issues"

[project.scripts]
dymoprint = "dymoprint.command_line:main"
dymoprint = "dymoprint.cli.cli:main"
dymoprint_gui = "dymoprint.gui.gui:main"

[tool.hatch.version]
Expand Down Expand Up @@ -183,3 +183,9 @@ ignore = [
"ISC002", # multi-line-implicit-string-concatenation
]
target-version = "py38"

[tool.mypy]
exclude = ["_vendor"]
ignore_missing_imports = true
check_untyped_defs = true
install_types = true
4 changes: 2 additions & 2 deletions src/dymoprint/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .labeler import DymoLabeler
from .metadata import __version__
from dymoprint.lib.labeler import DymoLabeler
from dymoprint.metadata import __version__

__all__ = ["__version__", "DymoLabeler"]
55 changes: 31 additions & 24 deletions src/dymoprint/command_line.py → src/dymoprint/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,33 @@
# === END LICENSE STATEMENT ===

import argparse
import sys
import webbrowser
from pathlib import Path
from tempfile import NamedTemporaryFile

from PIL import Image, ImageOps

from . import __version__
from .constants import (
from dymoprint import __version__
from dymoprint.lib.constants import (
AVAILABLE_BARCODES,
DEFAULT_MARGIN_PX,
PIXELS_PER_MM,
USE_QR,
e_qrcode,
)
from .detect import detect_device
from .dymo_print_engines import DymoRenderEngine, print_label
from .font_config import available_fonts, font_filename
from .metadata import our_metadata
from .unicode_blocks import image_to_unicode
from .utils import die
from dymoprint.lib.detect import detect_device
from dymoprint.lib.dymo_print_engines import DymoRenderEngine, print_label
from dymoprint.lib.font_config import FontConfig, FontStyle, NoFontFound
from dymoprint.lib.unicode_blocks import image_to_unicode
from dymoprint.lib.utils import die
from dymoprint.metadata import our_metadata

FLAG_TO_STYLE = {
"r": FontStyle.REGULAR,
"b": FontStyle.BOLD,
"i": FontStyle.ITALIC,
"n": FontStyle.NARROW,
}


def parse_args():
Expand All @@ -48,6 +55,7 @@ def parse_args():
)
parser.add_argument(
"-s",
"--style",
choices=["r", "b", "i", "n"],
default="r",
help="Set fonts style (regular,bold,italic,narrow)",
Expand Down Expand Up @@ -177,21 +185,20 @@ def main():
render_engine = DymoRenderEngine(args.t)

# read config file
FONT_FILENAME = font_filename(args.s)
style = FLAG_TO_STYLE.get(args.style)
try:
font_config = FontConfig(font=args.font, style=style)
except NoFontFound as e:
valid_font_names = [f.stem for f in FontConfig.available_fonts()]
print(
f"Valid fonts are: {', '.join(valid_font_names)}.",
file=sys.stderr,
)
raise e

labeltext = args.text
font_filename = font_config.path

if args.font is not None:
if Path(args.font).is_file():
FONT_FILENAME = args.font
else:
try:
FONT_FILENAME = next(
f.absolute() for f in available_fonts() if args.font == f.stem
)
except StopIteration:
fonts = ",".join(f.stem for f in available_fonts())
die(f"Error: file '{args.font}' not found. Available fonts: {fonts}")
labeltext = args.text

# check if barcode, qrcode or text should be printed, use frames only on text
if args.qr and not USE_QR:
Expand Down Expand Up @@ -222,15 +229,15 @@ def main():
elif args.barcode_text:
bitmaps.append(
render_engine.render_barcode_with_text(
labeltext.pop(0), args.barcode_text, FONT_FILENAME, args.f
labeltext.pop(0), args.barcode_text, font_filename, args.f
)
)

if labeltext:
bitmaps.append(
render_engine.render_text(
text_lines=labeltext,
font_file_name=FONT_FILENAME,
font_file_name=font_filename,
frame_width_px=args.f,
font_size_ratio=int(args.scale) / 100.0,
align=args.a,
Expand Down
39 changes: 0 additions & 39 deletions src/dymoprint/font_config.py

This file was deleted.

8 changes: 4 additions & 4 deletions src/dymoprint/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
)
from usb.core import NoBackendError, USBError

from dymoprint.constants import DEFAULT_MARGIN_PX, ICON_DIR
from dymoprint.detect import detect_device
from dymoprint.dymo_print_engines import DymoRenderEngine, print_label
from dymoprint.lib.constants import DEFAULT_MARGIN_PX, ICON_DIR
from dymoprint.lib.detect import DeviceDetectionError, detect_device
from dymoprint.lib.dymo_print_engines import DymoRenderEngine, print_label

from .q_dymo_labels_list import QDymoLabelList

Expand Down Expand Up @@ -194,7 +194,7 @@ def check_status(self):
try:
self.detected_device = detect_device()
is_enabled = True
except (NoBackendError, USBError) as e:
except (DeviceDetectionError, NoBackendError, USBError) as e:
self.error_label.setText(f"Error: {e}")
self.detected_device = None
self.error_label.setVisible(not is_enabled)
Expand Down
16 changes: 9 additions & 7 deletions src/dymoprint/gui/q_dymo_label_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@
QWidget,
)

from dymoprint.constants import AVAILABLE_BARCODES, ICON_DIR
from dymoprint.dymo_print_engines import DymoRenderEngine
from dymoprint.font_config import parse_fonts
from dymoprint.lib.constants import AVAILABLE_BARCODES, ICON_DIR
from dymoprint.lib.dymo_print_engines import DymoRenderEngine
from dymoprint.lib.font_config import FontConfig


class FontStyle(QComboBox):
def __init__(self):
super().__init__()
# Populate font_style
for name, font_path in parse_fonts():
self.addItem(name, font_path)
for font_path in FontConfig.available_fonts():
name = font_path.stem
absolute_path = font_path.absolute()
self.addItem(name, absolute_path)
self.setCurrentText("Carlito-Regular")


Expand All @@ -48,6 +50,8 @@ class BaseDymoLabelWidget(QWidget):
Abstract method to be implemented by subclasses for rendering the label.
"""

render_engine: DymoRenderEngine

itemRenderSignal = QtCore.pyqtSignal(name="itemRenderSignal")

def content_changed(self):
Expand Down Expand Up @@ -88,7 +92,6 @@ class TextDymoLabelWidget(BaseDymoLabelWidget):
itemRenderSignal: A signal emitted when the content of the label changes.
"""

render_engine: DymoRenderEngine
align: QComboBox
label: QPlainTextEdit
font_style: FontStyle
Expand Down Expand Up @@ -243,7 +246,6 @@ class BarcodeDymoLabelWidget(BaseDymoLabelWidget):
and barcode type.
"""

render_engine: DymoRenderEngine
label: QLineEdit
barcode_type_label: QLabel
barcode_type: QComboBox
Expand Down
26 changes: 17 additions & 9 deletions src/dymoprint/gui/q_dymo_labels_list.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from typing import Optional

from PIL import Image
from PyQt6 import QtCore
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QAbstractItemView, QListWidget, QListWidgetItem, QMenu

from .q_dymo_label_widgets import (
from dymoprint.gui.q_dymo_label_widgets import (
BarcodeDymoLabelWidget,
ImageDymoLabelWidget,
QrDymoLabelWidget,
TextDymoLabelWidget,
)
from dymoprint.lib.dymo_print_engines import DymoRenderEngine


class QDymoLabelList(QListWidget):
Expand Down Expand Up @@ -38,6 +42,8 @@ class QDymoLabelList(QListWidget):
"""

renderSignal = QtCore.pyqtSignal(Image.Image, name="renderSignal")
render_engine: DymoRenderEngine
itemWidget: TextDymoLabelWidget

def __init__(
self, render_engine, min_payload_len_px=0, justify="center", parent=None
Expand Down Expand Up @@ -66,7 +72,9 @@ def dropEvent(self, e) -> None:
super().dropEvent(e)
self.render_label()

def update_params(self, render_engine, min_payload_len_px=0, justify="center"):
def update_params(
self, render_engine: DymoRenderEngine, min_payload_len_px=0, justify="center"
):
"""Update the render engine used for rendering the label.

Args:
Expand Down Expand Up @@ -109,11 +117,11 @@ def contextMenuEvent(self, event):
event (QContextMenuEvent): The context menu event.
"""
contextMenu = QMenu(self)
add_text = contextMenu.addAction("Add Text")
add_qr = contextMenu.addAction("Add QR")
add_barcode = contextMenu.addAction("Add Barcode")
add_img = contextMenu.addAction("Add Image")
delete = contextMenu.addAction("Delete")
add_text: Optional[QAction] = contextMenu.addAction("Add Text")
add_qr: Optional[QAction] = contextMenu.addAction("Add QR")
add_barcode: Optional[QAction] = contextMenu.addAction("Add Barcode")
add_img: Optional[QAction] = contextMenu.addAction("Add Image")
delete: Optional[QAction] = contextMenu.addAction("Delete")
menu_click = contextMenu.exec(event.globalPos())

if menu_click == add_text:
Expand Down Expand Up @@ -149,8 +157,8 @@ def contextMenuEvent(self, event):
item_widget.itemRenderSignal.connect(self.render_label)
if menu_click == delete:
try:
item = self.itemAt(event.pos())
self.takeItem(self.indexFromItem(item).row()) # self.update()
item_to_delete = self.itemAt(event.pos())
self.takeItem(self.indexFromItem(item_to_delete).row()) # self.update()
except Exception as e: # noqa: BLE001
print(f"No item selected {e}")
self.render_label()
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,19 @@ def _init(self, code):
self._draw = ImageDraw.Draw(self._image)

def _paint_module(self, xpos, ypos, width, color):
size = [
size = (
(mm2px(xpos, self.dpi), mm2px(ypos, self.dpi)),
(
mm2px(xpos + width, self.dpi),
mm2px(ypos + self.module_height, self.dpi),
),
]
)
assert self._draw is not None
self._draw.rectangle(size, outline=color, fill=color)

def _finish(self):
# although Image mode set to "1", draw function writes white as 255
assert self._image is not None
self._image = self._image.point(lambda x: 1 if x > 0 else 0, mode="1")
return self._image

Expand Down
33 changes: 33 additions & 0 deletions src/dymoprint/lib/config_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from configparser import ConfigParser
from pathlib import Path

from platformdirs import user_config_dir


class SectionNotFound(Exception):
def __init__(self, config_file_path, section_name):
msg = f"Section {section_name} not fount in {config_file_path}"
super().__init__(msg)


class ConfigFile:
_CONFIG_FILE_PATH = Path(user_config_dir()) / "dymoprint.ini"
_config_parser = None

def __init__(self):
config_parser = ConfigParser()
if config_parser.read(self._CONFIG_FILE_PATH):
self._config_parser = config_parser

def section(self, section_name):
"""Return the given config file section as dict."""
if self._config_parser:
try:
return dict(self._config_parser[section_name])
except KeyError:
raise SectionNotFound(self._CONFIG_FILE_PATH, section_name) from None
return None

@property
def fonts_section(self):
return self.section("FONTS")
Loading