Improvements

This commit is contained in:
Ircama 2024-08-13 18:01:12 +02:00
parent b9048b5bcd
commit 58dbd5cfd8
6 changed files with 136 additions and 72 deletions

View file

@ -23,6 +23,7 @@ jobs:
python -m pip install --upgrade pip
#pip install git+https://github.com/pyinstaller/pyinstaller@develop
pip install pyinstaller
pip install Pillow
pip install -r requirements.txt
- name: Run PyInstaller to create epson_print_conf.exe
@ -30,9 +31,9 @@ jobs:
python -m PyInstaller epson_print_conf.spec -- --default
- name: Zip the epson_print_conf.exe asset to epson_print_conf.zip
run: >
powershell -Command Compress-Archive dist/epson_print_conf.exe
dist/epson_print_conf.zip
run: |
Compress-Archive dist/epson_print_conf.exe dist/epson_print_conf.zip
shell: pwsh
- name: Generate Changelog
run: >

View file

@ -212,27 +212,21 @@ pyinstaller epson_print_conf.spec -- --default
Then run the executable file created in the *dist/* folder, which has the same options of `ui.py`.
An alternative way to create the executable file from *ui.py* without using *epson_print_conf.spec* is the following:
```bash
pyinstaller --onefile ui.py --name epson_print_conf --hidden-import babel.numbers --windowed
```
A file named *gui.py* is also included (similar to *ui.py*), which automatically loads a previously created configuration file that has to be named *printer_conf.pickle*, merging it with the program configuration. (See below the *parse_devices.py* utility.) To build the executable program with this file instead of the default *ui.py*, run the following command:
It is also possible to automatically load a previously created configuration file that has to be named *epson_print_conf.pickle*, merging it with the program configuration. (See below the *parse_devices.py* utility.) To build the executable program with this file, run the following command:
```bash
pip install pyinstaller # if not yet installed
curl -o devices.xml https://codeberg.org/attachments/147f41a3-a6ea-45f6-8c2a-25bac4495a1d
python3 parse_devices.py -a 192.168.178.29 -s XP-205 -p printer_conf.pickle # use your default IP address and printer model as default settings for the GUI
python3 parse_devices.py -a 192.168.178.29 -s XP-205 -p epson_print_conf.pickle # use your default IP address and printer model as default settings for the GUI
pyinstaller epson_print_conf.spec
```
When the build operation is completed, you can run the executable program created in the *dist/* folder. It does not have options, embeds the *printer_conf.pickle* file and starts with the default IP address and printer model defined in the build phase.
This repository includes a Windows *epson_print_conf.exe* executable file which is automatically generated by a [GitHub Action](.github/workflows/build.yml). It is packaged in a ZIP file named *epson_print_conf.zip* and uploaded into the [Releases](https://github.com/Ircama/epson_print_conf/releases/latest) folder.
When embedding *epson_print_conf.pickle*, the created program does not have options and starts with the default IP address and printer model defined in the build phase.
As mentioned in the [documentation](https://pyinstaller.org/en/stable/), PyInstaller supports Windows, MacOS X, Linux and other UNIX Operating Systems. It creates an executable file which is only compatible with the operating system that is used to build the asset.
This repository includes a Windows *epson_print_conf.exe* executable file which is automatically generated by a [GitHub Action](.github/workflows/build.yml). It is packaged in a ZIP file named *epson_print_conf.zip* and uploaded into the [Releases](https://github.com/Ircama/epson_print_conf/releases/latest) folder.
## Utilities and notes
### parse_devices.py
@ -291,6 +285,8 @@ Generate printer configuration from devices.xml
The output is better formatted when also installing [black](https://pypi.org/project/black/).
The program does not provide *printer_head_id* and *Power off timer*.
### find_printers.py
*find_printers.py* can be executed via `python find_printers.py` and prints the list of the discovered printers to the standard output. It is internally used as a library by *ui.py*.

View file

@ -719,29 +719,13 @@ class EpsonPrinter:
else:
destination[key] = value
return destination
# process "alias" definintion
if conf_dict:
self.expand_printer_conf(conf_dict)
if conf_dict and replace_conf:
self.PRINTER_CONFIG = conf_dict
for printer_name, printer_data in self.PRINTER_CONFIG.copy().items():
if "alias" in printer_data:
aliases = printer_data["alias"]
del printer_data["alias"]
if not isinstance(aliases, list):
logging.error(
"Alias '%s' of printer '%s' in configuration "
"must be a list.",
aliases, printer_name
)
continue
for alias_name in aliases:
if alias_name in self.PRINTER_CONFIG:
logging.error(
"Alias '%s' of printer '%s' is already defined "
"in configuration.",
alias_name, printer_name
)
else:
self.PRINTER_CONFIG[alias_name] = printer_data
else:
self.expand_printer_conf(self.PRINTER_CONFIG)
if conf_dict and not replace_conf:
self.PRINTER_CONFIG = merge(self.PRINTER_CONFIG, conf_dict)
for key, values in self.PRINTER_CONFIG.items():
@ -752,22 +736,6 @@ class EpsonPrinter:
]
if not values['alias']:
del values['alias']
# process "same-as" definintion
for printer_name, printer_data in self.PRINTER_CONFIG.copy().items():
if "same-as" in printer_data:
sameas = printer_data["same-as"]
#del printer_data["same-as"]
if sameas in self.PRINTER_CONFIG:
self.PRINTER_CONFIG[printer_name] = {
**self.PRINTER_CONFIG[sameas],
**printer_data
}
else:
logging.error(
"Undefined 'same-as' printer '%s' "
"in '%s' configuration.",
sameas, printer_name
)
self.model = model
self.hostname = hostname
self.port = port
@ -793,6 +761,48 @@ class EpsonPrinter:
"""Return list of available information methods about a printer."""
return(filter(lambda x: x.startswith("get_"), dir(self)))
def expand_printer_conf(self, conf):
"""
Expand "alias" and "same-as" of a printer database for all printers
"""
# process "alias" definintion
for printer_name, printer_data in conf.copy().items():
if "alias" in printer_data:
aliases = printer_data["alias"]
del printer_data["alias"]
if not isinstance(aliases, list):
logging.error(
"Alias '%s' of printer '%s' in configuration "
"must be a list.",
aliases, printer_name
)
continue
for alias_name in aliases:
if alias_name in conf:
logging.error(
"Alias '%s' of printer '%s' is already defined "
"in configuration.",
alias_name, printer_name
)
else:
conf[alias_name] = printer_data
# process "same-as" definintion
for printer_name, printer_data in conf.copy().items():
if "same-as" in printer_data:
sameas = printer_data["same-as"]
#del printer_data["same-as"]
if sameas in conf:
conf[printer_name] = {
**conf[sameas],
**printer_data
}
else:
logging.error(
"Undefined 'same-as' printer '%s' "
"in '%s' configuration.",
sameas, printer_name
)
def stats(self):
"""Return all available information about a printer."""
stat_set = {}

View file

@ -1,17 +1,70 @@
# -*- mode: python ; coding: utf-8 -*-
import argparse
import os
import os.path
from PIL import Image, ImageDraw, ImageFont
def create_image(png_file, text):
img = Image.new('RGB', (800, 150), color='black')
fnt = ImageFont.truetype('arialbd.ttf', 30)
d = ImageDraw.Draw(img)
shadow_offset = 2
bbox = d.textbbox((0, 0), text, font=fnt)
x, y = (800-bbox[2])/2, (150-bbox[3])/2
d.text((x+shadow_offset, y+shadow_offset), text, font=fnt, fill='gray')
d.text((x, y), text, font=fnt, fill='#baf8f8')
img.save(png_file, 'PNG')
parser = argparse.ArgumentParser()
parser.add_argument("--default", action="store_true")
options = parser.parse_args()
PROGRAM = [ 'gui.py' ]
DATAS = [('printer_conf.pickle', '.')]
BASENAME = 'epson_print_conf'
DATAS = [(BASENAME + '.pickle', '.')]
SPLASH_IMAGE = BASENAME + '.png'
create_image(
SPLASH_IMAGE, 'Epson Printer Configuration tool loading...'
)
if not options.default and not os.path.isfile(DATAS[0][0]):
print("\nMissing file", DATAS[0][0], "without using the default option.")
quit()
gui_wrapper = """import pyi_splash
import pickle
from ui import EpsonPrinterUI
from os import path
path_to_pickle = path.abspath(
path.join(path.dirname(__file__), '""" + DATAS[0][0] + """')
)
with open(path_to_pickle, 'rb') as fp:
conf_dict = pickle.load(fp)
app = EpsonPrinterUI(conf_dict=conf_dict, replace_conf=False)
pyi_splash.close()
app.mainloop()
"""
if options.default:
PROGRAM = [ 'ui.py' ]
DATAS = []
gui_wrapper = """import pyi_splash
import pickle
from ui import main
from os import path
app = main()
pyi_splash.close()
app.mainloop()
"""
with open(PROGRAM[0], 'w') as file:
file.write(gui_wrapper)
a = Analysis(
PROGRAM,
@ -28,27 +81,39 @@ a = Analysis(
)
pyz = PYZ(a.pure)
splash = Splash(
SPLASH_IMAGE,
binaries=a.binaries,
datas=a.datas,
text_pos=None,
text_size=12,
minify_script=True,
always_on_top=True,
)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
splash,
splash.binaries,
[],
name='epson_print_conf',
name=BASENAME,
debug=False, # Setting to True gives you progress messages from the executable (for console=False there will be annoying MessageBoxes on Windows).
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
# console=False, # On Windows or Mac OS governs whether to use the console executable or the windowed executable. Always True on Linux/Unix (always console executable - it does not matter there).
console=options.default, # On Windows or Mac OS governs whether to use the console executable or the windowed executable. Always True on Linux/Unix (always console executable - it does not matter there).
disable_windowed_traceback=False, # Disable traceback dump of unhandled exception in windowed (noconsole) mode (Windows and macOS only)
# hide_console='hide-early', # Windows only. In console-enabled executable, hide or minimize the console window ('hide-early', 'minimize-early', 'hide-late', 'minimize-late')
hide_console='hide-early', # Windows only. In console-enabled executable, hide or minimize the console window ('hide-early', 'minimize-early', 'hide-late', 'minimize-late')
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
os.remove(SPLASH_IMAGE)
os.remove(PROGRAM[0])

11
gui.py
View file

@ -1,11 +0,0 @@
import pickle
from ui import EpsonPrinterUI
from os import path
PICKLE_CONF_FILE = "printer_conf.pickle"
path_to_pickle = path.abspath(path.join(path.dirname(__file__), PICKLE_CONF_FILE))
with open(path_to_pickle, 'rb') as fp:
conf_dict = pickle.load(fp)
app = EpsonPrinterUI(conf_dict=conf_dict, replace_conf=False)
app.mainloop()

9
ui.py
View file

@ -942,7 +942,7 @@ class EpsonPrinterUI(tk.Tk):
self.clipboard_append(item_text)
if __name__ == "__main__":
def main():
import argparse
import pickle
@ -972,9 +972,12 @@ if __name__ == "__main__":
if args.pickle:
conf_dict = pickle.load(args.pickle[0])
app = EpsonPrinterUI(conf_dict=conf_dict, replace_conf=args.override)
return EpsonPrinterUI(conf_dict=conf_dict, replace_conf=args.override)
if __name__ == "__main__":
try:
app.mainloop()
main().mainloop()
except:
print("\nInterrupted.")
sys.exit(0)