Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9884e0f
Fix wrong bitmap was shown in preview
Jan 31, 2024
ee8d291
Show actual vertical margins in preview mode
Jan 31, 2024
16ec16b
Coding: rename margins variable
Jan 31, 2024
96390da
gui_dev.sh: add verbosity
Feb 3, 2024
1dd19be
Helper render_enginers property
Feb 3, 2024
157a204
Update margins logic
Feb 3, 2024
36d64dc
Refactor DymoLabeler classes
Feb 4, 2024
cf8efef
Move preview rendering to preview render engine
Feb 6, 2024
04f1bb4
Fix vertical margin calculation
Feb 10, 2024
b94c13f
Show preview margins
Feb 10, 2024
bf3cb39
Show dimensions
Feb 10, 2024
c4ae74e
Configure CLI logging earlier
maresb Feb 10, 2024
5e6d105
Rework logger to initialize as verbose
maresb Feb 10, 2024
04e2176
Rename the verbose envvar to DYMOPRINT_VERBOSE
maresb Feb 10, 2024
40d52b3
Fix exception printing
maresb Feb 10, 2024
cb24bc4
Use functional style for config file
maresb Feb 10, 2024
89ef0bd
Remove unneeded classes for font config
maresb Feb 10, 2024
6b9d144
Refactor RenderContext
maresb Mar 3, 2024
ca7e0e4
Update Ruff pre-commit version
maresb Mar 3, 2024
a16922f
Update pyproject.toml to new ruff conventions
maresb Mar 3, 2024
ccacf6c
Use updated ruff docstring formatting
maresb Mar 3, 2024
055fa1c
Fix typos in explanatory "print mode" comment
maresb Mar 3, 2024
8851f0d
Use typing to declare possible values of align
maresb Mar 3, 2024
b15d8d1
Don't mutate RenderContext
maresb Mar 3, 2024
b790238
Install types-pillow to fix type checking
maresb Mar 3, 2024
d4df4da
Eliminate render_kwargs
maresb Mar 3, 2024
a041882
Major refactor for render engines
Mar 23, 2024
ee92b33
Use more specific exception
Mar 24, 2024
593e4c5
Add Markdown lint
Mar 24, 2024
d118bbb
Colorize margins according to system theme
Mar 24, 2024
a41d2b2
Update type hint for justify
Mar 24, 2024
6f67623
Update margin calculation comment
Mar 24, 2024
6a859d9
Use typing literal instead of enums
Mar 24, 2024
ad2f603
Fix lint issue
Mar 24, 2024
907c6dd
Update pre-commit repos
Mar 24, 2024
e5ed453
Add github.vscode-github-actions extension
Mar 24, 2024
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
7 changes: 4 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
"customizations": {
"vscode": {
"extensions": [
"charliermarsh.ruff",
"eamodio.gitlens",
"github.vscode-github-actions",
"ms-azuretools.vscode-docker",
"ms-python.mypy-type-checker",
"tamasfe.even-better-toml",
"zhoufeng.pyqt-integration",
"charliermarsh.ruff",
"ms-python.mypy-type-checker"
"zhoufeng.pyqt-integration"
]
}
},
Expand Down
13 changes: 10 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:
args: ['--fix=lf']

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.13
rev: v0.3.4
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand All @@ -31,6 +31,13 @@ repos:
- id: check-json5

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
rev: v1.9.0
hooks:
- id: mypy
- id: mypy
additional_dependencies:
- types-pillow

- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.39.0
hooks:
- id: markdownlint
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ For more information about experimental device support, see [#44](https://github

## Installation

It is recommended to install dymoprint with [pipx](https://pypa.github.io/pipx/) so that it runs in an isolated virtual environment:
It is recommended to install dymoprint with
[pipx](https://pypa.github.io/pipx/) so that it runs in an isolated virtual
environment:

```bash
pipx install dymoprint
Expand All @@ -53,6 +55,8 @@ By default, users don't have permission to access generic USB devices, so you wi
need to add a rule. The first time you run `dymoprint`, it will give instructions
about how to do this:

{{< mdl-disable "<!-- markdownlint-disable MD013 -->" >}}

```bash
$ dymoprint "Hello world"
...
Expand All @@ -62,6 +66,8 @@ You do not have sufficient access to the device. You probably want to add the a
...
```

{{< mdl-enable "<!-- markdownlint-enable MD013 -->" >}}

## Testing experimental features

To install a test branch, by user `ghuser` for the branch `branchname`, run
Expand All @@ -76,17 +82,20 @@ To revert back to the release version, run
pipx install --force dymoprint
```

To install a particular release version, specify `dymoprint==x.y.z` instead of `dymoprint` in the above command.
To install a particular release version, specify `dymoprint==x.y.z` instead of
`dymoprint` in the above command.

## Development and code style

To install for development, fork and clone this repository, and run (ideally within a venv):
To install for development, fork and clone this repository, and run (ideally
within a venv):

```bash
pip install --editable .
```

This project uses [pre-commit](https://pre-commit.com/) to run some checks before committing.
This project uses [pre-commit](https://pre-commit.com/) to run some checks
before committing.
After installing the `pre-commit` executable, please run

```bash
Expand Down Expand Up @@ -146,16 +155,17 @@ Any picture with JPEG standard may be printed. Beware it will be downsized to ta

```dymoprint -p mypic.jpg ""```

Take care of the trailing "" - you may enter text here which gets printed in front of the image
Take care of the trailing "" - you may enter text here which gets printed in
front of the image

## GUI

### Run DymoPrint GUI

```dymoprint_gui```

### GUI App Features

### Features
* Live preview
* margin settings
* type size selector
Expand All @@ -175,8 +185,8 @@ Take care of the trailing "" - you may enter text here which gets printed in fro
* path to file

Nodes can be freely arranged, simply drag&drop rows on the list.
To add or delete the node from the label - right-click on the list and select the action from the context menu.
To print - click the print button.
To add or delete the node from the label - right-click on the list and select
the action from the context menu. To print - click the print button.

### Example

Expand All @@ -192,11 +202,10 @@ Example 3: barcode, text, image

![alt](doc/DymoPrint_example_3.png)



## Development

Besides the travis-ci one should run the following command on a feature implemention or change to ensure the same outcome on a real device:
Besides the travis-ci one should run the following command on a feature
implemention or change to ensure the same outcome on a real device:

```bash
dymoprint Tst && \
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies = [
"python-barcode>=0.13.1,<1",
"pyusb",
"PyQt6",
"darkdetect",
]
classifiers = [
"Operating System :: POSIX :: Linux",
Expand Down Expand Up @@ -80,6 +81,7 @@ commands =
dymoprint --version
dymoprint --help
dymoprint --preview "single line"
dymoprint --preview-inverted "single line"
dymoprint --preview multiple lines
dymoprint --preview -qr "qr text"
dymoprint --preview -c code128 "bc txt"
Expand Down Expand Up @@ -138,6 +140,9 @@ drop = [
line-length = 88
extend-exclude = ["_vendor"]
src = ["src"]
target-version = "py38"

[tool.ruff.lint]
select = [
"D", # pydocstyle
"E", # pycodestyle errors
Expand Down Expand Up @@ -182,10 +187,8 @@ ignore = [
"ISC001", # single-line-implicit-string-concatenation
"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 scripts/gui_dev.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash

while true; do
VERBOSE=$VERBOSE \
dymoprint_gui;
DYMOPRINT_VERBOSE=$DYMOPRINT_VERBOSE \
dymoprint_gui -v;
sleep 1
done
90 changes: 50 additions & 40 deletions src/dymoprint/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
e_qrcode,
)
from dymoprint.lib.dymo_labeler import DymoLabeler
from dymoprint.lib.font_config import FontConfig, FontStyle, NoFontFound
from dymoprint.lib.logger import configure_logging, set_verbose
from dymoprint.lib.font_config import NoFontFound, get_available_fonts, get_font_path
from dymoprint.lib.logger import configure_logging, is_verbose_env_vars, set_not_verbose
from dymoprint.lib.render_engines import (
BarcodeRenderEngine,
BarcodeWithTextRenderEngine,
HorizontallyCombinedRenderEngine,
PictureRenderEngine,
PrintPayloadRenderEngine,
PrintPreviewRenderEngine,
QrRenderEngine,
RenderContext,
TestPatternRenderEngine,
Expand All @@ -40,10 +42,10 @@
LOG = logging.getLogger(__name__)

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


Expand Down Expand Up @@ -209,22 +211,21 @@ def mm_to_payload_px(mm, margin):
def run():
args = parse_args()

if (not args.verbose) and (not is_verbose_env_vars()):
# Neither --verbose flag nor the environment variable is set.
set_not_verbose()

# read config file
style = FLAG_TO_STYLE.get(args.style)
try:
font_config = FontConfig(font=args.font, style=style)
font_path = get_font_path(font=args.font, style=style)
except NoFontFound as e:
valid_font_names = [f.stem for f in FontConfig.available_fonts()]
valid_font_names = [f.stem for f in get_available_fonts()]
msg = f"{e}. Valid fonts are: {', '.join(valid_font_names)}"
raise CommandLineUsageError(msg) from None

font_filename = font_config.path

labeltext = args.text

if args.verbose:
set_verbose()

# check if barcode, qrcode or text should be printed, use frames only on text
if args.qr and not USE_QR:
raise CommandLineUsageError(
Expand Down Expand Up @@ -260,15 +261,15 @@ def run():
elif args.barcode_text:
render_engines.append(
BarcodeWithTextRenderEngine(
labeltext.pop(0), args.barcode_text, font_filename, args.frame_width_px
labeltext.pop(0), args.barcode_text, font_path, args.frame_width_px
)
)

if labeltext:
render_engines.append(
TextRenderEngine(
text_lines=labeltext,
font_file_name=font_filename,
font_file_name=font_path,
frame_width_px=args.frame_width_px,
font_size_ratio=int(args.scale) / 100.0,
align=args.align,
Expand All @@ -285,50 +286,59 @@ def run():
min_label_mm_len = args.min_length
max_label_mm_len = args.max_length

margin = args.margin_px
min_payload_len_px = mm_to_payload_px(min_label_mm_len, margin)
margin_px = args.margin_px
min_payload_len_px = mm_to_payload_px(min_label_mm_len, margin_px)
max_payload_len_px = (
mm_to_payload_px(max_label_mm_len, margin)
mm_to_payload_px(max_label_mm_len, margin_px)
if max_label_mm_len is not None
else None
)

render = HorizontallyCombinedRenderEngine(
render_engines,
min_payload_len_px=min_payload_len_px,
max_payload_len_px=max_payload_len_px,
justify=args.justify,
)

dymo_labeler = DymoLabeler(
margin_px=args.margin_px,
tape_size_mm=args.tape_size_mm,
dymo_labeler = DymoLabeler(tape_size_mm=args.tape_size_mm)
render_engine = HorizontallyCombinedRenderEngine(render_engines)
render_context = RenderContext(
background_color="white",
foreground_color="black",
height_px=dymo_labeler.height_px,
preview_show_margins=False,
)
render_context = RenderContext(height_px=dymo_labeler.height_px)
label_bitmap = render.render(render_context)

# print or show the label
if args.preview or args.preview_inverted or args.imagemagick or args.browser:
LOG.debug("Demo mode: showing label..")
# fix size, adding print borders
label_image = Image.new(
"1", (margin + label_bitmap.width + margin, label_bitmap.height)
render = PrintPreviewRenderEngine(
render_engine=render_engine,
justify=args.justify,
visible_horizontal_margin_px=margin_px,
labeler_margin_px=dymo_labeler.labeler_margin_px,
max_width_px=max_payload_len_px,
min_width_px=min_payload_len_px,
)
label_image.paste(label_bitmap, (margin, 0))
bitmap = render.render(render_context)
LOG.debug("Demo mode: showing label..")
if args.preview or args.preview_inverted:
label_rotated = label_bitmap.transpose(Image.ROTATE_270)
label_rotated = bitmap.transpose(Image.ROTATE_270)
print(image_to_unicode(label_rotated, invert=args.preview_inverted))
if args.imagemagick:
ImageOps.invert(label_image).show()
ImageOps.invert(bitmap).show()
if args.browser:
with NamedTemporaryFile(suffix=".png", delete=False) as fp:
ImageOps.invert(label_image).save(fp)
inverted = ImageOps.invert(bitmap.convert("RGB"))
ImageOps.invert(inverted).save(fp)
webbrowser.open(f"file://{fp.name}")
else:
dymo_labeler.print(label_bitmap)
render = PrintPayloadRenderEngine(
render_engine=render_engine,
justify=args.justify,
visible_horizontal_margin_px=margin_px,
labeler_margin_px=dymo_labeler.labeler_margin_px,
max_width_px=max_payload_len_px,
min_width_px=min_payload_len_px,
)
bitmap, _ = render.render(render_context)
dymo_labeler.print(bitmap)


def main():
configure_logging()
with system_run():
configure_logging()
run()
8 changes: 3 additions & 5 deletions src/dymoprint/gui/common.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import logging
import traceback

from PyQt6.QtWidgets import (
QMessageBox,
)
from PyQt6.QtWidgets import QMessageBox, QWidget

from dymoprint.lib.logger import VERBOSE_NOTICE, is_verbose, print_exception

LOG = logging.getLogger(__name__)


def crash_msg_box(parent, title, err):
def crash_msg_box(parent: QWidget, title: str, err: Exception):
print_exception(err)
text = f"{err}\n\n{traceback.format_exc() if is_verbose() else VERBOSE_NOTICE}"
text = f"{err!r}\n\n{traceback.format_exc() if is_verbose() else VERBOSE_NOTICE}"
QMessageBox.warning(parent, title, text)
Loading