Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Ircama committed Aug 13, 2024
1 parent b9048b5 commit 58dbd5c
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 72 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ 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
run: |
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: >
Expand Down
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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*.
Expand Down
84 changes: 47 additions & 37 deletions epson_print_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,29 +719,13 @@ def merge(source, destination):
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():
Expand All @@ -752,22 +736,6 @@ def merge(source, destination):
]
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
Expand All @@ -793,6 +761,48 @@ def list_methods(self):
"""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 = {}
Expand Down
77 changes: 71 additions & 6 deletions epson_print_conf.spec
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 changes: 0 additions & 11 deletions gui.py

This file was deleted.

11 changes: 7 additions & 4 deletions ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ def copy_selected_item(self):
self.clipboard_append(item_text)


if __name__ == "__main__":
def main():
import argparse
import pickle

Expand Down Expand Up @@ -972,9 +972,12 @@ def copy_selected_item(self):
if args.pickle:
conf_dict = pickle.load(args.pickle[0])

app = EpsonPrinterUI(conf_dict=conf_dict, replace_conf=args.override)
try:
app.mainloop()
return EpsonPrinterUI(conf_dict=conf_dict, replace_conf=args.override)


if __name__ == "__main__":
try:
main().mainloop()
except:
print("\nInterrupted.")
sys.exit(0)

0 comments on commit 58dbd5c

Please sign in to comment.