diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cd4232f..aa28a6f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ ci: for more information, see https://pre-commit.ci autofix_prs: false autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate' - autoupdate_schedule: weekly + autoupdate_schedule: monthly skip: [] submodules: false repos: @@ -16,21 +16,12 @@ repos: args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: check-yaml - exclude: devtools/conda.recipe/meta.yaml # doesn't play nice with jinja -# - id: no-commit-to-branch # only makes sense for local pre-commit hooks -- repo: https://github.com/psf/black - rev: 23.11.0 - hooks: - - id: black -- repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort -- repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 - hooks: - - id: flake8 -- repo: https://github.com/PyCQA/pydocstyle - rev: 6.3.0 - hooks: - - id: pydocstyle + +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.7 + hooks: + # Run the linter. + - id: ruff + # Run the formatter. + - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index 700c95d..95a4063 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,56 @@ +[project] +name = "weldx_widgets" +dynamic = [ # see: https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata + "version", # version gets derived from git by setuptools_scm. +] +authors = [ + {name="Martin K. Scherer", email="martin.scherer@bam.de"}, + {name="Cagtay Fabry", email="cagtay.fabry@bam.de"} +] +description="Python based widgets for the weldx core package" +readme = "README.md" +license = {file = "LICENSE", name="BSD License"} +keywords = [ + "weldx", + "ipywidgets", + "widgets", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Natural Language :: English", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Scientific/Engineering :: Physics", +] + +# Dependencies +requires-python = ">=3.9" +dependencies = [ + "weldx >=0.6", + "ipywidgets", + "k3d >=2.12", + "ipympl", + "bidict", + "ipyfilechooser", + "tqdm", +] +[project.optional-dependencies] +test = [ + "pytest-cov", + "pytest-xdist" +] + +[project.urls] +documentation = "https://weldx.readthedocs.io" +repository = "https://github.com/BAMweldx/weldx-widgets" +bug_tracker = "https://github.com/BAMweldx/weldx-widgets/issues" +changelog = "https://github.com/BAMweldx/weldx-widgets/blob/master/CHANGELOG.md" + [build-system] requires = ["setuptools>=45", "wheel", @@ -5,5 +58,94 @@ requires = ["setuptools>=45", "babel", ] +# Tool configs [tool.setuptools_scm] write_to = "weldx_widgets/_version.py" +write_to_template = '__version__ = "{version}"' + +[tool.setuptools.packages.find] +where = ["."] + +[tool.pytest.ini_options] +addopts = "--tb=short --color=yes -rsw --doctest-modules --cov=weldx_widgets" +testpaths = "weldx_widgets/tests" +filterwarnings = [ + "ignore::DeprecationWarning:traittypes.*:", + "ignore:Passing method to :FutureWarning:xarray.*:", + "error::pint.UnitStrippedWarning", +] + +[tool.coverage.run] +source = ["weldx_widgets"] + +[tool.coverage.report] +omit = [ + "weldx_widgets/_version.py", + "weldx_widgets/tests/*", +] +exclude_lines = [ +# Have to re-enable the standard pragma + "pragma: no cover", +] + +[tool.ruff] +target-version = "py38" # designated Python version +exclude = [ + "__init__.py", + "doc/src/conf.py", +] +# TODO: should be the following list, but Ruff does not yet impl all of them. +# W503,W504 +# E203 +# C408 +ignore = [ + "C408", + #"E203", + "E402", + #"W503", + #"W504", + "D203", "D211", "D213" +] +line-length = 88 +select = [ + "B", # flake8-bugbear + "C", # flake8-comprehensions + #"D", # note: all relevant D's will be set by setting pydocstyle.convention=numpy! + "E", # pycodestyles + "F", # pyflakes + "W", # pycodestyle warnings + "UP", # pyupgrade + "T2", # flake8-print + "I001", # isort + "ICN", # import conventions, e.g. import numpy as np + #"B950", # not yet implemented by Ruff. + "RUF100", # ensure 'noqa' declarations are still valid. +] + +# Allow pydocstyle violations in certain areas. +per-file-ignores."**/{tests,tags,asdf,devtools}/**" = ["D"] +per-file-ignores."conftest.py" = ["D"] +per-file-ignores."doc/src/tutorials/*" = ["D"] +per-file-ignores."doc/src/conf.py" = ["E501", # ignore long lines. + "RUF100", # do no check if 'noqa' is needed (circular import workaround) +] +# Allow prints in certain areas. +per-file-ignores."**/{cli,tests,tutorials,devtools}/**/*{.py,ipynb}" = ["T2"] + +external = ["B950"] +pydocstyle = {convention = "numpy"} +pyupgrade = {keep-runtime-typing = true} + +mccabe = {max-complexity = 15} # max branches inside a function. + +[tool.ruff.isort] +known-first-party = ["weldx"] +required-imports = ["from __future__ import annotations"] + +[tool.ruff.flake8-import-conventions] +extend-aliases = {xarray = "xr"} + +[tool.nbqa.addopts] +ruff = [ + "--extend-ignore=B018" +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 4a4ac63..0000000 --- a/setup.cfg +++ /dev/null @@ -1,87 +0,0 @@ -[metadata] -name = weldx_widgets -author = "Martin K. Scherer , Cagtay Fabry " -author_email = "martin.scherer@bam.de" -home_page = https://www.bam.de/weldx -description = Python based widgets for the weldx core package -long_description = file: README.md -long_description_content_type = text/markdown -license = BSD License -license_file = LICENSE -platform = any -keywords = - welding - weldx - bam -classifiers = - Development Status :: 3 - Alpha - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Operating System :: OS Independent - Natural Language :: English - Programming Language :: Python - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Topic :: Scientific/Engineering :: Physics -project_urls = - Documentation = https://weldx.readthedocs.io - Source Code = https://github.com/BAMweldx/weldx-widgets - Bug Tracker = https://github.com/BAMweldx/weldx-widgets/issues -# Changelog = https://github.com/BAMweldx/weldx/blob/master/CHANGELOG.md - -[options] -packages = find: -python_requires = >=3.9 -setup_requires = - setuptools >=38.3.0 - setuptools_scm -install_requires = - weldx >=0.6 - ipywidgets - k3d >=2.12 - ipympl - bidict - ipyfilechooser - tqdm - #jpy_video @ git+https://github.com/Who8MyLunch/Jupyter_Video_Widget@462f875 - -[compile_catalog] -directory = weldx_widgets/locale -domain = base -use-fuzzy = True -statistics = True - -[extract_messages] -keywords = _ - -[options.extras_require] -test = - pytest - pytest-cov - pytest-xdist - -[flake8] -# see weldx formatting -ignore = W503,W504,E203 -max-line-length = 88 -select = C,E,F,W,B,B950 # black formatting options -exclude = - __init__.py, - -[pydocstyle] -# convention numpy is currently equivalent to ignoring 'D107', 'D203', 'D212', 'D213', 'D402', 'D413' -convention = numpy -match = (?!__)(?!_version)(?!conftest).*\.py -match_dir = [^\.][^\docs].* - -[isort] -profile = black -default_section = THIRDPARTY -known_first_party = weldx,weldx_widgets -sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER - - -[tool:pytest] -addopts = --tb=short --color=yes -rsw --cov=weldx_widgets --cov-report=term-missing:skip-covered diff --git a/setup.py b/setup.py deleted file mode 100644 index 0f6e146..0000000 --- a/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Setup for weldx-widgets.""" -import setuptools -from setuptools.command.egg_info import egg_info - - -class InstallWithCompile(egg_info): - """Build messages catalog (.mo files) during egg_info.""" - - def run(self): - """Run it.""" - from babel.messages.frontend import compile_catalog - - compiler = compile_catalog(self.distribution) - option_dict = self.distribution.get_option_dict("compile_catalog") - compiler.domain = [option_dict["domain"][1]] - compiler.directory = option_dict["directory"][1] - compiler.use_fuzzy = True # fixme: this should not be mandatory! - compiler.run() - - super().run() - - -if __name__ == "__main__": - setuptools.setup( - cmdclass=dict(egg_info=InstallWithCompile), - package_data={"": ["locale/*/*/*.mo", "locale/*/*/*.po"]}, - ) diff --git a/weldx_widgets/__init__.py b/weldx_widgets/__init__.py index 13f5c2e..80ef810 100644 --- a/weldx_widgets/__init__.py +++ b/weldx_widgets/__init__.py @@ -1,6 +1,6 @@ """Weldx widgets.""" from .generic import WidgetSaveButton, WidgetTimeSeries -from .widget_factory import FloatWithUnit, WidgetLabeledTextInput +from .widget_factory import WidgetFloatWithUnit, WidgetLabeledTextInput from .widget_gas import WidgetShieldingGas from .widget_gmaw import WidgetGMAW from .widget_groove_sel import WidgetGrooveSelection, WidgetGrooveSelectionTCPMovement @@ -13,7 +13,7 @@ WidgetLabeledTextInput, WidgetSaveButton, WidgetGMAW, - FloatWithUnit, # TODO: rename + WidgetFloatWithUnit, ] __all__ = map(str, (x.__name__ for x in __all__)) diff --git a/weldx_widgets/generic.py b/weldx_widgets/generic.py index 3783ce5..a548fe0 100644 --- a/weldx_widgets/generic.py +++ b/weldx_widgets/generic.py @@ -10,7 +10,6 @@ from ipywidgets import HTML, Button, HBox, Label import weldx -from weldx_widgets.translation_utils import _i18n as _ from weldx_widgets.widget_base import WeldxImportExport, WidgetMyHBox, WidgetMyVBox from weldx_widgets.widget_factory import ( WidgetLabeledTextInput, @@ -59,14 +58,12 @@ def __init__( filename=filename, filter_pattern=filter_pattern, select_default=select_default, - select_desc=_("Select"), - change_desc=_("Change"), + select_desc="Select", + change_desc="Change", ) - self.button = Button(description=_(desc), layout=button_layout) + self.button = Button(description=desc, layout=button_layout) - super(WidgetSaveButton, self).__init__( - children=(self.file_chooser, self.button) - ) + super().__init__(children=(self.file_chooser, self.button)) def set_handler(self, handler: Callable): """Set action handler on save button click.""" @@ -91,11 +88,6 @@ def path(self): class WidgetTimeSeries(WidgetMyVBox, WeldxImportExport): """Preliminary time series editing widget.""" - @property - def schema(self) -> str: - """Return schema to validate data against.""" - return "time_series" - # TODO: handle math-expr def __init__( self, base_unit, time_unit="s", base_data="0", time_data="0", title="" @@ -121,7 +113,7 @@ def __init__( ] if title: children.insert(0, Label(title)) - super(WidgetTimeSeries, self).__init__(children=children) + super().__init__(children=children) def to_tree(self) -> dict: """Get mapping of input fields.""" @@ -153,7 +145,7 @@ def download_button( button_description: str, html_instance: Optional[HTML] = None, ) -> HTML: - """Load data from buffer into base64 payload embedded into a HTML button. + """Load data from buffer into base64 payload embedded into an HTML button. Parameters ---------- diff --git a/weldx_widgets/kisa/__init__.py b/weldx_widgets/kisa/__init__.py deleted file mode 100644 index bd362ff..0000000 --- a/weldx_widgets/kisa/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Widgets solely belonging to the KISA project.""" diff --git a/weldx_widgets/kisa/load.py b/weldx_widgets/kisa/load.py deleted file mode 100644 index 14c08bd..0000000 --- a/weldx_widgets/kisa/load.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Utility functions to set widget state from a given file.""" -from pathlib import Path -from typing import Iterable, Union - -import weldx -from weldx_widgets.kisa.save import get_param_from_env -from weldx_widgets.widget_base import WeldxImportExport - - -def set_state_from_file( - widget: Union[WeldxImportExport, Iterable[WeldxImportExport]], file=None -): - """Set the state of given widgets from weldx file tree.""" - if not file: - file = get_param_from_env("file") - - if not isinstance(widget, Iterable): - widget = [widget] - - if isinstance(file, (str, Path)): - file = Path(file) - if file.exists() and file.stat().st_size > 0: - try: - with weldx.WeldxFile(file, mode="r") as wx: - for w in widget: - try: - w.from_tree(wx) - except KeyError as ke: - print(f"Key not found in given file. Details: {ke}") - except Exception as e: - print(f"Error during reading {file}: {e}") - except Exception as e: - print(f"Error during reading {file}: {e}") - else: - print(f"Unknown input file type: {type(file)}") diff --git a/weldx_widgets/kisa/save.py b/weldx_widgets/kisa/save.py deleted file mode 100644 index 818e793..0000000 --- a/weldx_widgets/kisa/save.py +++ /dev/null @@ -1,254 +0,0 @@ -"""Widget to save the state of a WeldxFile and invoke the next step in the pipeline.""" -import pathlib -import typing -from os import environ as env -from typing import Any, Dict, Mapping, TypeVar -from urllib.parse import parse_qs, urlencode - -import ipywidgets as w - -import weldx -import weldx_widgets -import weldx_widgets.widget_base -import weldx_widgets.widget_factory -from weldx_widgets.translation_utils import _i18n as _ -from weldx_widgets.widget_factory import button_layout - -__all__ = [ - "SaveAndNext", - "build_url", - "get_param_from_env", - "invoke_url", -] - - -def get_param_from_env(name, default=None) -> str: - """Extract parameter from env.QUERY_STRING. - - Parameters - ---------- - name : - name of the parameter to extract. - - default : - optional default value, if parameter is not set. - - Returns - ------- - str : - value of the requested parameter. - - """ - query_string = env.get("QUERY_STRING", "") - parameters = parse_qs(query_string) - try: - value = parameters[name][0] - except KeyError: # TODO: this can also raise something else, right? - if default: - return default - else: - raise RuntimeError( - f"parameter '{name}' unset and no default provided." - f" Given parameters: {parameters}" - ) - return value - - -def build_url(board: str, parameters: dict = None, invoke=True, out=None) -> str: - """Build an URL with given parameters. - - Parameters - ---------- - board : - dash board to invoke next. May contain a relative path. - parameters : - optional parameters to encode. - invoke : - should the url be invoked in a web browser? - - Returns - ------- - str : - the built url. - """ - if invoke and not out: - raise ValueError("need output to invoke Javascript.") - - server = env.get("SERVER_NAME", "localhost") - protocol = env.get("SERVER_PROTOCOL", "HTTP") - if "HTTPS" in protocol: - url = "https://" - else: - url = "http://" - url += server - - # TODO: this only works from voila! - port = env.get("SERVER_PORT", "8888") - - if port: - url += f":{port}/" - else: - url += "/" - voila = "voila" in env.get("SERVER_SOFTWARE", "") - prefix = "voila/render" if voila else "" - - url += f"{prefix}/{board}" - - if parameters: - params_encoded = urlencode(parameters) - url += f"?{params_encoded}" - - if invoke: - invoke_url(url, out) - return url - - -def invoke_url(url, out): - """Invoke url in new browser tab. - - We cannot use python stdlib webbrowser here, because this code will be executed - on the server. So we impl this via Javascript. - """ - from IPython.display import Javascript, clear_output, display - - with out: - clear_output() - js = Javascript(f'window.open("{url}");') - display(js) - - -_KeyType = TypeVar("KeyType") - - -def _deep_update_inplace( - mapping: Dict[_KeyType, Any], *updating_mappings: Dict[_KeyType, Any] -) -> Dict[_KeyType, Any]: - for updating_mapping in updating_mappings: - for k, v in updating_mapping.items(): - if k in mapping and isinstance(mapping[k], dict) and isinstance(v, Mapping): - mapping[k] = _deep_update_inplace(mapping[k], v) - else: - mapping[k] = v - return mapping - - -class SaveAndNext(weldx_widgets.widget_base.WidgetMyVBox): - """Collect all the data from passed import/output widget list and stores it. - - Parameters - ---------- - filename: - output file name. - next_notebook: - next dashboard/notebook to invoke. - status : - the file update will contain the new status. - collect_data_from : - a list of widgets to build a tree from. - next_notebook_params : - optional parameters for next dashboard. - - Notes - ----- - The passed status will be set into the wx_user["kisa"]["status"] dict. - """ - - def __init__( - self, - filename, - next_notebook: str, - status: str, - collect_data_from: typing.List[weldx_widgets.widget_base.WeldxImportExport], - next_notebook_desc: str = "Invoke next step", - next_notebook_params=None, - title="Save results", - disable_next_button=True, - ): - self.status = status - self.collect_data_from = collect_data_from - self.out = w.Output() - - if not disable_next_button: - self.btn_next = w.Button( - description=_(next_notebook_desc), layout=button_layout - ) - if next_notebook_params is None: - next_notebook_params = dict() - self.next_notebook_params = next_notebook_params - self.next_notebook = next_notebook - self.btn_next.on_click(self.on_next) - - self._initial_file = filename # remember initial choice of file. - fn_path = pathlib.Path(filename) - path = str(fn_path.parent) - fn = str(fn_path.name) - self.save_button = weldx_widgets.WidgetSaveButton( - desc="1." + _("Save") if not disable_next_button else _("Save"), - filename=fn, - path=path, - select_default=True, - ) - self.save_button.set_handler(self.on_save) - if not disable_next_button: - self.save_button.children += (self.btn_next,) - - children = [ - weldx_widgets.widget_factory.make_title(title), - self.save_button, - self.out, - ] - super(SaveAndNext, self).__init__(children=children) - - @property - def filename(self): - """Return output file name.""" - return self.save_button.path - - def on_save(self, _): - """Handle saving data to file.""" - from IPython.display import clear_output, display - - clear_output() - - result = dict() - for widget in self.collect_data_from: - _deep_update_inplace(result, widget.to_tree()) - - # set status - result["wx_user"] = {"KISA": {"status": self.status}} - - def show_header(handle): - with self.out: - clear_output() - display(handle.header(False, True)) - - # open (existing) file and update it. - if pathlib.Path(self.filename).stem.endswith("_r"): - with self.out: - print("Refusing to save a read-only (template) file!") - print("Please choose another name with the '_r' suffix.") - return - if self.filename != self._initial_file: - # we want to save the previous file under a different name, so load contents - with weldx.WeldxFile(self._initial_file, mode="r") as fh: - _deep_update_inplace(fh, result) - if not pathlib.Path(self.filename).exists(): - fh.write_to(self.filename) - show_header(fh) - else: - with weldx.WeldxFile(self.filename, mode="rw") as fh2: - _deep_update_inplace(fh2, fh) - show_header(fh2) - else: - with weldx.WeldxFile(self.filename, mode="rw", sync=True) as fh: - _deep_update_inplace(fh, result) - show_header(fh) - - def on_next(self, _): - """Invoke next notebook.""" - build_url( - board=self.next_notebook, - parameters=dict(file=self.filename, **self.next_notebook_params), - invoke=True, - out=self.out, - ) diff --git a/weldx_widgets/kisa/widget_robot_program.py b/weldx_widgets/kisa/widget_robot_program.py deleted file mode 100644 index e5188f0..0000000 --- a/weldx_widgets/kisa/widget_robot_program.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Widget to create a robot program.""" -import tempfile -from io import BytesIO -from pathlib import Path -from typing import Callable, Optional, Union - -import ipywidgets as widgets -import numpy as np -import pint -from ipywidgets import HTML, Button, Label, Layout - -from weldx import Q_, WeldxFile -from weldx.tags.core.file import ExternalFile -from weldx_widgets.generic import download_button -from weldx_widgets.translation_utils import _i18n as _ -from weldx_widgets.widget_base import WidgetMyVBox -from weldx_widgets.widget_factory import FloatWithUnit, make_title - -__all__ = [ - "WidgetLinearWeldYaskawa", -] - - -class WidgetLinearWeldYaskawa(WidgetMyVBox): - """After process parameters and groove shape selection, generate a program. - - The program is created with one button, then downloaded with another. - Another side-effect of saving this widget into a weldx file is, that the content - of the robot program is stored in the weldx file. - - Parameters - ---------- - wx_file : - Weldx input file name, has to contain geometry and welding seam information. - program_func : - Callable to create to actual robot program. - """ - - def __init__( - self, - wx_file: str, - program_func: Callable[ - [Union[str, Path, WeldxFile], pint.Quantity, str, bool], Union[Path, bytes] - ], - ): - self.temp_dir = tempfile.TemporaryDirectory() # outputs will be stored here. - self.output: Optional[bytes] = None # binary contents of robot program. - self.wx_file = wx_file - self.program_func = program_func - # The translation from the user frame (UF) to the - # workpiece coordinate system (WCS). - # The vector points UF -> WCS. - xyz = ( - FloatWithUnit(text="x", unit="mm", value=-20, min=-999999), - FloatWithUnit(text="y", unit="mm", value=115.8, min=-999999), - FloatWithUnit(text="z [optional]", unit="mm", value=0, min=-999999), - ) - - self.uf_to_workpiece = WidgetMyVBox( - children=[ - Label( - "Translation vector from user frame to workpiece coordinate system", - ), - *xyz, - ] - ) - self.jobname = widgets.Text(description=_("Jobname"), value="MAIN_LINEAR_NEW") - - self.button_create = Button( - description=_("Create program"), - ) - self.button_create.on_click(self.create_linear_program) - - # changes to x, y, z or job name invalidate the download button - for elem in xyz: - for w in elem.children[1:]: - w.observe(self._invalidate_dl_button) - self.jobname.observe(self._invalidate_dl_button) - - super(WidgetLinearWeldYaskawa, self).__init__( - children=[ - make_title(_("Create Yaskawa program (linear seam)")), - self.uf_to_workpiece, - self.jobname, - self.button_create, - HTML(), - ], - layout=Layout(width="500px"), - ) - - def _invalidate_dl_button(self, button): - self.children[-1].value = "" - - def create_linear_program(self, button): - """Invoke self.program_func with form parameters and create download button.""" - base_unit = self.uf_to_workpiece.children[1].unit - uf_to_workpiece = Q_(np.array([None, None, None]), base_unit) - uf_to_workpiece[0] = self.uf_to_workpiece.children[1].quantity.to(base_unit) - uf_to_workpiece[1] = self.uf_to_workpiece.children[2].quantity.to(base_unit) - if self.uf_to_workpiece.children[3].float_value != 0: - uf_to_workpiece[2] = self.uf_to_workpiece.children[3].quantity.to(base_unit) - else: - uf_to_workpiece = uf_to_workpiece[:2] - assert len(uf_to_workpiece) == 2 - - with WeldxFile(self.wx_file, mode="r") as wx_input: - temp: BytesIO = wx_input.write_to(None) - output: bytes = self.program_func(temp, uf_to_workpiece, write_file=False) - dl_button = self.children[-1] - download_button( - output, - filename=f"{self.jobname.value}.JBI", - button_description=_("Download program"), - html_instance=dl_button, - ) - - self.output = output - - def to_tree(self) -> dict: - """Export state.""" - if not self.output: - return {} - fn = Path(self.jobname.value) - with open(self.temp_dir.name / fn, mode="wb") as fh: - fh.write(self.output) - external_file = ExternalFile(fh.name, asdf_save_content=True) - return dict(robot_program=external_file) - - def from_tree(self, tree): - """Set state. Is currently a dummy.""" diff --git a/weldx_widgets/kisa/widget_scans.py b/weldx_widgets/kisa/widget_scans.py deleted file mode 100644 index face352..0000000 --- a/weldx_widgets/kisa/widget_scans.py +++ /dev/null @@ -1,258 +0,0 @@ -"""Widget showing scanner results.""" -from pathlib import Path -from typing import Tuple - -import numpy as np -import xarray as xr -from tqdm.autonotebook import tqdm - -import weldx -from weldx_widgets.widget_base import WidgetSimpleOutput - -__all__ = ["WidgetScans"] - - -# TODO: import/use this from libo lib! -# TODO: handle both cases, parameterstudie (mehrere schweißungen auf einem werkstück) -# und einzel schweißung! -class WidgetScans(WidgetSimpleOutput): - """Extracts triggers and scan data from given files and visualizes it. - - Parameters - ---------- - mh24_file : - path to twincat main file. - tool_file : - path to yaskawa tool file. - scans_dir : - path to scan directory. - single_weld : - Just one scan, or multiple? - max_scans : - limit for scans to read in/visualize. - """ - - def __init__( - self, mh24_file, tool_file, scans_dir, single_weld=False, max_scans=None - ): - super(WidgetScans, self).__init__(width="100%", height="800 px") - - self.mh24_file = Path(mh24_file) - self.tool_file = Path(tool_file) - self.scans_dir = Path(scans_dir) - assert self.scans_dir.exists() - assert self.mh24_file.exists() - assert self.tool_file.exists() - - self.max_scans = max_scans - self.single_weld = single_weld - - from cached_io import read_cached_txt - - mh24_ds = read_cached_txt(mh24_file) - from libo.utils import split_by_trigger - - mh_scan_list = split_by_trigger(mh24_ds, "trigScan1_prog") - mh_schweiss_list = split_by_trigger(mh24_ds, "trigSchweissen") - - self.csm = weldx.CoordinateSystemManager("user_frame") - - self._create_csm(mh_schweiss_list) - self._vorher_nachher_scans_an_csm(mh_scan_list) - - def _create_csm(self, mh_schweiss_list): - for mh_schweiss in mh_schweiss_list[: self.max_scans]: - coords = mh_schweiss[["UF_X", "UF_Y", "UF_Z"]].dropna(dim="time") - coords = ( - coords.to_array(dim="c") - .assign_coords(dict(c=["x", "y", "z"])) - .astype("float32") - ) - coords_start = coords.isel(time=0).drop("time") - tcp_start = weldx.LocalCoordinateSystem(coordinates=coords_start) - tpc_in_uf = weldx.LocalCoordinateSystem(coordinates=coords - coords_start) - naht_NR = int(mh_schweiss.naht_NR.max().values) - self.csm.add_cs(f"n_start{naht_NR}", "user_frame", tcp_start) - self.csm.add_cs(f"n{naht_NR}", f"n_start{naht_NR}", tpc_in_uf) - - def _vorher_nachher_scans_an_csm(self, mh_scan_list): - scans = [] - # TODO: handle self.single_weld (different pattern needed). - if self.single_weld: - _slice = slice(1, self.max_scans, 2) - n = len(mh_scan_list[_slice]) - mh_scan_list_i = iter(mh_scan_list[_slice]) - else: - _slice = slice(1, self.max_scans) - mh_scan_list_i = iter(mh_scan_list[_slice]) - n = len(mh_scan_list[_slice]) - self._max_heights = [] - with self.out: - for mh_scan in tqdm(mh_scan_list_i, total=n, desc="process scan data"): - naht_nr = int(mh_scan.naht_NR.max().values) - schw_nr = int(mh_scan.schw_NR.max().values) - pattern = f"LLT1_WID*_N{naht_nr:03d}_L001_R001_S{schw_nr}_*.nc" - scans_list = list(self.scans_dir.glob(pattern)) - assert len(scans_list) == 1 - - SCAN_file = scans_list[0] - scan = xr.load_dataset(SCAN_file) - - res = self._build_scan_data(mh_scan, scan) - scan_name = f"scan_N{naht_nr}_S{schw_nr}" - scans.append(scan_name) - res = self.csm.transform_data(res, "user_frame", f"n_start{naht_nr}") - # determine max value and store it for later display - # TODO: max should return use only z-dimension - max_height = res.coordinates.max(dim="n") - print(max_height) - self._max_heights.append(max_height[2]) - self.csm.assign_data(res, scan_name, f"n_start{naht_nr}") - self.csm.assign_data( - max_height, scan_name + "max_h", f"n_start{naht_nr}" - ) - self.scans = scans - - def display(self): - """Render all pairs of scans and tcp movements.""" - scans = [ - f"n{x}" - for x in range(1, self.max_scans - 1 if self.max_scans else len(self.scans)) - ] - self.out.clear_output() - with self.out: - self.csm.plot( - backend="k3d", - coordinate_systems=["user_frame"] + scans, - data_sets=self.scans, - axes_equal=True, - ) - # from matplotlib.pylab import plot - - # plot(self._max_heights) - # TODO: add maximum height points and descriptions - # import k3d - - # for i, max_ in enumerate(self._max_heights): - # max_heights = k3d.points( - # positions=max_.T, point_size=3, name="max height %i" % i - # ) - # vis.plot += max_heights - - def _build_scan_data( - self, - mh_scan, - scan: xr.Dataset, - ) -> weldx.SpatialData: - """Create transformed scan SpatialData from robot movement and LLT scan data. - - Parameters - ---------- - mh_scan: - The pre sliced trigger information - create by e.g. ``split_by_trigger(mh24, "trigScan1_prog")`` - scan: - The scan data as loaded xr.Dataset - - Returns - ------- - weldx.SpatialData - The scan data transformed into default user_frame - """ - from libo.io.yaskawa import create_csm - from libo.utils import get_data_transformation - - # load tool CSM -------------------------------------------------------------- - csm_tool = create_csm(self.tool_file) - csm_tool.relabel({"STANDARD": "TCP"}) - csm_tool.add_cs( - "LLT_1_data", - "LLT_1", - lcs=weldx.LocalCoordinateSystem( - coordinates=[0, 0, -260], - orientation=weldx.WXRotation.from_euler("z", -90, degrees=True), - ), - ) - - lcs = get_data_transformation(mh_scan) - - # build and reshape scan data ------------------------------------------------- - data, triangle_indices = self._reshape_scan_data(scan, lcs) - - data[(data[:, 2] < -250), 2] = np.nan # simple outliner removal - data[(data[:, 2] > 50), 2] = np.nan # simple outliner removal - sd = weldx.SpatialData( - coordinates=data.astype("float32"), - triangles=triangle_indices.astype("uint32"), - ) - - return sd - - def _reshape_scan_data( - self, - ds: xr.Dataset, - lcs: weldx.LocalCoordinateSystem, - ) -> Tuple[np.ndarray, np.ndarray]: - """Transform data gathered by LLT Dashboard into userframe coordinates. - - Parameters - ---------- - ds: - scan-dataset produced by LLT Dashboard - lcs: - LocalCoordinateSystem that describes the movement of - the LLT Scanner during scan. - - Returns - ------- - xyz-Data and default triangulation of profile-scan - - """ - nx = ds.n.shape[0] - ny = ds.p.shape[0] - - ds["y"] = (("p", "n"), np.zeros((ny, nx))) - - data_arr = ds[["x", "y", "z"]].to_array("c") - - transformed = weldx.util.xr_matmul( - lcs.orientation, data_arr, dims_a=["c", "v"], dims_b=["c"], dims_out=["c"] - ) - # TODO: input param! - n_slice = slice(450, 800) - transformed = transformed + lcs.coordinates.rename({"time": "p"}) - transformed = transformed.isel(n=n_slice) - - ds = transformed.to_dataset(dim="c") - - nx = ds.n.shape[0] - ny = ds.p.shape[0] - - data = np.zeros((nx * ny, 3)) - data[:, 0] = ds.x.data.flatten() - data[:, 1] = ds.y.data.flatten() - data[:, 2] = ds.z.data.flatten() - - triangle_indices = np.empty((ny - 1, nx - 1, 2, 3), dtype=int) - r = np.arange(nx * ny).reshape(ny, nx) - triangle_indices[:, :, 0, 0] = r[:-1, :-1] - triangle_indices[:, :, 1, 0] = r[:-1, 1:] - triangle_indices[:, :, 0, 1] = r[:-1, 1:] - - triangle_indices[:, :, 1, 1] = r[1:, 1:] - triangle_indices[:, :, :, 2] = r[1:, :-1, None] - triangle_indices.shape = (-1, 3) - - return data, triangle_indices - - -if __name__ == "__main__": - from kisa_config import p_base - - WID = 432 - base = p_base / f"WID{WID}" - - TOOL_file = p_base / "MH24_TOOL.CND" - MH24_file = list((base / "MAIN").glob("*_MH24_MAIN_AutoSave.txt.gz"))[0] - - WidgetScans(MH24_file, TOOL_file, base / "SCAN", max_scans=4).display() diff --git a/weldx_widgets/locale/de/LC_MESSAGES/base.po b/weldx_widgets/locale/de/LC_MESSAGES/base.po deleted file mode 100644 index def95ca..0000000 --- a/weldx_widgets/locale/de/LC_MESSAGES/base.po +++ /dev/null @@ -1,320 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-12-21 10:30+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: UTF-8\n" -"charset=utf8" - -#: widget_gas.py:47 -msgid "Hydrogen" -msgstr "Wasserstoff" - -#: widget_gas.py:47 -msgid "Oxygen" -msgstr "Sauerstoff" - -#: widget_gas.py:41 -msgid "Add gas component" -msgstr "Gas hinzufügen" - -#: widget_gas.py:69 -msgid "percentage" -msgstr "Prozent" - -#: widget_gas.py:75 -msgid "delete" -msgstr "löschen" - -#: widget_gas.py:151 -msgid "Flow rate" -msgstr "Flußrate" - -#: widget_gas.py:112 -msgid "Check percentages, all components should sum up to 100!" -msgstr "" - -#: widget_measurement.py:57 widget_gmaw.py:42 -msgid "time" -msgstr "Zeit" - -#: widget_gmaw.py:62 -msgid "Manufacturer" -msgstr "Hersteller" - -#: widget_gmaw.py:63 -msgid "Power source" -msgstr "Stromquelle" - -#: widget_gmaw.py:65 -msgid "Wire feed rate" -msgstr "Drahtvorschubrate" - -#: widget_gmaw.py:102 -msgid "Pulse duration" -msgstr "Pulsdauer" - -#: widget_gmaw.py:104 -msgid "Pulse frequency" -msgstr "Pulsfrequenz" - -#: widget_gmaw.py:106 -msgid "Base current" -msgstr "Grundstromstärke" - -#: widget_gmaw.py:109 -msgid "Pulse voltage" -msgstr "Pulsspannung" - -#: widget_gmaw.py:111 -msgid "Pulse current" -msgstr "Pulsstromstärke" - -#: widget_gmaw.py:122 widget_gmaw.py:270 widget_gmaw.py:271 widget_gmaw.py:335 -msgid "Pulsed" -msgstr "Gepulst" - -#: widget_gmaw.py:122 -msgid "process parameters" -msgstr "Prozessparameter" - -#: widget_gmaw.py:185 -msgid "Impedance" -msgstr "Impedanz" - -#: widget_gmaw.py:186 -msgid "Characteristic" -msgstr "Charakteristisch" - -#: widget_gmaw.py:190 -msgid "Spray process parameters" -msgstr "Sprühlichtbogen Parameter" - -#: widget_gmaw.py:233 -msgid "Diameter" -msgstr "Durchmesser" - -#: widget_gmaw.py:234 -msgid "Class" -msgstr "Klasse" - -#: widget_gmaw.py:237 -msgid "Metadata" -msgstr "Metadaten" - -#: widget_gmaw.py:239 -msgid "Wire parameters" -msgstr "Drahtparameter" - -#: widget_gmaw.py:269 widget_gmaw.py:337 -msgid "Spray" -msgstr "Sprüh" - -msgid "CMT" -msgstr "" - -#: widget_gmaw.py:287 -msgid "Process type" -msgstr "Prozesstyp" - -#: widget_gmaw.py:295 -msgid "GMAW process parameters" -msgstr "GMAW Prozessparameter" - -#: widget_gmaw.py:296 -msgid "Shielding gas" -msgstr "Schutzgas" - -#: widget_gmaw.py:340 -msgid "unknown process type" -msgstr "Unbekannter Prozesstyp" - -#: widget_gmaw.py:118 -msgid "voltage/current" -msgstr "Spannung/Stromstärke" - -#: widget_gmaw.py:120 -msgid "current" -msgstr "Stromstärke" - -msgid "invoke next step" -msgstr "Nächster Schritt" - -msgid "Save results" -msgstr "Speichern" - -#: kisa/save.py:169 -msgid "Save" -msgstr "Speichern" - -#: generic.py:62 -msgid "Select" -msgstr "Auswählen" - -#: generic.py:63 -msgid "Change" -msgstr "Ändern" - -#: widget_groove_sel.py:67 -msgid "Export geometry to CAD file [optional]" -msgstr "CAD-Export [optional]" - -#: widget_groove_sel.py:75 -msgid "Data format" -msgstr "Datenformat" - -#: widget_groove_sel.py:77 -msgid "Create program" -msgstr "Programm erstellen" - -#: widget_groove_sel.py:88 -msgid "Profile raster width" -msgstr "Breite Profilraster" - -#: widget_groove_sel.py:94 -msgid "Trace raster width" -msgstr "Breite Spurraster" - -#: widget_groove_sel.py:141 -msgid "Download program" -msgstr "Lade Program" - -#: widget_groove_sel.py:152 -msgid "Common name" -msgstr "Allgemeiner Name" - -#: widget_groove_sel.py:153 -msgid "Standard" -msgstr "Standard" - -#: widget_groove_sel.py:154 -msgid "Thickness" -msgstr "Dicke" - -#: widget_groove_sel.py:156 -msgid "Base metal" -msgstr "Grundmetal" - -#: widget_groove_sel.py:217 -msgid "Groove type" -msgstr "Fugentyp" - -#: widget_groove_sel.py:230 widget_groove_sel.py:422 -msgid "profile" -msgstr "Profil" - -#: widget_groove_sel.py:232 -msgid "ISO 9692-1 Groove selection" -msgstr "ISO 9692-1 Fugenauswahl" - -#: widget_groove_sel.py:320 -msgid "Code number" -msgstr "Kodenummer" - -#: widget_groove_sel.py:324 -msgid "angle" -msgstr "Winkel" - -#: widget_groove_sel.py:326 -msgid "workpiece_thickness" -msgstr "Werkstückdicke" - -#: widget_groove_sel.py:314 widget_groove_sel.py:347 widget_groove_sel.py:359 -msgid "code_number" -msgstr "Kodenummer" - -#: widget_groove_sel.py:400 widget_evaluate.py:121 -msgid "Seam length" -msgstr "Nahtlänge" - -#: widget_groove_sel.py:405 -msgid "weld speed" -msgstr "Schweißgeschwindigkeit" - -#: widget_groove_sel.py:409 -msgid "Welding parameters" -msgstr "Schweißparameter" - -#: widget_measurement.py:58 -msgid "Measurements" -msgstr "Messungen" - -#: widget_measurement.py:96 -msgid "Measurement chain" -msgstr "Messkette" - -#: widget_evaluate.py:41 widget_evaluate.py:108 -msgid "Process parameters" -msgstr "Prozessparameter" - -#: widget_evaluate.py:101 -msgid "ASDF-header" -msgstr "ASDF-Dateikopf" - -#: widget_evaluate.py:112 -msgid "Specimen" -msgstr "Werkstück" - -#: widget_evaluate.py:161 -msgid "CSM-Subsystems" -msgstr "" - -#: widget_evaluate.py:271 -msgid "Difference in welding speed" -msgstr "Unterschied Schweißgeschwindigkeit" - -#: widget_evaluate.py:272 widget_evaluate.py:281 -msgid "time in s" -msgstr "Zeit in s" - -#: widget_evaluate.py:273 widget_evaluate.py:282 -msgid "diff in mm" -msgstr "Unterschied in mm" - -#: widget_evaluate.py:276 -msgid "User frame deviation" -msgstr "Abweichung Nutzerrahmen" - -# groove types -msgid "Groove angle" -msgstr "Fugenwinkel" - -msgid "Workpiece thickness" -msgstr "Dicke Werkstück" - -msgid "Root face" -msgstr "Wurzelzone" - -msgid "Root gap" -msgstr "Wurzellücke" - -msgid "Bevel angle" -msgstr "Keilwinkel" - -msgid "Bevel radius" -msgstr "Keilradius" - -msgid "Special depth" -msgstr "Spezialtiefe" - -# workpiece.ipynb heading -msgid "Choose a groove and your linear weld seam parameters." -msgstr "Wähle Fugen- und lineare Schweißbewegungsparameter." - -msgid "Here you can choose from various shielding gases and spray and pulsed MIG processes and its parameters." -msgstr "Hier können Sie verschiedene Prozessparameter, wie Schutzgase und Sprüh/Pulslichtbogenparameter einstellen." - -msgid "Welding process type and parameters" -msgstr "Schweißprozessparameter" diff --git a/weldx_widgets/locale/en/LC_MESSAGES/base.po b/weldx_widgets/locale/en/LC_MESSAGES/base.po deleted file mode 100644 index 864452e..0000000 --- a/weldx_widgets/locale/en/LC_MESSAGES/base.po +++ /dev/null @@ -1,18 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-12-16 16:02+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: en\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: utf-8\n" diff --git a/weldx_widgets/tests/conftest.py b/weldx_widgets/tests/conftest.py index 227b32c..0529201 100644 --- a/weldx_widgets/tests/conftest.py +++ b/weldx_widgets/tests/conftest.py @@ -5,8 +5,8 @@ @pytest.fixture(scope="session", autouse=True) def setup_and_teardown_package(): """Set up testing package.""" - import matplotlib + import matplotlib as mpl """Set Agg matplotlib backend for this module.""" - matplotlib.use("Agg", force=True) + mpl.use("Agg", force=True) yield diff --git a/weldx_widgets/tests/test_gas.py b/weldx_widgets/tests/test_gas.py index b7df33f..e241a58 100644 --- a/weldx_widgets/tests/test_gas.py +++ b/weldx_widgets/tests/test_gas.py @@ -3,8 +3,6 @@ from weldx import WeldxFile from weldx_widgets import WidgetShieldingGas -from weldx_widgets.tests.util import voila_language -from weldx_widgets.widget_gas import WidgetSimpleGasSelection @pytest.mark.parametrize("write_file", (True, False)) @@ -15,7 +13,7 @@ def test_import_export(write_file): w.gas_components.gas_selection.index = 3 w.gas_components._add_gas_comp(None) percentages = (80, 20) - for i, (name, box) in enumerate(w.gas_components.components.items()): + for i, (_name, box) in enumerate(w.gas_components.components.items()): box.children[1].value = percentages[i] tree = w.to_tree() if write_file: @@ -28,10 +26,3 @@ def test_import_export(write_file): w2 = WidgetShieldingGas() w2.from_tree(tree) assert w2.to_tree() == tree - - -def test_lang(): - """Test translation.""" - with voila_language(lang="de"): - w = WidgetSimpleGasSelection() - assert "Sauerstoff" in w.gas_list diff --git a/weldx_widgets/tests/test_k3d.py b/weldx_widgets/tests/test_k3d.py index 13267be..fe62dca 100644 --- a/weldx_widgets/tests/test_k3d.py +++ b/weldx_widgets/tests/test_k3d.py @@ -24,7 +24,7 @@ def test_k3d_csm_vis(): root_face="1 mm", ) - spatial = weldx.Geometry(groove, "10mm").spatial_data("1 mm", "1 mm") + spatial = weldx.Geometry(groove, "10 mm").spatial_data("1 mm", "1 mm") csm.assign_data(spatial, "workpiece", "A") plot = CoordinateSystemManagerVisualizerK3D(csm=csm) diff --git a/weldx_widgets/tests/test_process.py b/weldx_widgets/tests/test_process.py index 65fd9ee..6463cf0 100644 --- a/weldx_widgets/tests/test_process.py +++ b/weldx_widgets/tests/test_process.py @@ -4,18 +4,17 @@ import weldx from weldx import Q_ from weldx_widgets import WidgetGMAW -from weldx_widgets.tests.util import voila_language @pytest.mark.parametrize( - ("kind", "write_file"), + "kind", ( - ("spray", True), - ("UI", True), - ("II", True), + "spray", + "UI", + "II", ), ) -def test_import_export(kind, write_file): +def test_import_export(kind): """Ensure import and exports of Widgets works.""" w = WidgetGMAW(process_type=kind) proc = w.welding_process @@ -33,21 +32,13 @@ def test_import_export(kind, write_file): tree = w.to_tree() - if write_file: - tree = { - key: value - for key, value in weldx.WeldxFile(tree=tree, mode="rw").items() - if key not in ("asdf_library", "history") - } + tree = { + key: value + for key, value in weldx.WeldxFile(tree=tree, mode="rw").items() + if key not in ("asdf_library", "history") + } w2 = WidgetGMAW() w2.from_tree(tree) assert w2.to_tree() == tree - - -def test_lang(): - """Test translation.""" - with voila_language(lang="de"): - w = WidgetGMAW(process_type="spray") - assert w.welding_wire.diameter.text == "Durchmesser" diff --git a/weldx_widgets/tests/test_save.py b/weldx_widgets/tests/test_save.py deleted file mode 100644 index e14d356..0000000 --- a/weldx_widgets/tests/test_save.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Tests for save widget.""" -import shutil - -import pytest - -import weldx -from weldx_widgets.kisa.save import SaveAndNext - - -class SimpleIO: - """Implements serialization protocol for weldx widgets.""" - - def __init__(self): - self.data = 42 - - def to_tree(self): - """Get data.""" - return {"data": self.data} - - def from_tree(self, tree: dict): # noqa - """Set data.""" - self.data = tree["data"] - - -@pytest.mark.parametrize("change", [False, True]) -def test_on_save_update(tmpdir, change): - """Ensure data from input widget get serialized to desired output file. - - Also ensure existing data is preserved, if not being changed by the update. - """ - out_file = str(tmpdir / "out") - - with weldx.WeldxFile(out_file, mode="rw") as fh: - fh["wx_user"] = {"some": "data"} - - status = "test" - w = SaveAndNext( - out_file, next_notebook="no", collect_data_from=[SimpleIO()], status=status - ) - if change: # fake a change of the initial file choice. - new_file = out_file + "_2" - shutil.copy(out_file, new_file) - setattr(SaveAndNext, "filename", new_file) - w.on_save(None) - out_file = new_file - else: - w.on_save(None) - # verify output - with weldx.WeldxFile(out_file) as wx: - assert wx["data"] == 42 - assert "some" in wx["wx_user"] - assert wx["wx_user"]["KISA"]["status"] == status diff --git a/weldx_widgets/tests/test_visualization.py b/weldx_widgets/tests/test_visualization.py index 3678acc..b781891 100644 --- a/weldx_widgets/tests/test_visualization.py +++ b/weldx_widgets/tests/test_visualization.py @@ -36,7 +36,7 @@ def test_plot_coordinate_system(): # exceptions ------------------------------------------ # label without color - with pytest.raises(Exception): + with pytest.raises(ValueError): vs.draw_coordinate_system_matplotlib(lcs_constant, ax, label="label") diff --git a/weldx_widgets/tests/util.py b/weldx_widgets/tests/util.py index a18c295..6be1644 100644 --- a/weldx_widgets/tests/util.py +++ b/weldx_widgets/tests/util.py @@ -11,7 +11,8 @@ def temp_env(**kw): yield - os.environ = old + os.environ.clear() + os.environ.update(old) @contextlib.contextmanager diff --git a/weldx_widgets/translation_utils.py b/weldx_widgets/translation_utils.py deleted file mode 100644 index 99749fe..0000000 --- a/weldx_widgets/translation_utils.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Translation related utilities.""" -import gettext -import os -from pathlib import Path - -__all__ = [ - "gettext", - "set_trans_from_env", - "_i18n", - "_", -] - -_translations = dict() - - -def set_trans_from_env(): - """Set locale from env.QUERY_STRING (if available, defaults to english).""" - from weldx_widgets.kisa.save import get_param_from_env - - lang = get_param_from_env("LANG", default="en") - os.environ["LANG"] = lang - get_trans(lang) - - -def get_trans(lang_="en") -> gettext.GNUTranslations: - """Get translation object for given lang string.""" - trans = _translations.get(lang_) - if not trans: - # Set up message catalog access - if lang_ not in ("en", "de"): - raise ValueError(f"Unsupported language: {lang_}") - - base_dir = Path(__file__).parent / "locale" - if not base_dir.exists() and base_dir.is_dir(): - raise RuntimeError("could not set locale dir (non-existent or not a dir).") - - trans = gettext.translation("base", localedir=str(base_dir), languages=[lang_]) - _translations[lang_] = trans - - assert trans - return trans - - -def _i18n(message: str) -> str: - """Translate given message. Uses os.environ['LANG'] as target language.""" - lang = os.environ.get("LANG", "en")[:2] - if lang.startswith("C"): - lang = "en" - trans_ = get_trans(lang) - if not trans_: - return message - - return trans_.gettext(message) - - -_ = _i18n # alias diff --git a/weldx_widgets/visualization/colors.py b/weldx_widgets/visualization/colors.py index 9618ff2..f7e7c89 100644 --- a/weldx_widgets/visualization/colors.py +++ b/weldx_widgets/visualization/colors.py @@ -50,7 +50,7 @@ def _color_int_to_rgb(integer: int) -> Tuple[int, int, int]: def _color_rgb_to_rgb_normalized( - rgb: Tuple[int, int, int] + rgb: Tuple[int, int, int], ) -> Tuple[float, float, float]: """Normalize an RGB color tuple with the range (0-255) to the range (0.0-1.0). @@ -69,7 +69,7 @@ def _color_rgb_to_rgb_normalized( def _color_rgb_normalized_to_rgb( - rgb: Tuple[float, float, float] + rgb: Tuple[float, float, float], ) -> Tuple[int, int, int]: """Normalize an RGB color tuple with the range (0.0-1.0) to the range (0-255). @@ -143,7 +143,7 @@ def _shuffled_tab20_colors() -> List[int]: def color_to_rgb_normalized( - color: Union[int, Tuple[int, int, int], Tuple[float, float, float]] + color: Union[int, Tuple[int, int, int], Tuple[float, float, float]], ) -> Tuple[float, float, float]: """Convert an arbitrary RGB color representation into a normalized RGB triplet. @@ -228,5 +228,5 @@ def get_color( return _color_rgb_to_int(value) try: return next(color_generator) - except StopIteration: - raise RuntimeError(f"given generator {color_generator} exhausted.") + except StopIteration as si: + raise RuntimeError(f"given generator {color_generator} exhausted.") from si diff --git a/weldx_widgets/visualization/csm_k3d.py b/weldx_widgets/visualization/csm_k3d.py index a6b86b4..0f59eb6 100644 --- a/weldx_widgets/visualization/csm_k3d.py +++ b/weldx_widgets/visualization/csm_k3d.py @@ -198,7 +198,7 @@ def _update_positions(self, coordinates: pint.Quantity, orientation: np.ndarray) orientation : The new orientation """ - self._vectors.origins = [coordinates for _ in range(3)] + self._vectors.origins = [coordinates.to(_DL).m for _ in range(3)] self._vectors.vectors = orientation.transpose() * self._vector_scale self.origin.model_matrix = _create_model_matrix(coordinates, orientation) if self._label is not None: @@ -285,7 +285,7 @@ def limits(self): class SpatialDataVisualizer: """Visualizes spatial data.""" - visualization_methods = ["auto", "point", "mesh", "both"] + visualization_methods = ("auto", "point", "mesh", "both") def __init__( self, @@ -683,6 +683,7 @@ def _get_limits_spatial(self): """Get the limits of all spatial data.""" if not self._data_vis: return None + limits = np.stack([s.data.limits() for s in self._data_vis.values()]) return _get_limits_from_stack(limits) diff --git a/weldx_widgets/visualization/csm_mpl.py b/weldx_widgets/visualization/csm_mpl.py index 08e5b1a..470d093 100644 --- a/weldx_widgets/visualization/csm_mpl.py +++ b/weldx_widgets/visualization/csm_mpl.py @@ -177,7 +177,7 @@ def draw_coordinate_system_matplotlib( if show_origin: axes.plot([p_0[0]], [p_0[1]], [p_0[2]], "o", color=color, label=label) elif label is not None: - raise Exception("Labels can only be assigned if a color was specified") + raise ValueError("Labels can only be assigned if a color was specified") def plot_local_coordinate_system_matplotlib( diff --git a/weldx_widgets/widget_base.py b/weldx_widgets/widget_base.py index 8ee3e61..b9f4572 100644 --- a/weldx_widgets/widget_base.py +++ b/weldx_widgets/widget_base.py @@ -1,19 +1,14 @@ """Base classes for widgets.""" import abc -import functools -from pathlib import Path from ipywidgets import HBox, Layout, Output, VBox -from weldx.asdf.util import get_schema_path -from weldx_widgets.translation_utils import set_trans_from_env - def metaclass_resolver(*classes): """Merge multiple meta classes.""" # Does something like this: # https://coderedirect.com/questions/163000/multiple-inheritance-metaclass-conflict - metaclass = tuple(set(type(cls) for cls in classes)) + metaclass = tuple({type(cls) for cls in classes}) def cls_name(classes): return "_".join(mcls.__name__ for mcls in classes) @@ -26,19 +21,11 @@ def cls_name(classes): return metaclass(cls_name(classes), classes, {}) # class C -class _TranslationUtil: - """Ensures upon instantiation of the class we have the proper translation.""" - - def __new__(cls, *args, **kwargs): - set_trans_from_env() - return super().__new__(cls) - - -class _merged_meta(type(abc.ABC), type(_TranslationUtil)): # avoid metaclass conflict. +class _merged_meta(type(abc.ABC)): # avoid metaclass conflict. pass -class WidgetBase(abc.ABC, _TranslationUtil, metaclass=_merged_meta): +class WidgetBase(abc.ABC, metaclass=_merged_meta): """Base class for weldx widgets.""" def copy(self): @@ -83,7 +70,7 @@ def __init__(self, *args, **kwargs): layout.border = border_debug_style layout.margin = margin - super(WidgetMyHBox, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) class WidgetMyVBox(metaclass_resolver(VBox, WidgetBase)): @@ -98,7 +85,7 @@ def __init__(self, *args, **kwargs): layout.border = border_debug_style layout.margin = margin - super(WidgetMyVBox, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) class WidgetSimpleOutput(WidgetMyHBox): @@ -120,7 +107,7 @@ def __init__(self, out=None, height=None, width=None): else: layout = out.layout self.out = out - super(WidgetSimpleOutput, self).__init__(children=[self.out], layout=layout) + super().__init__(children=[self.out], layout=layout) def __enter__(self): """Enter.""" @@ -134,28 +121,10 @@ def __exit__(self, exc_type, exc_val, exc_tb): class WeldxImportExport(abc.ABC): """Abstract import and export interfaces for weldx data exchange.""" - @property - @abc.abstractmethod - def schema(self) -> str: - """Return a schema name is used to validate input and output.""" - pass - - @functools.lru_cache - def get_schema_path(self) -> Path: - """Resolve a schema name to path.""" - return get_schema_path(self.schema) - - def validate(self, tree): - """Validate given tree against schema of this class.""" - # should be implemented such that we can validate both input and output. - pass - @abc.abstractmethod def from_tree(self, tree: dict): """Fill the widget with given state dictionary.""" - pass @abc.abstractmethod def to_tree(self) -> dict: """Return a dict containing data from widget.""" - pass diff --git a/weldx_widgets/widget_evaluate.py b/weldx_widgets/widget_evaluate.py index e941d0e..e04328c 100644 --- a/weldx_widgets/widget_evaluate.py +++ b/weldx_widgets/widget_evaluate.py @@ -26,7 +26,6 @@ Trace, WeldxFile, ) -from weldx_widgets.translation_utils import _i18n as _ from weldx_widgets.widget_base import WidgetBase, WidgetSimpleOutput, metaclass_resolver from weldx_widgets.widget_factory import make_title from weldx_widgets.widget_measurement import WidgetMeasurement, WidgetMeasurementChain @@ -36,9 +35,9 @@ class WidgetProcessInfo(WidgetSimpleOutput): """Plot GMAW process parameter into output widget.""" def __init__(self, gmaw_process: GmawProcess, t, out=None): - super(WidgetProcessInfo, self).__init__(out=out) + super().__init__(out=out) - children = [make_title(_("Process parameters")), self.out] + children = [make_title("Process parameters"), self.out] self.children = children with self: @@ -98,27 +97,27 @@ def make_output(): tabs = defaultdict(make_output) - with tabs[_("ASDF-header")]: + with tabs["ASDF-header"]: display(file.header(False)) # start and end time of experiment t = (file["TCP"].time[[0, -1]]).as_timedelta() WidgetProcessInfo( - file["process"]["welding_process"], t, out=tabs[_("Process parameters")] + file["process"]["welding_process"], t, out=tabs["Process parameters"] ) groove = file["workpiece"]["geometry"]["groove_shape"] - with tabs[_("Specimen")]: - print("Material") - print(file["workpiece"]["base_metal"]["common_name"]) - print(file["workpiece"]["base_metal"]["standard"]) + with tabs["Specimen"]: + print("Material") # noqa: T201 + print(file["workpiece"]["base_metal"]["common_name"]) # noqa: T201 + print(file["workpiece"]["base_metal"]["standard"]) # noqa: T201 groove.plot() plt.show() seam_length = file["workpiece"]["geometry"]["seam_length"] - print(_("Seam length") + ":", seam_length) + print("Seam length:", seam_length) # noqa: T201 # 3D Geometry geometry = self._create_geometry(groove, seam_length, Q_(10, "mm")) @@ -158,7 +157,7 @@ def make_output(): spatial_data_geo_reduced, "workpiece geometry (reduced)", "workpiece" ) - with tabs[_("CSM-Subsystems")]: + with tabs["CSM-Subsystems"]: csm.plot_graph() plt.show() self._show_csm_subsystems(csm) @@ -219,9 +218,7 @@ def make_output(): self._compare_design_tcp(csm) plt.show() - super(WidgetEvaluateSinglePassWeld, self).__init__( - children=tuple(tabs.values()) - ) + super().__init__(children=tuple(tabs.values())) for i, key in enumerate(tabs.keys()): self.set_title(i, key) self.tabs = tabs @@ -231,7 +228,7 @@ def _show_csm_subsystems(csm): subsystems = csm.subsystems if not subsystems: return - print(csm.subsystem_names) + print(csm.subsystem_names) # noqa: T201 fig, ax = plt.subplots(ncols=len(subsystems)) for i, subsystem in enumerate(subsystems): subsystem.plot_graph(ax=ax[i]) @@ -252,18 +249,18 @@ def _compare_design_tcp(csm): # difference in welding speed fig, ax = plt.subplots(1, 2) ax[0].plot(time.m, tcp_diff.data[:, 0]) - ax[0].set_title(_("Difference in welding speed")) - ax[0].set_xlabel(_("time in s")) - ax[0].set_ylabel(_("diff in mm")) + ax[0].set_title("Difference in welding speed") + ax[0].set_xlabel("time in s") + ax[0].set_ylabel("diff in mm") # diffs depend on how well the user frame matches - ax[1].set_title(_("User frame deviation")) + ax[1].set_title("User frame deviation") ax[1].plot(time.m, tcp_diff.data[:, 1], label="y") ax[1].plot(time.m, tcp_diff.data[:, 2], label="z") ax[1].legend() - ax[1].set_xlabel(_("time in s")) - ax[1].set_ylabel(_("diff in mm")) + ax[1].set_xlabel("time in s") + ax[1].set_ylabel("diff in mm") @staticmethod def _welding_wire_geo_data(radius, length, cross_section_resolution=8): diff --git a/weldx_widgets/widget_factory.py b/weldx_widgets/widget_factory.py index 3141e1e..8ed3224 100644 --- a/weldx_widgets/widget_factory.py +++ b/weldx_widgets/widget_factory.py @@ -1,8 +1,7 @@ """Factory for commonly used widget elements.""" import contextlib -import ipywidgets as widgets -from ipywidgets import HTML, Label, Layout, Text +from ipywidgets import HTML, BoundedFloatText, Label, Layout, Text from traitlets import All, HasTraits from weldx import Q_, TimeSeries @@ -49,7 +48,7 @@ def __init__(self, label_text, prefilled_text=None): self.label = Label(label_text, layout=description_layout) self.text = Text(value=prefilled_text, layout=textbox_layout) children = [self.label, self.text] - super(WidgetLabeledTextInput, self).__init__(children=children) + super().__init__(children=children) @property def text_value(self) -> str: @@ -61,26 +60,26 @@ def text_value(self, value): self.text.value = value -class FloatWithUnit(WidgetMyHBox): +class WidgetFloatWithUnit(WidgetMyHBox): """Widget grouping a float with unit.""" def __init__(self, text, unit, value: float = 0.0, min=0): self._label = Label(text, layout=description_layout) - self._float = widgets.BoundedFloatText( + self._float = BoundedFloatText( value=value, min=min, max=2**32, layout=textbox_layout ) self._unit = Text(value=unit, placeholder="unit", layout=textbox_layout) - super(FloatWithUnit, self).__init__( + super().__init__( children=[self._label, self._float, self._unit], ) - def observe_float_value(self, handler, names=All, type="change"): # noqa + def observe_float_value(self, handler, names=All, type="change"): self._float.observe(handler, names, type) observe_float_value.__doc__ = HasTraits.observe.__doc__ - def observe_unit(self, handler, names=All, type="change"): # noqa + def observe_unit(self, handler, names=All, type="change"): self._unit.observe(handler, names, type) observe_unit.__doc__ = HasTraits.observe.__doc__ @@ -137,6 +136,4 @@ def as_time_series(self) -> TimeSeries: def make_title(text, heading_level=3): """Return an HTML formatted heading.""" - from weldx_widgets.translation_utils import _i18n as _ - - return HTML(f"{_(text)}") + return HTML(f"{text}") diff --git a/weldx_widgets/widget_gas.py b/weldx_widgets/widget_gas.py index fd354c4..b6ef176 100644 --- a/weldx_widgets/widget_gas.py +++ b/weldx_widgets/widget_gas.py @@ -6,10 +6,9 @@ from weldx import Q_ from weldx.tags.aws import GasComponent, ShieldingGasForProcedure, ShieldingGasType -from weldx_widgets.translation_utils import _i18n as _ from weldx_widgets.widget_base import WidgetMyVBox from weldx_widgets.widget_factory import ( - FloatWithUnit, + WidgetFloatWithUnit, button_layout, description_layout, ) @@ -38,19 +37,14 @@ def __init__(self, index=0, percentage=100): ) ) - button_add = Button(description=_("Add gas component")) + button_add = Button(description="Add gas component") button_add.on_click(self._add_gas_comp) - super(WidgetSimpleGasSelection, self).__init__(children=[button_add, gas]) + super().__init__(children=[button_add, gas]) def _set_gas_list(self): - self.gas_list = ["Argon", "CO2", "Helium", _("Hydrogen"), _("Oxygen")] - self._mapping = bidict( - { - gui_name: _asdf_name - for gui_name, _asdf_name in zip(self.gas_list, self._asdf_gas_names) - } - ) + self.gas_list = ["Argon", "CO2", "Helium", "Hydrogen", "Oxygen"] + self._mapping = bidict(dict(zip(self.gas_list, self._asdf_gas_names))) def _clear(self): self.children = [self.children[0]] @@ -66,13 +60,13 @@ def _create_gas_dropdown(self, index=0, percentage=100): ) percentage = IntSlider( - start=0, end=100, value=percentage, description=_("percentage") + start=0, end=100, value=percentage, description="percentage" ) percentage.observe(self._check, type="change") self.gas_selection = gas_dropdown - button_del = Button(description=_("delete"), layout=button_layout) + button_del = Button(description="delete", layout=button_layout) box = HBox((gas_dropdown, percentage, button_del)) @@ -108,9 +102,7 @@ def _check(self, value): gas_components = self.to_tree()["gas_component"] if not sum(g.gas_percentage for g in gas_components) == 100: with self.out: - print( - _("Check percentages, all components should sum up to 100!") - ) # , file=sys.stderr) + print("Check percentages, all components should sum up to 100!") # noqa: T201 else: # remove output, if everything is alright. self.out.clear_output() @@ -148,11 +140,11 @@ class WidgetShieldingGas(WidgetMyVBox): # TODO: this could in principle be used multiple times for all positions # e.g. torch, trailing, backing def __init__(self, position="torch"): - self.flowrate = FloatWithUnit(_("Flow rate"), "l/min", value=20) + self.flowrate = WidgetFloatWithUnit("Flow rate", "l/min", value=20) self.gas_components = WidgetSimpleGasSelection() children = [self.gas_components, self.flowrate] - super(WidgetShieldingGas, self).__init__(children=children) + super().__init__(children=children) def to_tree(self) -> dict: """Return weldx objects describing the shielding gas.""" diff --git a/weldx_widgets/widget_gmaw.py b/weldx_widgets/widget_gmaw.py index 9047552..663d694 100644 --- a/weldx_widgets/widget_gmaw.py +++ b/weldx_widgets/widget_gmaw.py @@ -9,10 +9,9 @@ from weldx import Q_, GmawProcess, Time, TimeSeries from weldx_widgets import WidgetShieldingGas from weldx_widgets.generic import WidgetTimeSeries -from weldx_widgets.translation_utils import _i18n as _ from weldx_widgets.widget_base import WeldxImportExport, WidgetMyVBox from weldx_widgets.widget_factory import ( - FloatWithUnit, + WidgetFloatWithUnit, WidgetLabeledTextInput, make_title, ) @@ -39,7 +38,7 @@ def plot_gmaw(gmaw, t): fig, ax = plt.subplots(nrows=n, sharex="all", figsize=(_DEFAULT_FIGWIDTH, 2 * n)) for i, k in enumerate(pars): parplot(pars[k], t, k, ax[i]) - ax[-1].set_xlabel(_("time") + " / s") + ax[-1].set_xlabel("time / s") ax[0].set_title(title, loc="left") # ipympl_style(fig) @@ -59,17 +58,17 @@ def __init__(self, tag: str, meta=None): self.tag = tag self.meta = meta - self.manufacturer = WidgetLabeledTextInput(_("Manufacturer"), "Fronius") - self.power_source = WidgetLabeledTextInput(_("Power source"), "TPS 500i") - self.wire_feedrate = FloatWithUnit( - text=_("Wire feed rate"), value=10, min=0, unit="m/min" + self.manufacturer = WidgetLabeledTextInput("Manufacturer", "Fronius") + self.power_source = WidgetLabeledTextInput("Power source", "TPS 500i") + self.wire_feedrate = WidgetFloatWithUnit( + text="Wire feed rate", value=10, min=0, unit="m/min" ) children = [ self.manufacturer, self.power_source, self.wire_feedrate, ] - super(BaseProcess, self).__init__(children=children) + super().__init__(children=children) def to_tree(self): """Return base process parameters.""" @@ -99,34 +98,36 @@ class ProcessPulsed(WidgetMyVBox): """Widget for pulsed processes.""" def __init__(self, kind="UI"): - self.pulse_duration = FloatWithUnit(_("Pulse duration"), value=5.0, unit="ms") - self.pulse_frequency = FloatWithUnit( - _("Pulse frequency"), value=100.0, unit="Hz" + self.pulse_duration = WidgetFloatWithUnit( + "Pulse duration", value=5.0, unit="ms" ) - self.base_current = FloatWithUnit(_("Base current"), value=60.0, unit="A") + self.pulse_frequency = WidgetFloatWithUnit( + "Pulse frequency", value=100.0, unit="Hz" + ) + self.base_current = WidgetFloatWithUnit("Base current", value=60.0, unit="A") if kind == "UI": - self.pulsed_dim = FloatWithUnit(_("Pulse voltage"), "V", 40) + self.pulsed_dim = WidgetFloatWithUnit("Pulse voltage", "V", 40) elif kind == "II": - self.pulsed_dim = FloatWithUnit(_("Pulse current"), "A", 300) + self.pulsed_dim = WidgetFloatWithUnit("Pulse current", "A", 300) else: raise ValueError(f"unknown kind: {kind}") self.kind = kind self.base_process = BaseProcess("CLOOS/pulse", {"modulation": self.kind}) if self.kind == "UI": - desc = _("voltage/current") + desc = "voltage/current" else: - desc = _("current") + desc = "current" children = [ - make_title(_("Pulsed") + f" {desc} " + _("process parameters")), + make_title(f"Pulsed {desc} process parameters"), self.base_process, self.pulse_duration, self.pulse_frequency, self.base_current, self.pulsed_dim, ] - super(ProcessPulsed, self).__init__(children=children) + super().__init__(children=children) def to_tree(self): """Return pulsed process parameters.""" @@ -182,12 +183,12 @@ def __init__(self): self.voltage = WidgetTimeSeries( base_data="40.0, 20.0", base_unit="V", time_data="0.0, 10.0", time_unit="s" ) - self.impedance = FloatWithUnit(text=_("Impedance"), value=10, unit="percent") - self.characteristic = FloatWithUnit(_("Characteristic"), value=5, unit="V/A") + self.impedance = WidgetFloatWithUnit(text="Impedance", value=10, unit="percent") + self.characteristic = WidgetFloatWithUnit("Characteristic", value=5, unit="V/A") - super(ProcessSpray, self).__init__( + super().__init__( children=[ - make_title(_("Spray process parameters")), + make_title("Spray process parameters"), self.base_process, self.voltage, self.impedance, @@ -230,18 +231,18 @@ class WidgetWire(WidgetMyVBox): heading_level = 4 def __init__(self): - self.diameter = FloatWithUnit(_("Diameter"), unit="mm", min=0, value=1.2) - self.wire_class = WidgetLabeledTextInput(_("Class"), "G 42 2 C/M G4Si1") + self.diameter = WidgetFloatWithUnit("Diameter", unit="mm", min=0, value=1.2) + self.wire_class = WidgetLabeledTextInput("Class", "G 42 2 C/M G4Si1") # TODO: consider a tree like editing widget for metadata. - self.metadata = WidgetLabeledTextInput(_("Metadata"), "") + self.metadata = WidgetLabeledTextInput("Metadata", "") children = [ - make_title(_("Wire parameters"), heading_level=WidgetWire.heading_level), + make_title("Wire parameters", heading_level=WidgetWire.heading_level), self.diameter, self.wire_class, self.metadata, ] - super(WidgetWire, self).__init__(children=children) + super().__init__(children=children) def to_tree(self): """Return welding wire parameters.""" @@ -266,25 +267,20 @@ class WidgetGMAW(WidgetMyVBox, WeldxImportExport): def _set_gui_mapping(self): self.translate = bidict( { - _("Spray"): "spray", - _("Pulsed") + " (UI)": "UI", - _("Pulsed") + " (II)": "II", + "Spray": "spray", + "Pulsed (UI)": "UI", + "Pulsed (II)": "II", # _("CMT"): NotImplemented, } ) - @property - def schema(self) -> str: - """Return schema.""" - raise - def __init__(self, process_type="spray"): self._set_gui_mapping() # set up translation mapping. index = list(self.translate.values()).index(process_type) self.process_type = Dropdown( options=list(self.translate.keys()), index=index, - description=_("Process type"), + description="Process type", ) self.process_type.observe(self._create_process_widgets, names="value") self.gas = WidgetShieldingGas() @@ -292,8 +288,8 @@ def __init__(self, process_type="spray"): self.welding_wire = WidgetWire() children = [ - make_title(_("GMAW process parameters")), - make_title(_("Shielding gas"), 4), + make_title("GMAW process parameters"), + make_title("Shielding gas", 4), self.gas, self.welding_wire, # self.weld_speed, # TODO: speed is given by feedrate and groove! @@ -301,7 +297,7 @@ def __init__(self, process_type="spray"): self.process_type, self._welding_process, ] - super(WidgetGMAW, self).__init__(children=children) + super().__init__(children=children) # initially create the selected process type self._create_process_widgets(dict(new=self.process_type.value)) @@ -309,7 +305,7 @@ def __init__(self, process_type="spray"): def _create_process_widgets(self, change): new = change["new"] arg = self.translate[new] - box = self._cached_process_widgets(arg) + box = WidgetGMAW._cached_process_widgets(arg) self._welding_process.children = (box,) @property @@ -317,8 +313,9 @@ def welding_process(self) -> Union[ProcessSpray, ProcessPulsed]: """Return welding process widget.""" return self._welding_process.children[0] + @staticmethod @lru_cache(None) - def _cached_process_widgets(self, process): + def _cached_process_widgets(process): if process == "spray": return ProcessSpray() @@ -332,12 +329,12 @@ def from_tree(self, tree: dict): # set the right welding process widget if welding_process.base_process == "pulse": kind = welding_process.meta["modulation"] - process_type = _("Pulsed") + f" ({kind})" + process_type = f"Pulsed ({kind})" elif welding_process.base_process == "spray": - process_type = _("Spray") + process_type = "Spray" else: raise NotImplementedError( - _("unknown process type") + f"{welding_process.base_process}" + f"unknown process type: {welding_process.base_process}" ) self.process_type.value = process_type diff --git a/weldx_widgets/widget_groove_sel.py b/weldx_widgets/widget_groove_sel.py index ccb37eb..8d91631 100644 --- a/weldx_widgets/widget_groove_sel.py +++ b/weldx_widgets/widget_groove_sel.py @@ -23,10 +23,9 @@ get_groove, ) from weldx_widgets.generic import download_button -from weldx_widgets.translation_utils import _i18n as _ from weldx_widgets.widget_base import WeldxImportExport, WidgetMyHBox, WidgetMyVBox from weldx_widgets.widget_factory import ( - FloatWithUnit, + WidgetFloatWithUnit, WidgetLabeledTextInput, description_layout, make_title, @@ -52,11 +51,10 @@ class WidgetCADExport(WidgetMyVBox): does nothing. """ - data_formats = ( - [ # ".stl", # FIXME: for some reason there is a div by zero error in meshio - ".ply" - ] - ) # same groove is fine in ply format... + data_formats = [ + ".ply", + # ".stl", # FIXME: for some reason there is a div by zero error in meshio + ] # same groove is fine in ply format... """ example trace for a simple vgroove: meshio/stl/_stl.py in write(filename, mesh, binary) 203 normals = np.cross(pts[:, 1] - pts[:, 0], pts[:, 2] - pts[:, 0]) @@ -69,7 +67,7 @@ class WidgetCADExport(WidgetMyVBox): """ def __init__(self): - title = make_title(_("Export geometry to CAD file [optional]"), heading_level=4) + title = make_title("Export geometry to CAD file [optional]", heading_level=4) # if the format changes, we have to update the file_pattern mask # of the chooser of the save widget. @@ -77,9 +75,9 @@ def __init__(self): self.format = Dropdown( options=WidgetCADExport.data_formats, index=default_format_index, - description=_("Data format"), + description="Data format", ) - self.create_btn = Button(description=_("Create program")) + self.create_btn = Button(description="Create program") self.geometry = None # disable button initially, because we first need to have a geometry self.create_btn.disabled = True @@ -89,14 +87,14 @@ def __init__(self): # program directly to his/her computer. self._html_dl_button = HTML() - self.profile_raster_width = FloatWithUnit( - _("Profile raster width"), + self.profile_raster_width = WidgetFloatWithUnit( + "Profile raster width", value=2, unit="mm", # tooltip="Target distance between the individual points of a profile", ) - self.trace_raster_width = FloatWithUnit( - _("Trace raster width"), + self.trace_raster_width = WidgetFloatWithUnit( + "Trace raster width", value=30, unit="mm", # tooltip="Target distance between the individual profiles on the trace", @@ -123,7 +121,7 @@ def geometry(self, value): if value is not None: self.create_btn.disabled = False - def _on_export_geometry(self, *args): + def _on_export_geometry(self, *_): if self.geometry is None: return ext = self.format.value @@ -143,7 +141,7 @@ def _on_export_geometry(self, *args): # read and embed in HTML button. ntf.seek(0) download_button( - button_description=_("Download program"), + button_description="Download program", filename=f"specimen{ext}", html_instance=self._html_dl_button, content=ntf.read(), @@ -154,11 +152,11 @@ class WidgetMetal(WidgetMyVBox): """Widget to select metal type and parameters.""" def __init__(self): - self.common_name = WidgetLabeledTextInput(_("Common name"), "S355J2+N") - self.standard = WidgetLabeledTextInput(_("Standard"), "DIN EN 10225-2:2011") - self.thickness = FloatWithUnit(_("Thickness"), value=30, unit="mm") + self.common_name = WidgetLabeledTextInput("Common name", "S355J2+N") + self.standard = WidgetLabeledTextInput("Standard", "DIN EN 10225-2:2011") + self.thickness = WidgetFloatWithUnit("Thickness", value=30, unit="mm") children = [ - make_title(_("Base metal"), heading_level=4), + make_title("Base metal", heading_level=4), self.common_name, self.standard, self.thickness, @@ -219,7 +217,7 @@ def __init__(self): [ WidgetMyHBox( [ - Label(_("Groove type"), layout=description_layout), + Label("Groove type", layout=description_layout), self.groove_type_dropdown, ] ), @@ -232,9 +230,9 @@ def __init__(self): self.output_tabs = Tab() self.output_tabs.layout = Layout(width="70%") self.output_tabs.children = [self.out] - self.output_tabs.set_title(0, "2D " + _("profile")) + self.output_tabs.set_title(0, "2D profile") children = [ - make_title(_("ISO 9692-1 Groove selection"), 3), + make_title("ISO 9692-1 Groove selection", 3), WidgetMyHBox(children=[self.groove_selection, self.output_tabs]), ] @@ -259,20 +257,15 @@ def groove_obj(self, value: IsoBaseGroove): gui_params = self.groove_params_dropdowns # inhibit notifications during updating the parameters (or we would call the - # _update_plot method for every setting! + # _update_plot method for every setting!) with contextlib.ExitStack() as stack: for k, v in self.groove_obj.parameters().items(): mapped_k = self._groove_obj._mapping[k] - widget: FloatWithUnit = gui_params[mapped_k] + widget: WidgetFloatWithUnit = gui_params[mapped_k] stack.enter_context(widget.silence_events()) widget.quantity = v - @property - def schema(self) -> str: - """Return schema.""" - raise NotImplementedError - def from_tree(self, tree: dict): """Fill widget from tree.""" self.groove_obj = tree["groove_shape"] @@ -320,24 +313,30 @@ def _create_groove_dropdown(self): layout=description_layout, ) param_widgets[item] = HBox( - [Label(_("Code number"), layout=description_layout), dropdown] + [Label("Code number", layout=description_layout), dropdown] ) else: # replace underscores with spaces, first letter uppercase, translate. t = f"{(item[0].upper() + item[1:]).replace('_', ' ')}" matches = re.match("(.*)([0-9]+)", t) if matches: - t = _(matches.group(1)) + t = matches.group(1) number = matches.group(2) text = f"{t} {number}" else: - text = _(t) + text = t if "angle" in item: - param_widgets[item] = FloatWithUnit(text=text, unit="°", value=45) + param_widgets[item] = WidgetFloatWithUnit( + text=text, unit="°", value=45 + ) elif "workpiece_thickness" in item: - param_widgets[item] = FloatWithUnit(text=text, unit="mm", value=15) + param_widgets[item] = WidgetFloatWithUnit( + text=text, unit="mm", value=15 + ) else: - param_widgets[item] = FloatWithUnit(text=text, unit="mm", value=5) + param_widgets[item] = WidgetFloatWithUnit( + text=text, unit="mm", value=5 + ) param_widgets[item].mapping = item groove_list = list(_groove_name_to_type.keys()) @@ -357,7 +356,7 @@ def _create_groove_dropdown(self): def add_parameter_observer(self, observer: Callable): """Add observers to groove parameters.""" - for key, box in self.groove_params_dropdowns.items(): + for _key, box in self.groove_params_dropdowns.items(): box.children[1].observe(observer, "value") # if key != "code_number": # box.children[2].observe(observer, "value") @@ -369,7 +368,7 @@ def _update_plot(self, *args): groove_params = dict(groove_type=groove_type) for child in self.groove_params.children: param_key = child.mapping - if param_key == _("code_number"): + if param_key == "code_number": groove_params[param_key] = child.children[1].value else: magnitude = child.children[1].value @@ -412,19 +411,21 @@ def __init__(self): self.last_plot: Optional[CoordinateSystemManagerVisualizerK3D] = None self.groove_sel = WidgetGrooveSelection() - self.seam_length = FloatWithUnit(_("Seam length"), value=300, min=0, unit="mm") + self.seam_length = WidgetFloatWithUnit( + "Seam length", value=300, min=0, unit="mm" + ) self.seam_length.observe_float_value(self.create_csm_and_plot) self.seam_length.observe_unit(self.create_csm_and_plot) - self.tcp_y = FloatWithUnit("TCP-y", unit="mm") - self.tcp_z = FloatWithUnit("TCP-z", unit="mm") + self.tcp_y = WidgetFloatWithUnit("TCP-y", unit="mm") + self.tcp_z = WidgetFloatWithUnit("TCP-z", unit="mm") # TODO: compute weld speed accordingly to chosen groove area! # TODO: consider setting it read-only?? - self.weld_speed = FloatWithUnit(_("weld speed"), value=6, unit="mm/s") + self.weld_speed = WidgetFloatWithUnit("weld speed", value=6, unit="mm/s") self.base_metal = WidgetMetal() self.geometry_export = WidgetCADExport() self.additional_params = ( - make_title(_("Welding parameters"), 4), + make_title("Welding parameters", 4), self.seam_length, self.weld_speed, self.tcp_y, @@ -437,7 +438,7 @@ def __init__(self): # add 3d plot and CAD export to groove_sel output tab self.out = Output() self.groove_sel.output_tabs.children += (self.out, self.geometry_export) - self.groove_sel.output_tabs.set_title(1, "3D " + _("profile")) + self.groove_sel.output_tabs.set_title(1, "3D profile") self.groove_sel.output_tabs.set_title(2, "CAD export") self.groove_sel.output_tabs.observe( @@ -452,9 +453,7 @@ def __init__(self): self.groove_sel, ] - super(WidgetGrooveSelectionTCPMovement, self).__init__( - children=children, layout=Layout(width="100%") - ) + super().__init__(children=children, layout=Layout(width="100%")) def create_csm_and_plot(self, change=None, plot=True, **kwargs): """Create coordinates system manager containing TCP movement.""" diff --git a/weldx_widgets/widget_measurement.py b/weldx_widgets/widget_measurement.py index f2d9c1d..68cb1ea 100644 --- a/weldx_widgets/widget_measurement.py +++ b/weldx_widgets/widget_measurement.py @@ -5,7 +5,6 @@ import weldx from weldx.constants import WELDX_UNIT_REGISTRY as ureg -from weldx_widgets.translation_utils import _i18n as _ from weldx_widgets.widget_base import WidgetSimpleOutput from weldx_widgets.widget_factory import make_title @@ -34,7 +33,7 @@ def plot_signal(signal: weldx.measurement.Signal, name, limits=None, ax=None): ax.plot(time.m, data.data.m) ax.set_ylabel(f"{name} / {ureg.Unit(signal.units):~}") - ax.set_xlabel(_("time") + " / s") + ax.set_xlabel("time / s") ax.grid() if limits is not None: @@ -54,8 +53,8 @@ def plot_measurements( plot_signal(last_signal, measurement.name, ax=axes[i], limits=limits) axes[i].set_xlabel(None) - axes[-1].set_xlabel(_("time") + " / s") - axes[0].set_title(_("Measurements")) + axes[-1].set_xlabel("time / s") + axes[0].set_title("Measurements") return axes @@ -63,7 +62,7 @@ class WidgetMeasurement(WidgetSimpleOutput): """Widget to wrap around a measurement.""" def __init__(self, measurements: List["weldx.measurement.Measurement"], out=None): - super(WidgetMeasurement, self).__init__(out=out) + super().__init__(out=out) n = len(measurements) @@ -83,7 +82,7 @@ class WidgetMeasurementChain(WidgetSimpleOutput): """Plot measurement chains into output widget.""" def __init__(self, measurements, out=None): - super(WidgetMeasurementChain, self).__init__(out=out) + super().__init__(out=out) with self: fig, ax = plt.subplots( nrows=len(measurements), figsize=(_DEFAULT_FIGWIDTH, 18) @@ -93,4 +92,4 @@ def __init__(self, measurements, out=None): plt.tight_layout() plt.show() - self.children = [make_title(_("Measurement chain")), self.out] + self.children = [make_title("Measurement chain"), self.out]