Files
stepanalyser/.venv/lib/python3.12/site-packages/ezdxf/commands.py
Christian Anetzberger a197de9456 initial
2026-01-22 20:23:51 +01:00

1063 lines
32 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (c) 2021-2024, Manfred Moitzi
# License: MIT License
from __future__ import annotations
import pathlib
import tempfile
from typing import Callable, Optional, TYPE_CHECKING, Type, Sequence
import abc
import sys
import os
import glob
import signal
import time
import logging
from pathlib import Path
# --------------------------------------------------------------------------------------
# Only imports from the core package here - no add-ons!
#
# The command `ezdxf -V` must always work!
#
# Imports depending on additional packages like Pillow, Matplotlib, PySide6, ...
# have to be local imports, see 'draw' command as an example.
# --------------------------------------------------------------------------------------
import ezdxf
from ezdxf import recover
from ezdxf.lldxf import const
from ezdxf.lldxf.validator import is_dxf_file, is_binary_dxf_file, dxf_info
from ezdxf.dwginfo import dwg_file_info
if TYPE_CHECKING:
from ezdxf.entities import DXFGraphic
from ezdxf.addons.drawing.properties import Properties, LayerProperties
__all__ = ["get", "add_parsers"]
logger = logging.getLogger("ezdxf")
def get(cmd: str) -> Optional[Callable]:
cls = _commands.get(cmd)
if cls:
return cls.run
return None
def add_parsers(subparsers) -> None:
for cmd in _commands.values(): # in order of registration
try:
cmd.add_parser(subparsers)
except ImportError:
logger.info(f"ImportError - '{cmd.NAME}' command not available")
def is_dxf_r12_file(filename: str) -> bool:
try:
with open(filename, "rt", errors="ignore") as fp:
info = dxf_info(fp)
except IOError:
return False
return info.version <= const.DXF12
class Command:
"""abstract base class for launcher commands"""
NAME = "command"
@staticmethod
@abc.abstractmethod
def add_parser(subparsers) -> None:
pass
@staticmethod
@abc.abstractmethod
def run(args) -> None:
pass
_commands: dict[str, Type[Command]] = dict()
def register(cls: Type[Command]):
"""Register a launcher sub-command."""
_commands[cls.NAME] = cls
return cls
@register
class Audit(Command):
"""Launcher sub-command: audit"""
NAME = "audit"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(Audit.NAME, help="audit and repair DXF files")
parser.add_argument(
"files",
metavar="FILE",
nargs="+",
help="audit DXF files",
)
parser.add_argument(
"-s",
"--save",
action="store_true",
help='save recovered files with extension ".rec.dxf" ',
)
parser.add_argument(
"-x",
"--explore",
action="store_true",
help="filters invalid DXF tags, this may load corrupted files but "
"data loss is very likely",
)
@staticmethod
def run(args):
def build_outname(name: str) -> str:
p = Path(name)
return str(p.parent / (p.stem + ".rec.dxf"))
def log_fixes(auditor):
for error in auditor.fixes:
logger.info("fixed:" + error.message)
def log_errors(auditor):
for error in auditor.errors:
logger.error(error.message)
def _audit(filename: str) -> None:
msg = f"auditing file: {filename}"
print(msg)
logger.info(msg)
if args.explore:
logger.info("explore mode - skipping invalid tags")
loader = recover.explore if args.explore else recover.readfile
try:
doc, auditor = loader(filename)
except IOError:
msg = "Not a DXF file or a generic I/O error."
print(msg)
logger.error(msg)
return # keep on processing additional files
except const.DXFStructureError as e:
msg = f"Invalid or corrupted DXF file: {str(e)}"
print(msg)
logger.error(msg)
return # keep on processing additional files
if auditor.has_errors:
auditor.print_error_report()
log_errors(auditor)
if auditor.has_fixes:
auditor.print_fixed_errors()
log_fixes(auditor)
if auditor.has_errors is False and auditor.has_fixes is False:
print("No errors found.")
else:
print(
f"Found {len(auditor.errors)} errors, "
f"applied {len(auditor.fixes)} fixes"
)
if args.save:
outname = build_outname(filename)
try:
doc.saveas(outname)
except IOError as e:
print(f"Can not save recovered file '{outname}':\n{str(e)}")
else:
print(f"Saved recovered file as: '{outname}'")
for pattern in args.files:
names = list(glob.glob(pattern))
if len(names) == 0:
msg = f"File(s) '{pattern}' not found."
print(msg)
logger.error(msg)
continue
for filename in names:
if not os.path.exists(filename):
msg = f"File '{filename}' not found."
print(msg)
logger.error(msg)
continue
if not is_dxf_file(filename):
msg = f"File '{filename}' is not a DXF file."
print(msg)
logger.error(msg)
continue
_audit(filename)
def load_document(filename: str):
try:
doc, auditor = recover.readfile(filename)
except IOError:
msg = f'Not a DXF file or a generic I/O error: "{filename}"'
print(msg, file=sys.stderr)
sys.exit(2)
except const.DXFStructureError:
msg = f'Invalid or corrupted DXF file: "{filename}"'
print(msg, file=sys.stderr)
sys.exit(3)
if auditor.has_errors:
# But is most likely good enough for rendering.
msg = f"Audit process found {len(auditor.errors)} unrecoverable error(s)."
print(msg)
logger.error(msg)
if auditor.has_fixes:
msg = f"Audit process fixed {len(auditor.fixes)} error(s)."
print(msg)
logger.info(msg)
return doc, auditor
HELP_LTYPE = (
"select the line type rendering method, default is approximate. "
"Approximate uses the closest approximation available to the "
"backend, the accurate method renders as accurately as possible "
"but this approach is slower."
)
HELP_LWSCALE = (
"set custom line weight scaling, default is 0 to disable line " "weights at all"
)
@register
class Draw(Command):
"""Launcher sub-command: draw"""
NAME = "draw"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(
Draw.NAME, help="draw and save DXF as a bitmap or vector image"
)
parser.add_argument(
"file",
metavar="FILE",
nargs="?",
help="DXF file to view or convert",
)
parser.add_argument(
"--backend",
default="matplotlib",
choices=["matplotlib", "qt", "mupdf", "custom_svg"],
help="choose the backend to use for rendering",
)
parser.add_argument(
"--formats",
action="store_true",
help="show all supported export formats and exit",
)
parser.add_argument(
"-l",
"--layout",
default="Model",
help='select the layout to draw, default is "Model"',
)
parser.add_argument(
"--background",
default="DEFAULT",
choices=[
"DEFAULT",
"WHITE",
"BLACK",
"PAPERSPACE",
"MODELSPACE",
"OFF",
"CUSTOM",
],
help="choose the background color to use",
)
parser.add_argument(
"--all-layers-visible",
action="store_true",
help="draw all layers including the ones marked as invisible",
)
parser.add_argument(
"--all-entities-visible",
action="store_true",
help="draw all entities including the ones marked as invisible "
"(some entities are individually marked as invisible even "
"if the layer is visible)",
)
parser.add_argument(
"-o",
"--out",
required=False,
type=pathlib.Path,
help="output filename for export",
)
parser.add_argument(
"--dpi",
type=int,
default=300,
help="target render resolution, default is 300",
)
parser.add_argument(
"-f",
"--force",
action="store_true",
help="overwrite the destination if it already exists",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="give more output",
)
@staticmethod
def run(args):
try:
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.config import Configuration, BackgroundPolicy
from ezdxf.addons.drawing.file_output import (
open_file,
MatplotlibFileOutput,
PyQtFileOutput,
SvgFileOutput,
MuPDFFileOutput,
)
except ImportError as e:
print(str(e))
sys.exit(1)
if args.backend == "matplotlib":
try:
file_output = MatplotlibFileOutput(args.dpi)
except ImportError as e:
print(str(e))
sys.exit(1)
elif args.backend == "qt":
try:
file_output = PyQtFileOutput(args.dpi)
except ImportError as e:
print(str(e))
sys.exit(1)
elif args.backend == "mupdf":
try:
file_output = MuPDFFileOutput(args.dpi)
except ImportError as e:
print(str(e))
sys.exit(1)
elif args.backend == "custom_svg":
# has no additional dependencies
file_output = SvgFileOutput(args.dpi)
else:
raise ValueError(args.backend)
verbose = args.verbose
if verbose:
logging.basicConfig(level=logging.INFO)
if args.formats:
print(f"formats supported by {args.backend}:")
for extension, description in file_output.supported_formats():
print(f" {extension}: {description}")
sys.exit(0)
if args.file:
filename = args.file
else:
print("argument FILE is required")
sys.exit(1)
print(f'loading file "{filename}"...')
doc, _ = load_document(filename)
try:
layout = doc.layouts.get(args.layout)
except KeyError:
print(
f'Could not find layout "{args.layout}". '
f"Valid layouts: {[l.name for l in doc.layouts]}"
)
sys.exit(1)
ctx = RenderContext(doc)
config = Configuration().with_changes(
background_policy=BackgroundPolicy[args.background]
)
out = file_output.backend()
if args.all_layers_visible:
def override_layer_properties(layer_properties: Sequence[LayerProperties]) -> None:
for properties in layer_properties:
properties.is_visible = True
ctx.set_layer_properties_override(override_layer_properties)
frontend = Frontend(ctx, out, config=config)
if args.all_entities_visible:
def override_entity_properties(entity: DXFGraphic, properties: Properties) -> None:
properties.is_visible = True
frontend.push_property_override_function(override_entity_properties)
t0 = time.perf_counter()
if verbose:
print(f"drawing layout '{layout.name}' ...")
frontend.draw_layout(layout, finalize=True)
t1 = time.perf_counter()
if verbose:
print(f"took {t1-t0:.4f} seconds")
if args.out is not None:
if pathlib.Path(args.out).suffix not in {
f".{ext}" for ext, _ in file_output.supported_formats()
}:
print(
f'the format of the output path "{args.out}" '
f"is not supported by the backend {args.backend}"
)
sys.exit(1)
if args.out.exists() and not args.force:
print(f'the destination "{args.out}" already exists. Not writing')
sys.exit(1)
else:
print(f'exporting to "{args.out}"...')
t0 = time.perf_counter()
file_output.save(args.out)
t1 = time.perf_counter()
if verbose:
print(f"took {t1 - t0:.4f} seconds")
else:
print(f"exporting to temporary file...")
output_dir = pathlib.Path(tempfile.mkdtemp(prefix="ezdxf_draw"))
output_path = output_dir / f"output.{file_output.default_format()}"
file_output.save(output_path)
print(f'saved to "{output_path}"')
if verbose:
print("opening viewer...")
open_file(output_path)
@register
class View(Command):
"""Launcher sub-command: view"""
NAME = "view"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(
View.NAME, help="view DXF files by the PyQt viewer"
)
parser.add_argument(
"file",
metavar="FILE",
nargs="?",
help="DXF file to view",
)
parser.add_argument(
"-l",
"--layout",
default="Model",
help='select the layout to draw, default is "Model"',
)
# disable lineweight at all by default:
parser.add_argument(
"--lwscale",
type=float,
default=0,
help=HELP_LWSCALE,
)
@staticmethod
def run(args):
# Import on demand for a quicker startup:
try:
from ezdxf.addons.xqt import QtWidgets
except ImportError as e:
print(str(e))
sys.exit(1)
from ezdxf.addons.drawing.qtviewer import CADViewer
from ezdxf.addons.drawing.config import Configuration
config = Configuration(
lineweight_scaling=args.lwscale,
)
signal.signal(signal.SIGINT, signal.SIG_DFL) # handle Ctrl+C properly
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
set_app_icon(app)
viewer = CADViewer.from_config(config)
filename = args.file
if filename:
doc, auditor = load_document(filename)
viewer.set_document(
doc,
auditor,
layout=args.layout,
)
sys.exit(app.exec())
@register
class Browse(Command):
"""Launcher sub-command: browse"""
NAME = "browse"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(Browse.NAME, help="browse DXF file structure")
parser.add_argument(
"file",
metavar="FILE",
nargs="?",
help="DXF file to browse",
)
parser.add_argument(
"-l", "--line", type=int, required=False, help="go to line number"
)
parser.add_argument(
"-g",
"--handle",
required=False,
help="go to entity by HANDLE, HANDLE has to be a hex value without "
"any prefix like 'fefe'",
)
@staticmethod
def run(args):
try:
from ezdxf.addons.xqt import QtWidgets
except ImportError as e:
print(str(e))
sys.exit(1)
from ezdxf.addons import browser
signal.signal(signal.SIGINT, signal.SIG_DFL) # handle Ctrl+C properly
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
set_app_icon(app)
main_window = browser.DXFStructureBrowser(
args.file,
line=args.line,
handle=args.handle,
resource_path=resources_path(),
)
main_window.show()
sys.exit(app.exec())
@register
class BrowseAcisData(Command):
"""Launcher sub-command: browse-acis"""
NAME = "browse-acis"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(
BrowseAcisData.NAME, help="browse ACIS structures in DXF files"
)
parser.add_argument(
"file",
metavar="FILE",
nargs="?",
help="DXF file to browse",
)
parser.add_argument(
"-g",
"--handle",
required=False,
help="go to entity by HANDLE, HANDLE has to be a hex value without "
"any prefix like 'fefe'",
)
@staticmethod
def run(args):
try:
from ezdxf.addons.xqt import QtWidgets
except ImportError as e:
print(str(e))
sys.exit(1)
from ezdxf.addons.acisbrowser.browser import AcisStructureBrowser
signal.signal(signal.SIGINT, signal.SIG_DFL) # handle Ctrl+C properly
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
set_app_icon(app)
main_window = AcisStructureBrowser(
args.file,
handle=args.handle,
)
main_window.show()
sys.exit(app.exec())
@register
class Strip(Command):
"""Launcher sub-command: strip"""
NAME = "strip"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(Strip.NAME, help="strip comments from DXF files")
parser.add_argument(
"file",
metavar="FILE",
nargs="+",
help='DXF file to process, wildcards "*" and "?" are supported',
)
parser.add_argument(
"-b",
"--backup",
action="store_true",
required=False,
help='make a backup copy with extension ".bak" from the '
"DXF file, overwrites existing backup files",
)
parser.add_argument(
"-t",
"--thumbnail",
action="store_true",
required=False,
help="strip THUMBNAILIMAGE section",
)
parser.add_argument(
"--handles",
action="store_true",
required=False,
help="remove handles from DXF R12 or older files",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
required=False,
help="give more output",
)
@staticmethod
def run(args):
from ezdxf.tools.strip import strip
for pattern in args.file:
for filename in glob.glob(pattern):
codes = [999]
if args.handles:
if is_dxf_r12_file(filename):
codes.extend([5, 105])
else:
print(
f"Cannot remove handles from DXF R13 or later: {filename}"
)
strip(
filename,
backup=args.backup,
thumbnail=args.thumbnail,
verbose=args.verbose,
codes=codes,
)
@register
class Config(Command):
"""Launcher sub-command: config"""
NAME = "config"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(Config.NAME, help="manage config files")
parser.add_argument(
"-p",
"--print",
action="store_true",
help="print configuration",
)
parser.add_argument(
"-w",
"--write",
metavar="FILE",
help="write configuration",
)
parser.add_argument(
"--home",
action="store_true",
help="create config file 'ezdxf.ini' in the user home directory "
"'~/.config/ezdxf', $XDG_CONFIG_HOME is supported if set",
)
parser.add_argument(
"--reset",
action="store_true",
help="factory reset, delete default config files 'ezdxf.ini'",
)
@staticmethod
def run(args):
from ezdxf import options
action = False
if args.reset:
options.reset()
options.delete_default_config_files()
action = True
if args.home:
options.write_home_config()
action = True
if args.write:
action = True
filepath = Path(args.write).expanduser()
try:
options.write_file(str(filepath))
print(f"configuration written to: {filepath}")
except IOError as e:
print(str(e))
if args.print or action is False:
options.print()
def load_every_document(filename: str):
def io_error() -> str:
msg = f'Not a DXF file or a generic I/O error: "{filename}"'
print(msg, file=sys.stderr)
return msg
def structure_error() -> str:
msg = f'Invalid or corrupted DXF file: "{filename}"'
print(msg, file=sys.stderr)
return msg
binary_fmt = False
if is_binary_dxf_file(filename):
try:
doc = ezdxf.readfile(filename)
except IOError:
raise const.DXFLoadError(io_error())
except const.DXFStructureError:
raise const.DXFLoadError(structure_error())
auditor = doc.audit()
binary_fmt = True
else:
try:
doc, auditor = recover.readfile(filename)
except IOError:
raise const.DXFLoadError(io_error())
except const.DXFStructureError:
dwginfo = dwg_file_info(filename)
if dwginfo.version != "invalid":
print(
f"This is a DWG file!!!\n"
f'Filename: "{filename}"\n'
f"Format: DWG\n"
f"Release: {dwginfo.release}\n"
f"DWG Version: {dwginfo.version}\n"
)
raise const.DXFLoadError()
raise const.DXFLoadError(structure_error())
return doc, auditor, binary_fmt
@register
class Info(Command):
"""Launcher sub-command: info"""
NAME = "info"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(
Info.NAME,
help="show information and optional stats of DXF files as "
"loaded by ezdxf, this may not represent the original "
"content of the file, use the browse command to "
"see the original content",
)
parser.add_argument(
"file",
metavar="FILE",
nargs="+",
help='DXF file to process, wildcards "*" and "?" are supported',
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
required=False,
help="give more output",
)
parser.add_argument(
"-s",
"--stats",
action="store_true",
required=False,
help="show content stats",
)
@staticmethod
def run(args):
from ezdxf.document import info
def process(fn: str):
try:
doc, auditor, binary_fmt = load_every_document(fn)
except const.DXFLoadError:
pass
else:
fmt = "Binary" if binary_fmt else "ASCII"
print(
"\n".join(
info(
doc,
verbose=args.verbose,
content=args.stats,
fmt=fmt,
)
)
)
if auditor.has_fixes:
print(f"Audit process fixed {len(auditor.fixes)} error(s).")
if auditor.has_errors:
print(
f"Audit process found {len(auditor.errors)} unrecoverable error(s)."
)
print()
for pattern in args.file:
file_count = 0
for filename in glob.glob(pattern):
if os.path.isdir(filename):
dir_pattern = os.path.join(filename, "*.dxf")
for filename2 in glob.glob(dir_pattern):
process(filename2)
file_count += 1
else:
process(filename)
file_count += 1
if file_count == 0:
sys.stderr.write(f'No matching files for pattern: "{pattern}"\n')
@register
class HPGL(Command):
"""Launcher sub-command: hpgl"""
NAME = "hpgl"
@staticmethod
def add_parser(subparsers):
parser = subparsers.add_parser(
HPGL.NAME, help=f"view and/or convert HPGL/2 plot files to various formats"
)
parser.add_argument(
"file",
metavar="FILE",
nargs="?",
default="",
help=f"view and/or convert HPGL/2 plot files, wildcards (*, ?) supported in command line mode",
)
parser.add_argument(
"-e",
"--export",
metavar="FORMAT",
required=False,
help=f"convert HPGL/2 plot file to SVG, PDF or DXF from the command line (no gui)",
)
parser.add_argument(
"-r",
"--rotate",
type=int,
choices=(0, 90, 180, 270),
default=0,
required=False,
help="rotate page about 90, 180 or 270 degrees (no gui)",
)
parser.add_argument(
"-x",
"--mirror_x",
action="store_true",
required=False,
help="mirror page in x-axis direction, (no gui)",
)
parser.add_argument(
"-y",
"--mirror_y",
action="store_true",
required=False,
help="mirror page in y-axis direction, (no gui)",
)
parser.add_argument(
"-m",
"--merge_control",
type=int,
required=False,
default=2,
choices=(0, 1, 2),
help="provides control over the order of filled polygons, 0=off (print order), "
"1=luminance (order by luminance), 2=auto (default)",
)
parser.add_argument(
"-f",
"--force",
action="store_true",
required=False,
help="inserts the mandatory 'enter HPGL/2 mode' escape sequence into the data "
"stream; use this flag when no HPGL/2 data was found and you are sure the "
"file is a HPGL/2 plot file",
)
parser.add_argument(
"--aci",
action="store_true",
required=False,
help="use pen numbers as ACI colors and assign colors by layer (DXF only)",
)
parser.epilog = (
"Note that plot files are intended to be plotted on white paper."
)
parser.add_argument(
"--dpi",
type=int,
required=False,
default=96,
help="pixel density in dots per inch (PNG only)",
)
parser.epilog = (
"Note that plot files are intended to be plotted on white paper."
)
@staticmethod
def run(args):
if args.export:
if os.path.exists(args.file):
filenames = [args.file]
else:
filenames = glob.glob(args.file)
for filename in filenames:
export_hpgl2(Path(filename), args)
else:
launch_hpgl2_viewer(args.file, args.force)
def export_hpgl2(filepath: Path, args) -> None:
from ezdxf.addons.hpgl2 import api as hpgl2
from ezdxf.addons.drawing.dxf import ColorMode
fmt = args.export.upper()
start_msg = f"converting HPGL/2 plot file '{filepath.name}' to {fmt}"
try:
data = filepath.read_bytes()
except IOError as e:
print(str(e), file=sys.stderr)
return
if args.force:
data = b"%1B" + data
export_path = filepath.with_suffix(f".{fmt.lower()}")
if fmt == "DXF":
print(start_msg)
color_mode = ColorMode.ACI if args.aci else ColorMode.RGB
doc = hpgl2.to_dxf(
data,
rotation=args.rotate,
mirror_x=args.mirror_x,
mirror_y=args.mirror_y,
color_mode=color_mode,
merge_control=args.merge_control,
)
try:
doc.saveas(export_path)
except IOError as e:
print(str(e), file=sys.stderr)
elif fmt == "SVG":
print(start_msg)
svg_string = hpgl2.to_svg(
data,
rotation=args.rotate,
mirror_x=args.mirror_x,
mirror_y=args.mirror_y,
merge_control=args.merge_control,
)
try:
export_path.write_text(svg_string)
except IOError as e:
print(str(e), file=sys.stderr)
elif fmt == "PDF":
print(start_msg)
pdf_bytes = hpgl2.to_pdf(
data,
rotation=args.rotate,
mirror_x=args.mirror_x,
mirror_y=args.mirror_y,
merge_control=args.merge_control,
)
try:
export_path.write_bytes(pdf_bytes)
except IOError as e:
print(str(e), file=sys.stderr)
elif fmt == "PNG":
print(start_msg)
png_bytes = hpgl2.to_pixmap(
data,
rotation=args.rotate,
mirror_x=args.mirror_x,
mirror_y=args.mirror_y,
merge_control=args.merge_control,
fmt="png",
dpi=args.dpi,
)
try:
export_path.write_bytes(png_bytes)
except IOError as e:
print(str(e), file=sys.stderr)
else:
print(f"invalid export format: {fmt}")
exit(1)
print(f"file '{export_path.name}' successfully written")
def launch_hpgl2_viewer(filename: str, force: bool) -> None:
try:
from ezdxf.addons.xqt import QtWidgets
except ImportError as e:
print(str(e))
exit(1)
from ezdxf.addons.hpgl2.viewer import HPGL2Viewer
signal.signal(signal.SIGINT, signal.SIG_DFL) # handle Ctrl+C properly
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
set_app_icon(app)
viewer = HPGL2Viewer()
viewer.show()
if filename and os.path.exists(filename):
viewer.load_plot_file(filename, force=force)
sys.exit(app.exec())
def set_app_icon(app):
from ezdxf.addons.xqt import QtGui, QtCore
app_icon = QtGui.QIcon()
p = resources_path()
app_icon.addFile(str(p / "16x16.png"), QtCore.QSize(16, 16))
app_icon.addFile(str(p / "24x24.png"), QtCore.QSize(24, 24))
app_icon.addFile(str(p / "32x32.png"), QtCore.QSize(32, 32))
app_icon.addFile(str(p / "48x48.png"), QtCore.QSize(48, 48))
app_icon.addFile(str(p / "64x64.png"), QtCore.QSize(64, 64))
app_icon.addFile(str(p / "256x256.png"), QtCore.QSize(256, 256))
app.setWindowIcon(app_icon)
def resources_path():
from pathlib import Path
return Path(__file__).parent / "resources"