diff --git a/README.md b/README.md index 068d9f219ff29d1a7f83e87f6c0f9c095aa575e1..3ea34a4a9302dc1d0a2416ec7beb23955f310025 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,10 @@ SNLO-Helper helps to use [SNLO](https://as-photonics.com/products/snlo/) softwar An autoclicker clicks different buttons and fills fields in order to automate SNLO simulations. Afterwards, it can retrieve the results and return them as a dictionary. -Note that the script does use your mouse and keyboard, so you should not interact with the computer at the same time. -The autoclicker can be interrupted by moving the mouse into the top right corner of the screen. +Attention: +- The script does use your mouse and keyboard, so you should not interact with the computer at the same time. +- The script uses predefined positions of the windows, so **do not move the windows**. +- The autoclicker can be interrupted by moving the mouse into the top left corner of the screen. ## Installation @@ -19,10 +21,10 @@ Install it executing `pip install -e .` in this folder or via `pip install git+h ### Quick Start 1. Start SNLO on your computer -2. Import `snlohelper.snlo` as a starting point. -3. If your screen resolution differs from HD, you have to set screenfactors with `utils.set_screenfactors`. - That will rescale all positions to your current screen resolution. -4. Open the desired method and execute it. +2. Import `snlohelper.main_window.MainWindow` as a starting point. +3. Create an instande `mw = MainWindow` +4. Open the desired function: `ri = mw.open_function(Functions.REF_INDEX)` +5. Execute it `no, ne = ri.refractive_indices(Wavelength=1234)` Here is a small snippet how to do a 2D mix of long pulses: ``` @@ -42,9 +44,9 @@ For more examples see the `examples` folder. ### General usage -* The `main_window.open_function` method allows to open any SNLO function of the main window. +* The `main_window.MainWindow` class manages the main window. * For several functions exists a module containing a class, which in turn allows to configure the function, to run the calculation, and to extract the result. - 1. You start that class, for example `mix = two_d_mix_lp.TwoDMixLp()`. + 1. You start that class, for example `mix = two_d_mix_lp.TwoDMixLp()` or `mix = MainWindow().open_function("2D-Mix-LP")`. 2. You can configure it giving a configuration dictionary (the keys correspond to the names) with `mix.configure({"Wavelengths": [1064, None, None]})` 3. You can run it with `mix.run()` 4. With `results = mix.read_results()` you can extract the resulting text. diff --git a/snlohelper/base_function.py b/snlohelper/base_function.py index 8c38479128ca96bd6c8ff6b4889ff24946caac9e..0e55cc4e31edc2369fa896f2cbf6e7b3b636894b 100644 --- a/snlohelper/base_function.py +++ b/snlohelper/base_function.py @@ -2,7 +2,7 @@ import time from typing import Any, Optional, Protocol from .utils import Point, gui, scale, get_content_complete, set_value -from .main_window import Functions, open_function +from .functions import Functions, open_function class BaseFunction(Protocol): diff --git a/snlohelper/focus.py b/snlohelper/focus.py index 53f8826f4c325ad43bfe992cfb394d9d8b88d74a..f4deaacb003db07ddb26179cc90f1505ccd1c2ee 100644 --- a/snlohelper/focus.py +++ b/snlohelper/focus.py @@ -1,4 +1,7 @@ -from .main_window import open_function, Functions +from typing import Optional + +from .base_function import BaseFunction +from .functions import open_function, Functions from .utils import Point, gui, scale, set_value, get_value_complete @@ -41,3 +44,37 @@ def focus( radcurv = get_value_complete(_dict_focus["Radius of curv. (mm)"]) angle = get_value_complete(_dict_focus["Far field ang air (mrad)"]) return zr, diameter, radcurv, angle + + +class Focus(BaseFunction): + _function = Functions.FOCUS + + def __init__(self) -> None: + super().__init__() + self._configuration_pos = {key: [value] for key, value in _dict_focus.items()} + + def read_results(self) -> list[str]: + return super().read_results() + + def focus( + self, + wavelength_nm: Optional[float] = None, + ref_index: Optional[float] = None, + fwhm_mm: Optional[float] = None, + focus_pos_mm: Optional[float] = None, + ) -> tuple[float, float, float, float]: + self.configure( + { + "Wavelength (nm)": wavelength_nm, + "Refractive Index": ref_index, + "Waist size (mm)": fwhm_mm, + # Set face to focus and dist to focus to same value gives values in air at face + "Face to focus (mm)": focus_pos_mm, + "Dist. to focus (mm)": focus_pos_mm, + } + ) + zr = get_value_complete(_dict_focus["Rayleigh z in xtal (mm)"]) + diameter = get_value_complete(_dict_focus["Beam size (mm)"]) + radcurv = get_value_complete(_dict_focus["Radius of curv. (mm)"]) + angle = get_value_complete(_dict_focus["Far field ang air (mrad)"]) + return zr, diameter, radcurv, angle diff --git a/snlohelper/functions.py b/snlohelper/functions.py new file mode 100644 index 0000000000000000000000000000000000000000..ae7c0144bfe7840f10d0c32970ab5a605c5d1042 --- /dev/null +++ b/snlohelper/functions.py @@ -0,0 +1,53 @@ +from enum import StrEnum + +from .utils import Point, gui, scale + + +# coordinates of the functions (in FHD standard) +_functions_coord: dict[str, Point] = { + "Ref. Ind.": (66, 46), + "Qmix": (66, 66), + "Bmix": (66, 93), + "QPM": (66, 120), + "Opoangles": (66, 146), + "Ncpm": (66, 173), + "GVM": (66, 200), + "PW-mix-LP": (66, 233), + "PW-mix-SP": (66, 260), + "PW-mix-BB": (66, 286), + "2D-mix-LP": (66, 313), + "2D-mix-SP": (66, 340), + "PW-cav-LP": (66, 366), + "PW-OPO-SP": (66, 393), + "PW-OPO-BB": (66, 420), + "2D-cav-LP": (66, 446), + "Focus": (66, 473), + "Cavity": (66, 500), +} + + +class Functions(StrEnum): + """Enum for the functions.""" + + REF_INDEX = "Ref. Ind." + QMIX = "Qmix" + BMIX = "Bmix" + QPM = "QPM" + OPO_ANGLES = "Opoangles" + NCPM = "Ncpm" + GVM = "GVM" + PW_MIX_LP = "PW-mix-LP" + PW_MIX_SP = "PW-mix-SP" + PW_MIX_BB = "PW-mix-BB" + TWOD_MIX_LP = "2D-mix-LP" + TWOD_MIX_SP = "2D-mix-SP" + PW_CAV_LP = "PW-cav-LP" + PW_OPO_SP = "PW-OPO-SP" + PW_OPO_BB = "PW-OPO-BB" + TWOD_CAV_LP = "2D-cav-LP" + FOCUS = "Focus" + CAVITY = "Cavity" + +def open_function(key: str | Functions) -> None: + """opens function according to key""" + gui.click(*scale(*_functions_coord[key])) diff --git a/snlohelper/main_window.py b/snlohelper/main_window.py index 8a79ccdb017134a6ec8e7690ba4361d038cc5bf9..e5b40825db5d88023aad4c9cede8dac80d7f63f9 100644 --- a/snlohelper/main_window.py +++ b/snlohelper/main_window.py @@ -2,60 +2,38 @@ The SNLO main window """ -from enum import StrEnum - -from .utils import Point, gui, scale - -# coordinates of the functions (in FHD standard) -_functions_coord: dict[str, Point] = { - "Ref. Ind.": (66, 46), - "Qmix": (66, 66), - "Bmix": (66, 93), - "QPM": (66, 120), - "Opoangles": (66, 146), - "Ncpm": (66, 173), - "GVM": (66, 200), - "PW-mix-LP": (66, 233), - "PW-mix-SP": (66, 260), - "PW-mix-BB": (66, 286), - "2D-mix-LP": (66, 313), - "2D-mix-SP": (66, 340), - "PW-cav-LP": (66, 366), - "PW-OPO-SP": (66, 393), - "PW-OPO-BB": (66, 420), - "2D-cav-LP": (66, 446), - "Focus": (66, 473), - "Cavity": (66, 500), +from typing import Optional + +from .utils import gui, scale, get_screenfactors, set_screenfactors, Point +from .functions import Functions, open_function +from .base_function import BaseFunction +from .ref_index import RefractiveIndex +from .focus import Focus +from .two_d_mix_lp import TwoDMixLP +from .two_d_mix_sp import TwoDMixSP + + +function_classes = { + Functions.REF_INDEX: RefractiveIndex, + Functions.TWOD_MIX_LP: TwoDMixLP, + Functions.TWOD_MIX_SP: TwoDMixSP, + Functions.FOCUS: Focus, } -class Functions(StrEnum): - """Enum for the functions.""" - - REF_INDEX = "Ref. Ind." - QMIX = "Qmix" - BMIX = "Bmix" - QPM = "QPM" - OPO_ANGLES = "Opoangles" - NCPM = "Ncpm" - GVM = "GVM" - PW_MIX_LP = "PW-mix-LP" - PW_MIX_SP = "PW-mix-SP" - PW_MIX_BB = "PW-mix-BB" - TWOD_MIX_LP = "2D-mix-LP" - TWOD_MIX_SP = "2D-mix-SP" - PW_CAV_LP = "PW-cav-LP" - PW_OPO_SP = "PW-OPO-SP" - PW_OPO_BB = "PW-OPO-BB" - TWOD_CAV_LP = "2D-cav-LP" - FOCUS = "Focus" - CAVITY = "Cavity" - - -def open_function(key: str | Functions) -> None: - """opens function according to key""" - gui.click(*scale(*_functions_coord[key])) - - -def close_snlo() -> None: - gui.click(*scale(95, 14)) +class MainWindow: + _close_pos = (95, 14) + + def __init__(self, screenfactors: Optional[Point] = None, **kwargs) -> None: + super().__init__(**kwargs) + sf = get_screenfactors() + if sf is None or screenfactors is not None: + set_screenfactors(new_factors=screenfactors) + + def close(self) -> None: + gui.click(*scale(self._close_pos)) + + def open_function(self, key: str | Functions) -> Optional[BaseFunction]: + open_function(key) + if key in function_classes: + return function_classes[key]() diff --git a/snlohelper/ref_index.py b/snlohelper/ref_index.py index 6432588abf483e4723ffa7ede8e27c45bfe1134c..07476dec246bf17a69f5c8dbe55a88fd9f2b941b 100644 --- a/snlohelper/ref_index.py +++ b/snlohelper/ref_index.py @@ -28,6 +28,12 @@ class RefractiveIndex(BaseFunction): def refractive_indices( self, Crystal=None, Temperature=None, theta=None, phi=None, Wavelength=None ) -> list[float]: + """Get the refractive indices (o, e). + + For the crystal, you have to use letters to press, for example "BB" to select the second + crystal starting with a "B". In order to ensure, that it starts correctly, use any other + letter first, e.g. "ABB". + """ kwargs = { "Crystal": Crystal, "Temperature": Temperature, diff --git a/snlohelper/utils.py b/snlohelper/utils.py index a409c47f6e09c4c240867514040604f74f3e640c..e48775c5cee442e9c1df667b28d0fe98209cf9ec 100644 --- a/snlohelper/utils.py +++ b/snlohelper/utils.py @@ -28,8 +28,8 @@ screen resolution. """ -def get_screenfactors(standard: Point = (1920, 1080)) -> Point: - """Get the scaling factor from Full HD to the current display resolution.""" +def read_display_screenfactors(standard: Point = (1920, 1080)) -> Point: + """Read the scaling factor from Full HD to the current display resolution.""" width, height = gui.size() return standard[0] / width, standard[1] / height @@ -37,11 +37,20 @@ def get_screenfactors(standard: Point = (1920, 1080)) -> Point: def set_screenfactors(new_factors: Optional[tuple[float, float]] = None) -> tuple[float, float]: """Set the screenfactors to `new_factors` or detect them automatically.""" global factors - factors = get_screenfactors() if new_factors is None else new_factors + factors = read_display_screenfactors() if new_factors is None else new_factors return factors -def scale(x: float | Point, y: float | None = None) -> Point: +def get_screenfactors() -> Optional[tuple[float, float]]: + """Get the current screenfactors or None, if not yet set.""" + global factors + try: + return factors + except NameError: + return None + + +def scale(x: Union[float, Point], y: Optional[float] = None) -> Point: """Scale coordinates from the definition standard to the current screen.""" global factors if isinstance(x, (list, tuple)): @@ -110,3 +119,11 @@ def set_value(position: Point, value: Any) -> None: gui.press("delete") gui.doubleClick() gui.write(str(value)) + + +def alt_tab() -> None: + gui.hotkey("alt", "tab") + + +def get_position() -> Point: + return gui.position()