tinyprint 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tinyprint-0.2.0/tinyprint.egg-info → tinyprint-0.2.1}/PKG-INFO +5 -3
- {tinyprint-0.2.0 → tinyprint-0.2.1}/pyproject.toml +5 -3
- tinyprint-0.2.1/tests/test_integration.py +72 -0
- tinyprint-0.2.1/tests/test_jobconfig.py +157 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint/__init__.py +1 -1
- tinyprint-0.2.1/tinyprint/__main__.py +21 -0
- tinyprint-0.2.1/tinyprint/gui.py +248 -0
- tinyprint-0.2.1/tinyprint/gui_misc.py +160 -0
- tinyprint-0.2.1/tinyprint/job.py +151 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint/jobconfig.py +1 -1
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint/preprocessor.py +66 -22
- {tinyprint-0.2.0 → tinyprint-0.2.1/tinyprint.egg-info}/PKG-INFO +5 -3
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint.egg-info/SOURCES.txt +3 -2
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint.egg-info/requires.txt +3 -1
- tinyprint-0.2.0/tests/test_jobconfig.py +0 -12
- tinyprint-0.2.0/tinyprint/__main__.py +0 -263
- tinyprint-0.2.0/tinyprint/job.py +0 -321
- tinyprint-0.2.0/tinyprint/patches.py +0 -28
- tinyprint-0.2.0/tinyprint/xescpos.py +0 -166
- {tinyprint-0.2.0 → tinyprint-0.2.1}/LICENSE +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/README.md +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/setup.cfg +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tests/test_job.py +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint/config.py +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint.egg-info/dependency_links.txt +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint.egg-info/entry_points.txt +0 -0
- {tinyprint-0.2.0 → tinyprint-0.2.1}/tinyprint.egg-info/top_level.txt +0 -0
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tinyprint
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: ui & cli to print zines with esc/pos printer
|
|
5
|
-
Author-email: Levin Eric Zimmermann <levin.eric.zimmermann@posteo.
|
|
5
|
+
Author-email: Levin Eric Zimmermann <levin.eric.zimmermann@posteo.com>
|
|
6
6
|
License: GPL
|
|
7
7
|
Requires-Python: >=3.10
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE
|
|
10
|
-
Requires-Dist: python-escpos>=3.1
|
|
10
|
+
Requires-Dist: python-escpos[serial,usb]>=3.1
|
|
11
11
|
Requires-Dist: dataclasses-json>=0.6.0
|
|
12
12
|
Requires-Dist: marko>=2.1.2
|
|
13
13
|
Requires-Dist: pdf2image>=1.17.0
|
|
14
14
|
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: coverage; extra == "dev"
|
|
15
16
|
Requires-Dist: pytest; extra == "dev"
|
|
17
|
+
Requires-Dist: syrupy; extra == "dev"
|
|
16
18
|
Requires-Dist: mypy; extra == "dev"
|
|
17
19
|
Requires-Dist: types-six; extra == "dev"
|
|
18
20
|
Requires-Dist: types-pillow; extra == "dev"
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tinyprint"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
authors = [
|
|
5
|
-
{name = "Levin Eric Zimmermann", email = "levin.eric.zimmermann@posteo.
|
|
5
|
+
{name = "Levin Eric Zimmermann", email = "levin.eric.zimmermann@posteo.com"},
|
|
6
6
|
]
|
|
7
7
|
description = "ui & cli to print zines with esc/pos printer"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
dependencies = [
|
|
11
|
-
'python-escpos>=3.1',
|
|
11
|
+
'python-escpos[serial,usb]>=3.1',
|
|
12
12
|
'dataclasses-json>=0.6.0',
|
|
13
13
|
'marko>=2.1.2',
|
|
14
14
|
'pdf2image>=1.17.0',
|
|
@@ -16,7 +16,9 @@ dependencies = [
|
|
|
16
16
|
|
|
17
17
|
[project.optional-dependencies]
|
|
18
18
|
dev = [
|
|
19
|
+
"coverage",
|
|
19
20
|
"pytest",
|
|
21
|
+
"syrupy",
|
|
20
22
|
"mypy",
|
|
21
23
|
"types-six",
|
|
22
24
|
"types-pillow",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Integration tests for the tinyprint package.
|
|
2
|
+
|
|
3
|
+
This module contains end-to-end tests that verify the printing functionality
|
|
4
|
+
with different types of resources and job configurations using a dummy printer.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from itertools import product
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from tinyprint.job import Job
|
|
13
|
+
from tinyprint.jobconfig import JobConfig, Resource, Orientation
|
|
14
|
+
|
|
15
|
+
# Test data directory
|
|
16
|
+
TEST_DATA = Path(__file__).parent / "data"
|
|
17
|
+
|
|
18
|
+
# Test configurations
|
|
19
|
+
RESOURCES = {
|
|
20
|
+
"text": ["text/test.md"],
|
|
21
|
+
"image": [f"images/test-{i}.png" for i in range(4)],
|
|
22
|
+
"pdf": ["pdfs/test.pdf"],
|
|
23
|
+
}
|
|
24
|
+
ORIENTATIONS = list(Orientation)
|
|
25
|
+
COPY_COUNTS = [1, 2]
|
|
26
|
+
CUT_OPTIONS = [True, False]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def generate_test_cases():
|
|
30
|
+
"""Generate test cases for all combinations of resource types and config options."""
|
|
31
|
+
test_cases = []
|
|
32
|
+
# Define all possible combinations
|
|
33
|
+
combinations = product(RESOURCES.items(), ORIENTATIONS, COPY_COUNTS, CUT_OPTIONS)
|
|
34
|
+
for (rname, rpath_list), orient, copy_count, cut in combinations:
|
|
35
|
+
test_id = f"{rname}-{orient.name.lower()}-copies_{copy_count}-{'cut' if cut else 'nocut'}"
|
|
36
|
+
test_cases.append(
|
|
37
|
+
pytest.param(
|
|
38
|
+
rpath_list,
|
|
39
|
+
{"orientation": orient, "copy_count": copy_count, "cut": cut},
|
|
40
|
+
id=test_id,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
return test_cases
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.mark.parametrize("resource_path_list,config_kwargs", generate_test_cases())
|
|
47
|
+
def test_print(snapshot, resource_path_list, config_kwargs):
|
|
48
|
+
"""
|
|
49
|
+
Test printing with various resource types and job configurations.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
snapshot: The snapshot fixture for assertion
|
|
53
|
+
resource_path_list: List of relative path to the resource
|
|
54
|
+
files (from TEST_DATA)
|
|
55
|
+
config_kwargs: Configuration options for JobConfig
|
|
56
|
+
"""
|
|
57
|
+
config = JobConfig(
|
|
58
|
+
resource_list=[Resource(str(TEST_DATA / p)) for p in resource_path_list],
|
|
59
|
+
**config_kwargs,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
job = Job(config)
|
|
63
|
+
|
|
64
|
+
# Set a default width to prevent
|
|
65
|
+
# 'AssertionError: max_width of printer unknown!'
|
|
66
|
+
profile = job.manager.printer.profile
|
|
67
|
+
profile.profile_data["media"]["width"]["pixels"] = 384
|
|
68
|
+
|
|
69
|
+
job.start()
|
|
70
|
+
job.wait()
|
|
71
|
+
|
|
72
|
+
assert snapshot == job.manager.printer.output
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from tinyprint.jobconfig import (
|
|
5
|
+
JobConfig,
|
|
6
|
+
PageSize,
|
|
7
|
+
Resource,
|
|
8
|
+
Orientation,
|
|
9
|
+
_parse_str,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_orientation_enum():
|
|
14
|
+
"""Test Orientation enum values and string parsing."""
|
|
15
|
+
# Test enum values
|
|
16
|
+
assert Orientation.HORIZONTAL.value == 0
|
|
17
|
+
assert Orientation.VERTICAL.value == 1
|
|
18
|
+
|
|
19
|
+
# Test string parsing
|
|
20
|
+
assert Orientation.parse("horizontal") == Orientation.HORIZONTAL
|
|
21
|
+
assert Orientation.parse("HORIZONTAL") == Orientation.HORIZONTAL
|
|
22
|
+
assert Orientation.parse("vertical") == Orientation.VERTICAL
|
|
23
|
+
assert Orientation.parse("VERTICAL") == Orientation.VERTICAL
|
|
24
|
+
|
|
25
|
+
# Test invalid enum value
|
|
26
|
+
with pytest.raises(AttributeError):
|
|
27
|
+
Orientation.parse("invalid")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_parse_str_utility():
|
|
31
|
+
"""Test the _parse_str utility function."""
|
|
32
|
+
|
|
33
|
+
class TestEnum:
|
|
34
|
+
FOO = 1
|
|
35
|
+
BAR = 2
|
|
36
|
+
|
|
37
|
+
assert _parse_str(TestEnum, "foo") == TestEnum.FOO
|
|
38
|
+
assert _parse_str(TestEnum, "BAR") == TestEnum.BAR
|
|
39
|
+
|
|
40
|
+
with pytest.raises(AttributeError):
|
|
41
|
+
_parse_str(TestEnum, "baz")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_page_size_creation():
|
|
45
|
+
"""Test PageSize creation and from_any method."""
|
|
46
|
+
# Test direct creation
|
|
47
|
+
size1 = PageSize(10.5, 20.5)
|
|
48
|
+
assert size1.width == 10.5
|
|
49
|
+
assert size1.heigth == 20.5
|
|
50
|
+
|
|
51
|
+
# Test from_any with PageSize
|
|
52
|
+
size2 = PageSize.from_any(size1)
|
|
53
|
+
assert size2 == size1
|
|
54
|
+
|
|
55
|
+
# Test from_any with tuple
|
|
56
|
+
size3 = PageSize.from_any((30.0, 40.0))
|
|
57
|
+
assert size3.width == 30.0
|
|
58
|
+
assert size3.heigth == 40.0
|
|
59
|
+
|
|
60
|
+
# Test from_any with list
|
|
61
|
+
size4 = PageSize.from_any([50.0, 60.0])
|
|
62
|
+
assert size4.width == 50.0
|
|
63
|
+
assert size4.heigth == 60.0
|
|
64
|
+
|
|
65
|
+
# Test invalid type
|
|
66
|
+
with pytest.raises(NotImplementedError):
|
|
67
|
+
PageSize.from_any("invalid")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_resource_creation():
|
|
71
|
+
"""Test Resource creation and default values."""
|
|
72
|
+
# Test with required fields only
|
|
73
|
+
res1 = Resource("path/to/file.pdf")
|
|
74
|
+
assert res1.path == "path/to/file.pdf"
|
|
75
|
+
assert res1.fragment_height is None
|
|
76
|
+
assert res1.color == 0
|
|
77
|
+
|
|
78
|
+
# Test with all fields
|
|
79
|
+
res2 = Resource("path/to/other.pdf", fragment_height=100, color=1)
|
|
80
|
+
assert res2.path == "path/to/other.pdf"
|
|
81
|
+
assert res2.fragment_height == 100
|
|
82
|
+
assert res2.color == 1
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_job_config_creation():
|
|
86
|
+
"""Test JobConfig creation and default values."""
|
|
87
|
+
resources = [
|
|
88
|
+
Resource("file1.pdf"),
|
|
89
|
+
Resource("file2.pdf", fragment_height=200, color=1),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# Test with required fields only
|
|
93
|
+
c1 = JobConfig(resource_list=resources)
|
|
94
|
+
assert c1.resource_list == resources
|
|
95
|
+
assert c1.printer_config is None
|
|
96
|
+
assert c1.copy_count == 1
|
|
97
|
+
assert c1.cut is True
|
|
98
|
+
assert c1.orientation == Orientation.HORIZONTAL
|
|
99
|
+
assert c1.page_size is None
|
|
100
|
+
assert c1.sleep_time is None
|
|
101
|
+
|
|
102
|
+
# Test with all fields
|
|
103
|
+
page_size = PageSize(70.0, 30.0)
|
|
104
|
+
c2 = JobConfig(
|
|
105
|
+
resource_list=resources,
|
|
106
|
+
printer_config="myprinter.yml",
|
|
107
|
+
copy_count=3,
|
|
108
|
+
cut=False,
|
|
109
|
+
orientation=Orientation.VERTICAL,
|
|
110
|
+
page_size=page_size,
|
|
111
|
+
sleep_time=1.5,
|
|
112
|
+
)
|
|
113
|
+
assert c2.printer_config == "myprinter.yml"
|
|
114
|
+
assert c2.copy_count == 3
|
|
115
|
+
assert c2.cut is False
|
|
116
|
+
assert c2.orientation == Orientation.VERTICAL
|
|
117
|
+
assert c2.page_size == page_size
|
|
118
|
+
assert c2.sleep_time == 1.5
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_job_config_serialization(tmp_path: Path):
|
|
122
|
+
"""Test JobConfig serialization to/from JSON file."""
|
|
123
|
+
# Create test resources
|
|
124
|
+
resources = [
|
|
125
|
+
Resource("file1.pdf"),
|
|
126
|
+
Resource("file2.pdf", fragment_height=200, color=1),
|
|
127
|
+
]
|
|
128
|
+
page_size = PageSize(70.0, 30.0)
|
|
129
|
+
|
|
130
|
+
# Create and save config
|
|
131
|
+
config = JobConfig(
|
|
132
|
+
resource_list=resources,
|
|
133
|
+
printer_config="myprinter.yml",
|
|
134
|
+
copy_count=2,
|
|
135
|
+
cut=True,
|
|
136
|
+
orientation=Orientation.VERTICAL,
|
|
137
|
+
page_size=page_size,
|
|
138
|
+
sleep_time=0.5,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Test serialization/deserialization
|
|
142
|
+
json_path = tmp_path / "config.json"
|
|
143
|
+
config.to_file(str(json_path))
|
|
144
|
+
loaded_config = JobConfig.from_file(str(json_path))
|
|
145
|
+
|
|
146
|
+
# Verify all fields are preserved
|
|
147
|
+
assert loaded_config.resource_list[0].path == "file1.pdf"
|
|
148
|
+
assert loaded_config.resource_list[1].fragment_height == 200
|
|
149
|
+
assert loaded_config.resource_list[1].color == 1
|
|
150
|
+
assert loaded_config.printer_config == "myprinter.yml"
|
|
151
|
+
assert loaded_config.copy_count == 2
|
|
152
|
+
assert loaded_config.cut is True
|
|
153
|
+
assert loaded_config.orientation == Orientation.VERTICAL
|
|
154
|
+
assert loaded_config.page_size is not None
|
|
155
|
+
assert loaded_config.page_size.width == 70.0
|
|
156
|
+
assert loaded_config.page_size.heigth == 30.0
|
|
157
|
+
assert loaded_config.sleep_time == 0.5
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Main entry point for the TinyPrint application."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from tinyprint.gui import GUI
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
"""Entry point for the application."""
|
|
9
|
+
# Configure logging
|
|
10
|
+
logging.basicConfig(
|
|
11
|
+
level=logging.INFO,
|
|
12
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Start the application
|
|
16
|
+
with GUI() as gui:
|
|
17
|
+
gui.run()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
main()
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""TinyPrint GUI"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import logging
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from tkinter import Tk, filedialog
|
|
12
|
+
from tkinter.ttk import Frame
|
|
13
|
+
|
|
14
|
+
from tinyprint.jobconfig import JobConfig, Resource
|
|
15
|
+
from tinyprint.job import Job
|
|
16
|
+
from tinyprint.gui_misc import Block, Blocks, no_hidden_files
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GUI:
|
|
20
|
+
"""Main GUI class for the TinyPrint application."""
|
|
21
|
+
|
|
22
|
+
COLUMN_COUNT = 5
|
|
23
|
+
|
|
24
|
+
def __enter__(self):
|
|
25
|
+
self._tempdir = None
|
|
26
|
+
self._tempdir_context = tempfile.TemporaryDirectory(prefix="tinyprint")
|
|
27
|
+
self.tempdir = self._tempdir_context.name
|
|
28
|
+
self.setup()
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
32
|
+
self.cleanup()
|
|
33
|
+
|
|
34
|
+
def setup(self):
|
|
35
|
+
"""Set up the main window and all components."""
|
|
36
|
+
self.root = Tk()
|
|
37
|
+
no_hidden_files(self.root)
|
|
38
|
+
self.root.title("~ tiny print ~")
|
|
39
|
+
self.main_frame = Frame(self.root, padding=15)
|
|
40
|
+
self.main_frame.grid(sticky="nsew")
|
|
41
|
+
blk = self.blk = Blocks(self)
|
|
42
|
+
blk.ADD.job_controller(JobController)
|
|
43
|
+
blk.ADD.job_config_manager(JobConfigManager)
|
|
44
|
+
blk.ADD.job_config_editor(JobConfigEditor)
|
|
45
|
+
for i in range(GUI.COLUMN_COUNT):
|
|
46
|
+
self.main_frame.columnconfigure(i, weight=1)
|
|
47
|
+
|
|
48
|
+
def run(self):
|
|
49
|
+
"""Start the main event loop."""
|
|
50
|
+
self.root.mainloop()
|
|
51
|
+
|
|
52
|
+
def cleanup(self):
|
|
53
|
+
"""Clean up resources, including temporary directories."""
|
|
54
|
+
g = self
|
|
55
|
+
g.blk and g.blk.close()
|
|
56
|
+
g._tempdir_context and g._tempdir_context.cleanup()
|
|
57
|
+
g.blk, g.root, g.main_frame, g._tempdir_context, g._tempdir = [None] * 5
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class JobController(Block):
|
|
61
|
+
"""Controls to start/stop print job."""
|
|
62
|
+
|
|
63
|
+
def __init__(self, *args, **kwargs):
|
|
64
|
+
self.job = None
|
|
65
|
+
super().__init__(*args, **kwargs)
|
|
66
|
+
|
|
67
|
+
def create_widgets(self):
|
|
68
|
+
btn = self.btn
|
|
69
|
+
for i, cmd in enumerate("print stop".split()):
|
|
70
|
+
b = btn.ADD(cmd, text=cmd, command=getattr(self, cmd))
|
|
71
|
+
b.grid(column=i, row=0)
|
|
72
|
+
|
|
73
|
+
def print(self):
|
|
74
|
+
if self.job and self.job.is_running:
|
|
75
|
+
return logging.warning("job already printing!")
|
|
76
|
+
self.stop()
|
|
77
|
+
config = self.gui.blk.job_config_manager.job_config
|
|
78
|
+
if config:
|
|
79
|
+
self.job = Job(config)
|
|
80
|
+
self.job.start()
|
|
81
|
+
else:
|
|
82
|
+
logging.warning("no job config set")
|
|
83
|
+
|
|
84
|
+
def stop(self):
|
|
85
|
+
if not self.job:
|
|
86
|
+
return
|
|
87
|
+
self.job.stop()
|
|
88
|
+
self.job.close()
|
|
89
|
+
self.job = None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class JobConfigManager(Block):
|
|
93
|
+
"""Load/save a job configuration"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, gui, *args, **kwargs):
|
|
96
|
+
self.job_config = None
|
|
97
|
+
self.filename = None
|
|
98
|
+
super().__init__(gui, *args, **kwargs)
|
|
99
|
+
self.temp_resource = os.path.join(gui.tempdir, "tmp-resource.md")
|
|
100
|
+
with open(self.temp_resource, "w") as f:
|
|
101
|
+
f.write("tiny printer test")
|
|
102
|
+
self.new(os.path.join(self.gui.tempdir, "tmp-print-job.json"))
|
|
103
|
+
|
|
104
|
+
def create_widgets(self):
|
|
105
|
+
var, btn, lbl = self.var, self.btn, self.lbl
|
|
106
|
+
var.str.ADD.filename_indicator()
|
|
107
|
+
lbl.ADD.job_config(text="job config: ")
|
|
108
|
+
lbl.job_config.grid(column=0, row=0)
|
|
109
|
+
lbl.ADD.filename(textvariable=var.filename_indicator)
|
|
110
|
+
lbl.filename.grid(column=1, row=0, columnspan=3, pady=5, sticky="w")
|
|
111
|
+
for i, cmd in enumerate("new open save save_as reload".split()):
|
|
112
|
+
b = btn.ADD(cmd, text=cmd, command=getattr(self, cmd))
|
|
113
|
+
b.grid(column=i, row=1, pady=5, padx=2)
|
|
114
|
+
|
|
115
|
+
def new(self, *args, **kwargs):
|
|
116
|
+
self.job_config = JobConfig([Resource(self.temp_resource)])
|
|
117
|
+
self.save_as(*args, **kwargs)
|
|
118
|
+
|
|
119
|
+
def open(self):
|
|
120
|
+
if filename := self.select_file(
|
|
121
|
+
(("print job configuration", "*.json"), ("All files", "*.*"))
|
|
122
|
+
):
|
|
123
|
+
self.set_filename(filename)
|
|
124
|
+
self.reload()
|
|
125
|
+
|
|
126
|
+
def save(self):
|
|
127
|
+
self.job_config.to_file(self.filename)
|
|
128
|
+
|
|
129
|
+
def save_as(self, filename: Optional[str] = None):
|
|
130
|
+
if not filename:
|
|
131
|
+
filetypes = (("print job configuration", "*.json"), ("All files", "*.*"))
|
|
132
|
+
filename = filedialog.asksaveasfilename(
|
|
133
|
+
title="File path", filetypes=filetypes, initialdir=str(Path.home())
|
|
134
|
+
)
|
|
135
|
+
if not filename:
|
|
136
|
+
return
|
|
137
|
+
if not filename.endswith(".json"):
|
|
138
|
+
filename = f"{filename}.json"
|
|
139
|
+
self.set_filename(filename)
|
|
140
|
+
self.save()
|
|
141
|
+
|
|
142
|
+
def reload(self):
|
|
143
|
+
if not self.filename:
|
|
144
|
+
return logging.warning("can't load as no file loaded yet")
|
|
145
|
+
logging.info(f"load {self.filename}")
|
|
146
|
+
self.job_config = JobConfig.from_file(self.filename)
|
|
147
|
+
self.gui.blk.job_config_editor.sync_from_model()
|
|
148
|
+
|
|
149
|
+
def set_filename(self, filename: str):
|
|
150
|
+
self.filename = filename
|
|
151
|
+
self.var.filename_indicator.set(Path(filename).name)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class JobConfigEditor(Block):
|
|
155
|
+
"""Edit parameters of currently loaded job configuration"""
|
|
156
|
+
|
|
157
|
+
def __init__(self, *args, **kwargs):
|
|
158
|
+
self.temp_resource = None
|
|
159
|
+
super().__init__(*args, **kwargs)
|
|
160
|
+
|
|
161
|
+
def create_widgets(self):
|
|
162
|
+
var, btn, lbl, cbx, scl = self.var, self.btn, self.lbl, self.cbx, self.scl
|
|
163
|
+
|
|
164
|
+
var.str.ADD.resource_list()
|
|
165
|
+
var.str.ADD.printer_config()
|
|
166
|
+
var.double.ADD.sleep_time()
|
|
167
|
+
|
|
168
|
+
for i, t in enumerate( # render names of parameters
|
|
169
|
+
("resource", "printer configuration", "cut", "sleep time")
|
|
170
|
+
):
|
|
171
|
+
label = lbl.ADD(f"arg{i}", text=f"{t}: ")
|
|
172
|
+
label.grid(column=0, row=i, columnspan=2, sticky="w", pady=2)
|
|
173
|
+
|
|
174
|
+
btn.ADD.select_resource(text="select", command=self.select_resource)
|
|
175
|
+
lbl.ADD.resource_list(textvariable=var.resource_list)
|
|
176
|
+
btn.select_resource.grid(column=2, row=0, sticky="w")
|
|
177
|
+
lbl.resource_list.grid(column=4, row=0, columnspan=2, sticky="w")
|
|
178
|
+
|
|
179
|
+
btn.ADD.select_printer(text="select", command=self.select_printer)
|
|
180
|
+
lbl.ADD.printer_value(textvariable=var.printer_config)
|
|
181
|
+
btn.select_printer.grid(column=2, row=1, sticky="w")
|
|
182
|
+
lbl.printer_value.grid(column=4, row=1, sticky="w")
|
|
183
|
+
|
|
184
|
+
cbx.ADD.cut(values=["yes", "no"])
|
|
185
|
+
cbx.cut.set("no")
|
|
186
|
+
cbx.cut.bind("<<ComboboxSelected>>", self.select_cut)
|
|
187
|
+
cbx.cut.grid(column=2, row=2)
|
|
188
|
+
|
|
189
|
+
lbl.ADD.sleep_value(textvariable=var.sleep_time)
|
|
190
|
+
scl.ADD.sleep_time(
|
|
191
|
+
from_=0,
|
|
192
|
+
to=2,
|
|
193
|
+
command=self.select_sleep_time,
|
|
194
|
+
variable=var.sleep_time,
|
|
195
|
+
length=500,
|
|
196
|
+
)
|
|
197
|
+
lbl.sleep_value.grid(column=2, row=3, sticky="w")
|
|
198
|
+
scl.sleep_time.grid(column=3, row=3, columnspan=2)
|
|
199
|
+
|
|
200
|
+
def sync_from_model(self):
|
|
201
|
+
job_config, var, cbx = self.job_config, self.var, self.cbx
|
|
202
|
+
if not job_config:
|
|
203
|
+
return
|
|
204
|
+
resource_list = "; ".join(
|
|
205
|
+
[r.path.split(os.sep)[-1] for r in job_config.resource_list]
|
|
206
|
+
)
|
|
207
|
+
if len(resource_list) > 25:
|
|
208
|
+
resource_list = resource_list[:30] + " ..."
|
|
209
|
+
var.resource_list.set(resource_list)
|
|
210
|
+
var.printer_config.set(job_config.printer_config)
|
|
211
|
+
var.sleep_time.set(job_config.sleep_time)
|
|
212
|
+
cbx.cut.set(("no", "yes")[job_config.cut])
|
|
213
|
+
|
|
214
|
+
def select_resource(self):
|
|
215
|
+
self.select_file(
|
|
216
|
+
"resource_list",
|
|
217
|
+
(("all files", "*.*"), ("pdf", "*.pdf"), ("images", "*.jpg")),
|
|
218
|
+
multiple=True,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def select_printer(self):
|
|
222
|
+
self.select_file(
|
|
223
|
+
"printer_config", (("printer config", "*.yml"), ("all files", "*.*"))
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def select_file(self, attr, filetypes, multiple=False):
|
|
227
|
+
if file_list := super().select_file(filetypes, multiple):
|
|
228
|
+
setattr(
|
|
229
|
+
self.job_config,
|
|
230
|
+
attr,
|
|
231
|
+
[Resource(f) for f in file_list] if multiple else file_list[0],
|
|
232
|
+
)
|
|
233
|
+
self.sync_from_model()
|
|
234
|
+
|
|
235
|
+
def select_cut(self, *_):
|
|
236
|
+
self.job_config.cut = {"yes": True, "no": False}[self.cbx.cut.get()]
|
|
237
|
+
|
|
238
|
+
def select_sleep_time(self, value: str):
|
|
239
|
+
v = round(float(value), 2)
|
|
240
|
+
self.job_config.sleep_time = v
|
|
241
|
+
# NOTE Since var.sleep_time is also 'variable' of scale, this
|
|
242
|
+
# already gets set automatically - yet we still re-set it here
|
|
243
|
+
# to get the rounded value.
|
|
244
|
+
self.var.sleep_time.set(v)
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def job_config(self):
|
|
248
|
+
return self.gui.blk.job_config_manager.job_config
|