Skip to content
Snippets Groups Projects
Commit 8e7b9a84 authored by Christoph Schmidt's avatar Christoph Schmidt
Browse files

Updated to version 1.0.1

Logger can be set now. The handler of the logger is now defined during init.
parent f7d757cd
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.12 (confighandler)" jdkType="Python SDK" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="PackageRequirementsSettings"> <component name="PackageRequirementsSettings">
......
...@@ -13,9 +13,9 @@ import confighandler as cfg ...@@ -13,9 +13,9 @@ import confighandler as cfg
class SecondConfig(cfg.ConfigNode): class SecondConfig(cfg.ConfigNode):
def __init__(self, enable_log=True) -> None: def __init__(self, internal_log=True) -> None:
# Call the base class (important!) # Call the base class (important!)
super().__init__(enable_log=enable_log) super().__init__(internal_log=internal_log)
# Some fields # Some fields
# Create a field of type int. Set a default value, a friendly name and a description # Create a field of type int. Set a default value, a friendly name and a description
...@@ -27,9 +27,9 @@ class SecondConfig(cfg.ConfigNode): ...@@ -27,9 +27,9 @@ class SecondConfig(cfg.ConfigNode):
class ApplicationConfig(cfg.ConfigNode): class ApplicationConfig(cfg.ConfigNode):
def __init__(self, enable_log=True) -> None: def __init__(self, internal_log=True) -> None:
# Call the base class (important!) # Call the base class (important!)
super().__init__(enable_log=enable_log) super().__init__(internal_log=internal_log)
# Some fields # Some fields
# Create a field of type int. Set a default value, a friendly name and a description # Create a field of type int. Set a default value, a friendly name and a description
......
...@@ -34,14 +34,16 @@ has three advantages: ...@@ -34,14 +34,16 @@ has three advantages:
1. You can define the type of the fields. You can give them friendly names and descriptions. 1. You can define the type of the fields. You can give them friendly names and descriptions.
2. The parsing of the config file is much easier, because the parser knows the type of the fields. 2. The parsing of the config file is much easier, because the parser knows the type of the fields.
3. When working with this library, you can use the auto completion of your IDE, since it is a class. 3. When working with this library, you can use the auto completion of your IDE, since it is a class.
```python ```python
import confighandler as cfg import confighandler as cfg
class SecondConfig(cfg.ConfigNode): class SecondConfig(cfg.ConfigNode):
def __init__(self, enable_log=True) -> None: def __init__(self, enable_log=True) -> None:
# Call the base class (important!) # Call the base class (important!)
super().__init__(enable_log=enable_log) super().__init__(internal_log=enable_log)
# Some fields # Some fields
# Create a field of type int. Set a default value, a friendly name and a description # Create a field of type int. Set a default value, a friendly name and a description
...@@ -49,11 +51,13 @@ class SecondConfig(cfg.ConfigNode): ...@@ -49,11 +51,13 @@ class SecondConfig(cfg.ConfigNode):
friendly_name="My Test Int", friendly_name="My Test Int",
description="This is just an integer") description="This is just an integer")
# #
class ApplicationConfig(cfg.ConfigNode): class ApplicationConfig(cfg.ConfigNode):
def __init__(self, enable_log=True) -> None: def __init__(self, enable_log=True) -> None:
# Call the base class (important!) # Call the base class (important!)
super().__init__(enable_log=enable_log) super().__init__(internal_log=enable_log)
# Some fields # Some fields
# Create a field of type int. Set a default value, a friendly name and a description # Create a field of type int. Set a default value, a friendly name and a description
...@@ -61,7 +65,6 @@ class ApplicationConfig(cfg.ConfigNode): ...@@ -61,7 +65,6 @@ class ApplicationConfig(cfg.ConfigNode):
friendly_name="My Counter", friendly_name="My Counter",
description="This is just an integer") description="This is just an integer")
self.version: cfg.Field[str] = cfg.Field("v1.0", self.version: cfg.Field[str] = cfg.Field("v1.0",
friendly_name="Version", friendly_name="Version",
description="The version") description="The version")
...@@ -69,7 +72,6 @@ class ApplicationConfig(cfg.ConfigNode): ...@@ -69,7 +72,6 @@ class ApplicationConfig(cfg.ConfigNode):
# You can also omit the friendly name and description # You can also omit the friendly name and description
self.check: cfg.Field[bool] = cfg.Field(False) self.check: cfg.Field[bool] = cfg.Field(False)
# Some other fields # Some other fields
# Also possible to create a field of type list # Also possible to create a field of type list
self.my_tuple: cfg.Field[tuple] = cfg.Field((1, 2)) self.my_tuple: cfg.Field[tuple] = cfg.Field((1, 2))
......
import logging
import sys import sys
from pathlib import Path from pathlib import Path
sys.path.append('../src/') sys.path.append('../src/')
...@@ -8,12 +9,10 @@ from LaserConfig import LaserConfig ...@@ -8,12 +9,10 @@ from LaserConfig import LaserConfig
class ApplicationConfig(cfg.ConfigNode): class ApplicationConfig(cfg.ConfigNode):
def __init__(self, enable_log=True) -> None: def __init__(self, internal_log=True, internal_log_level= logging.DEBUG) -> None:
super().__init__(enable_log=enable_log) super().__init__(internal_log=internal_log, internal_log_level=internal_log_level)
self.output_directory: cfg.Field[Path] = cfg.Field(Path("C:\\{wafer_nr}"), self.output_directory: cfg.Field[Path] = cfg.Field(Path("C:\\{wafer_nr}"))
friendly_name="Output Directory",
description="The version of the wafer")
self.wafer_version: cfg.Field[str] = cfg.Field("v1.0", self.wafer_version: cfg.Field[str] = cfg.Field("v1.0",
friendly_name="wafer_version", friendly_name="wafer_version",
...@@ -39,7 +38,8 @@ class ApplicationConfig(cfg.ConfigNode): ...@@ -39,7 +38,8 @@ class ApplicationConfig(cfg.ConfigNode):
friendly_name="wafer_list", friendly_name="wafer_list",
description="The version of the wafer") description="The version of the wafer")
self.laser_config: LaserConfig = LaserConfig() self.laser_config: LaserConfig = LaserConfig(internal_log=internal_log,
internal_log_level=internal_log_level)
self.register() self.register()
...@@ -3,8 +3,8 @@ import confighandler as cfg ...@@ -3,8 +3,8 @@ import confighandler as cfg
class LaserConfig(cfg.ConfigNode): class LaserConfig(cfg.ConfigNode):
def __init__(self, enable_log=False) -> None: def __init__(self, internal_log, internal_log_level) -> None:
super().__init__(enable_log=enable_log) super().__init__(internal_log=internal_log, internal_log_level=internal_log_level)
self.wavelength_range = cfg.Field(850) self.wavelength_range = cfg.Field(850)
self.velocity = cfg.Field(2.0) self.velocity = cfg.Field(2.0)
self.acceleration = cfg.Field(1.0) self.acceleration = cfg.Field(1.0)
......
...@@ -12,7 +12,7 @@ if __name__ == "__main__": ...@@ -12,7 +12,7 @@ if __name__ == "__main__":
# setup the logging module # setup the logging module
config = ApplicationConfig(enable_log=True) config = ApplicationConfig(internal_log_level=logging.INFO)
#print(config.load('./configs/ApplicationConfig.yaml')) #print(config.load('./configs/ApplicationConfig.yaml'))
#config.autosave(enable=True, path='./configs_autosave') #config.autosave(enable=True, path='./configs_autosave')
......
...@@ -3,14 +3,6 @@ import os ...@@ -3,14 +3,6 @@ import os
import sys import sys
from rich.logging import RichHandler from rich.logging import RichHandler
FORMAT = "%(message)s"
logging.basicConfig(
level="DEBUG", format=FORMAT, datefmt="[%X]", handlers=[
RichHandler(rich_tracebacks=True)
]
)
sys.path.append(os.path.join(os.path.dirname(__file__), '../')) sys.path.append(os.path.join(os.path.dirname(__file__), '../'))
from .controller.Field import Field from .controller.Field import Field
from .controller.ConfigNode import ConfigNode from .controller.ConfigNode import ConfigNode
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Author(s): Christoph Schmidt <christoph.schmidt@tugraz.at>
Created: 2023-10-19 12:35
Package Version:
Description:
"""
import logging
import os
from rich.logging import RichHandler
class CObject:
def __init__(self, name: str = ""):
if name is None or name == "":
self.name = f"{self.__class__.__name__.replace('Field', '')}({hash(self)})"
else:
self.name = f"{name}({os.getpid()})"
self._internal_logger, self._internal_log_handler = self.create_new_logger(f"(cfg) {self.name}")
# ==================================================================================================================
# Logging
# ==================================================================================================================
def create_new_logger(self, name: str) -> (logging.Logger, logging.Handler):
qh = RichHandler(rich_tracebacks=True)
_internal_logger = logging.getLogger(name)
_internal_logger.handlers = [qh]
_internal_logger.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(name)s %(message)s')
qh.setFormatter(formatter)
return _internal_logger, qh
@property
def internal_log_enabled(self):
return not self._internal_logger.disabled
@internal_log_enabled.setter
def internal_log_enabled(self, enable: bool) -> None:
"""
Enables or disables internal logging. If disabled, the internal logger will be disabled and no messages will be
emitted to the state queue.
:param enable: True to enable, False to disable
"""
self._internal_logger.disabled = not enable
@property
def internal_log_level(self):
return self._internal_logger.level
@internal_log_level.setter
def internal_log_level(self, level: int) -> None:
"""
Sets the internal logging level.
:param level:
:return:
"""
self._internal_logger.setLevel(level)
\ No newline at end of file
...@@ -11,26 +11,31 @@ import logging ...@@ -11,26 +11,31 @@ import logging
import yaml import yaml
from PySide6.QtWidgets import QMessageBox from PySide6.QtWidgets import QMessageBox
from rich.logging import RichHandler
from .CObject import CObject
from .CSignal import CSignal from .CSignal import CSignal
from .Field import Field from .Field import Field
from ..view.ConfigView import ConfigView from ..view.ConfigView import ConfigView
import pathlib import pathlib
import rich
class ConfigNode(CObject):
class ConfigNode(object):
field_changed = CSignal() field_changed = CSignal()
cur_time = datetime.datetime.now() cur_time = datetime.datetime.now()
def __init__(self, enable_log: bool = False): def __init__(self, internal_log, internal_log_level):
super().__init__() super().__init__()
self._autosave = False self._autosave = False
self.enable_log = enable_log
self.internal_log_enabled = internal_log
self.internal_log_level = internal_log_level
self.name = self.__class__.__name__ self.name = self.__class__.__name__
self.logger = logging.getLogger(self.name) self.logger, self.log_handler = self.create_new_logger(self.name)
self.owner = None self.owner = None
self._level = 0 self._level = 0
...@@ -46,7 +51,7 @@ class ConfigNode(object): ...@@ -46,7 +51,7 @@ class ConfigNode(object):
} }
self.field_changed.connect(self._on_field_changed) self.field_changed.connect(self._on_field_changed)
self.config_logger(enable=self.enable_log)
# ================================================================================================================== # ==================================================================================================================
...@@ -100,7 +105,7 @@ class ConfigNode(object): ...@@ -100,7 +105,7 @@ class ConfigNode(object):
def deserialize(self, content): def deserialize(self, content):
"""Deserializes the content of the config based on the yaml file""" """Deserializes the content of the config based on the yaml file"""
self.logger.info(f"Deserializing {content}") self._internal_logger.info(f"Deserializing {content}")
for attr, val in content.items(): for attr, val in content.items():
# Check if the attribute is not of type GenericConfig # Check if the attribute is not of type GenericConfig
# therefore get the attribute type of this class # therefore get the attribute type of this class
...@@ -110,11 +115,11 @@ class ConfigNode(object): ...@@ -110,11 +115,11 @@ class ConfigNode(object):
self.deserialize(val) self.deserialize(val)
elif attr in self.__dict__: elif attr in self.__dict__:
if not isinstance(getattr(self, attr), ConfigNode): if not isinstance(getattr(self, attr), ConfigNode):
self.logger.info(f"Deserializing field {attr} with content: {val}") self._internal_logger.info(f"Deserializing field {attr} with content: {val}")
val = getattr(self, attr)._field_parser(val) val = getattr(self, attr)._field_parser(val)
getattr(self, attr).set(val) getattr(self, attr).set(val)
else: else:
self.logger.info(f"Deserializing config {attr} with content: {val}") self._internal_logger.info(f"Deserializing config {attr} with content: {val}")
getattr(self, attr).deserialize(val) getattr(self, attr).deserialize(val)
...@@ -134,7 +139,8 @@ class ConfigNode(object): ...@@ -134,7 +139,8 @@ class ConfigNode(object):
self.fields[attr] = val self.fields[attr] = val
# val.register(self.keywords, self.view.keywords_changed) # val.register(self.keywords, self.view.keywords_changed)
val.register(self.__class__.__name__, attr, self.keywords, self.field_changed) val.register(self.__class__.__name__, attr, self.keywords, self.field_changed)
val.config_logger(enable=self.enable_log) val.internal_log_enabled = self.internal_log_enabled
val.internal_log_level = self.internal_log_level
self.view.keywords_changed.emit(self.keywords) self.view.keywords_changed.emit(self.keywords)
def _register_config(self): def _register_config(self):
...@@ -157,7 +163,7 @@ class ConfigNode(object): ...@@ -157,7 +163,7 @@ class ConfigNode(object):
stream.write(self.serialize()) stream.write(self.serialize())
# print(self.serialize()) # print(self.serialize())
if not background_save: if not background_save:
self.logger.debug(f"Saved config to {file}") self._internal_logger.debug(f"Saved config to {file}")
# with open(file, 'w+') as stream: # with open(file, 'w+') as stream:
# yaml.dump(self, stream) # Dump it as a xaml file # yaml.dump(self, stream) # Dump it as a xaml file
# with open(file, 'w+') as stream: # with open(file, 'w+') as stream:
...@@ -196,11 +202,5 @@ class ConfigNode(object): ...@@ -196,11 +202,5 @@ class ConfigNode(object):
# Saves on every field change # Saves on every field change
self.save(file=file, background_save=True) self.save(file=file, background_save=True)
# ==================================================================================================================
# Miscs
# ==================================================================================================================
def config_logger(self, enable: bool = True, level: str = "DEBUG"):
self.logger.warning(f"Disabled logging for {self.name}")
self.logger.disabled = not enable
for attr, val in self.fields.items():
val.enable_log(enable, level)
...@@ -11,6 +11,7 @@ import re ...@@ -11,6 +11,7 @@ import re
from pathlib import Path from pathlib import Path
from typing import Generic, T, TypeVar from typing import Generic, T, TypeVar
from confighandler.controller.CObject import CObject
from confighandler.controller.CSignal import CSignal from confighandler.controller.CSignal import CSignal
from confighandler.view.FieldView import FieldView from confighandler.view.FieldView import FieldView
...@@ -24,14 +25,15 @@ class FieldData(object): ...@@ -24,14 +25,15 @@ class FieldData(object):
T = TypeVar('T') T = TypeVar('T')
class Field(Generic[T]): class Field(Generic[T], CObject):
def __init__(self, value: T, friendly_name: str = None, description: str = None, enable_log: bool = False): def __init__(self, value: T, friendly_name: str = None, description: str = None,
internal_log=True, internal_log_level=logging.INFO):
super().__init__() super().__init__()
self.enable_log = enable_log self.internal_log_enabled = internal_log
self.name = self.__class__.__name__ self.internal_log_level = internal_log_level
self.logger = logging.getLogger(self.name)
self.config_logger(enable=self.enable_log) self.logger, self.log_handler = self.create_new_logger(self.name)
self._data = FieldData(self.name, value, friendly_name, description) self._data = FieldData(self.name, value, friendly_name, description)
...@@ -43,7 +45,8 @@ class Field(Generic[T]): ...@@ -43,7 +45,8 @@ class Field(Generic[T]):
# The view, usd for handling the UI # The view, usd for handling the UI
self.view = FieldView(self) self.view = FieldView(self)
self.logger.debug(f"Field {self.__class__.__name__} created with value {value} of type {type(value)}")
self._internal_logger.debug(f"Field {self.__class__.__name__} created with value {value} of type {type(value)}")
def __new__(cls, value, friendly_name: str = None, description: str = None): def __new__(cls, value, friendly_name: str = None, description: str = None):
# print(f"Field {cls.__name__} created with value {value} of type {type(value)} -> {isinstance(value, int)}") # print(f"Field {cls.__name__} created with value {value} of type {type(value)} -> {isinstance(value, int)}")
...@@ -85,8 +88,13 @@ class Field(Generic[T]): ...@@ -85,8 +88,13 @@ class Field(Generic[T]):
:param keywords: The keywords dict :param keywords: The keywords dict
:param csig_field_changed: The signal that is emitted when the keywords are changed :param csig_field_changed: The signal that is emitted when the keywords are changed
""" """
self.name = name self.name = name
self.owner = owner self.owner = owner
formatter = logging.Formatter(f'%(name)s [{self.name}] %(message)s')
self._internal_log_handler.setFormatter(formatter)
if self._friendly_name is None: if self._friendly_name is None:
self._friendly_name = self.name self._friendly_name = self.name
...@@ -97,7 +105,7 @@ class Field(Generic[T]): ...@@ -97,7 +105,7 @@ class Field(Generic[T]):
# self.keywords_changed = keyword_changed # self.keywords_changed = keyword_changed
# self.keywords_changed.connect(self._on_keyword_changed) # self.keywords_changed.connect(self._on_keyword_changed)
self.set_keywords() self.set_keywords()
self.logger.info(f"Field assigned to {self.owner} with name {self.name}") self._internal_logger.info(f"Field '{self.name}' assigned to {self.owner}")
# ================================================================================================================== # ==================================================================================================================
# Set the keywords for the field # Set the keywords for the field
...@@ -105,7 +113,7 @@ class Field(Generic[T]): ...@@ -105,7 +113,7 @@ class Field(Generic[T]):
def set_keywords(self): def set_keywords(self):
"""Set the keywords for the field. Also updates the keywords dict if a value of a field is changed.""" """Set the keywords for the field. Also updates the keywords dict if a value of a field is changed."""
# self.keywords["{" + self.name + "}"] = self.value # self.keywords["{" + self.name + "}"] = self.value
# self.logger.info(f"Setting keywords for {self.name} to {self.value}") # self._internal_logger.info(f"Setting keywords for {self.name} to {self.value}")
self.keywords[self.name] = str(self.value).replace(' ', '_').replace('.', '_').replace(',', '_') self.keywords[self.name] = str(self.value).replace(' ', '_').replace('.', '_').replace(',', '_')
self.csig_field_changed.emit() self.csig_field_changed.emit()
...@@ -148,7 +156,7 @@ class Field(Generic[T]): ...@@ -148,7 +156,7 @@ class Field(Generic[T]):
def set(self, value: T): def set(self, value: T):
if not self.value == value: if not self.value == value:
self.logger.info(f"{self.name} = {value} ({type(value)})") self._internal_logger.info(f"{self.name} = {value} ({type(value)})")
self._set(value) self._set(value)
self.set_keywords() self.set_keywords()
# self.view.value_changed.emit(self.value) # self.view.value_changed.emit(self.value)
...@@ -178,12 +186,3 @@ class Field(Generic[T]): ...@@ -178,12 +186,3 @@ class Field(Generic[T]):
def __repr__(self): def __repr__(self):
return str(f"{self.__class__.__name__}({self.value})") return str(f"{self.__class__.__name__}({self.value})")
# ==================================================================================================================
# Miscs
# ==================================================================================================================
def config_logger(self, enable: bool = True, level: str = "DEBUG"):
if enable:
self.logger.info(f"Enabled logging for {self.name}")
else:
self.logger.warning(f"Disabled logging for {self.name}")
self.logger.disabled = not enable
...@@ -37,10 +37,10 @@ class FieldPath(Field): ...@@ -37,10 +37,10 @@ class FieldPath(Field):
# Overwritten function, to replace the @Path keyword # Overwritten function, to replace the @Path keyword
match = re.findall(r'@Path:<([^>]*)>', val) match = re.findall(r'@Path:<([^>]*)>', val)
if len(match) > 0: if len(match) > 0:
self.logger.info(f"Found @Path: {val}. Check syntax, multiple @Path: are not allowed in one field.") self._internal_logger.info(f"Found @Path: {val}. Check syntax, multiple @Path: are not allowed in one field.")
return Path(match[0]) return Path(match[0])
elif len(match) == 0: elif len(match) == 0:
self.logger.debug(f"No @Path: found in {val}. Please check field.") self._internal_logger.debug(f"No @Path: found in {val}. Please check field.")
return Path('./') return Path('./')
def __str__(self): def __str__(self):
return str(Path(self.value).as_posix()) return str(Path(self.value).as_posix())
\ No newline at end of file
...@@ -12,6 +12,7 @@ from PySide6.QtCore import Signal ...@@ -12,6 +12,7 @@ from PySide6.QtCore import Signal
from PySide6.QtWidgets import QWidget, QTreeWidgetItem, QMessageBox from PySide6.QtWidgets import QWidget, QTreeWidgetItem, QMessageBox
import confighandler.controller.Field as Field import confighandler.controller.Field as Field
from confighandler.controller.CObject import CObject
class FieldView(QWidget): class FieldView(QWidget):
......
...@@ -52,7 +52,7 @@ class FieldViewPath(FieldView): ...@@ -52,7 +52,7 @@ class FieldViewPath(FieldView):
grd.addWidget(btn_open, 0, 1) grd.addWidget(btn_open, 0, 1)
grd.addWidget(self.ui_edit_fields_lbl[-1], 1, 0, 1, 2) grd.addWidget(self.ui_edit_fields_lbl[-1], 1, 0, 1, 2)
self.parent_field.logger.info(f"Registered QEditField for {self.ui_edit_fields[-1]}") self.parent_field._internal_logger.info(f"Registered QEditField for {self.ui_edit_fields[-1]}")
wdg.setLayout(grd) wdg.setLayout(grd)
......
...@@ -31,14 +31,14 @@ class FieldViewString(FieldView): ...@@ -31,14 +31,14 @@ class FieldViewString(FieldView):
le: QLineEdit = view le: QLineEdit = view
le.setToolTip(f"({self.parent_field.name}) {self.parent_field._description}") le.setToolTip(f"({self.parent_field.name}) {self.parent_field._description}")
self.ui_edit_fields.append(le) self.ui_edit_fields.append(le)
self.parent_field.logger.debug(f"Registering LineEdit {le}") self.parent_field._internal_logger.debug(f"Registering LineEdit {le}")
self.ui_edit_fields[-1].textEdited.connect(lambda d: self._on_text_edited(le, d)) self.ui_edit_fields[-1].textEdited.connect(lambda d: self._on_text_edited(le, d))
# new # new
return le return le
def _on_text_edited(self, f, value): def _on_text_edited(self, f, value):
self.parent_field.logger.debug(f"LineEdit {f} changed to {value}.") self.parent_field._internal_logger.debug(f"LineEdit {f} changed to {value}.")
self.parent_field.set(value) self.parent_field.set(value)
def _on_value_changed(self, value): def _on_value_changed(self, value):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment