Skip to content

Commit

Permalink
Merge pull request #26 from ifm/develop
Browse files Browse the repository at this point in the history
Release 0.5.0
  • Loading branch information
cwiede authored Apr 19, 2021
2 parents 9d76351 + 958eda3 commit 8dc1053
Show file tree
Hide file tree
Showing 28 changed files with 1,169 additions and 472 deletions.
1 change: 0 additions & 1 deletion doc/manual/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
Expand Down
2 changes: 0 additions & 2 deletions doc/manual/nexxT.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ Subpackages
autogenerated/nexxT.services
autogenerated/nexxT.filters
autogenerated/nexxT.examples
cplusplus
commandline

Module contents
---------------
Expand Down
52 changes: 52 additions & 0 deletions doc/manual/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,55 @@ For being able to announce the C++ filters, the plugin needs to be defined. This
.. literalinclude:: ../../nexxT/tests/src/Plugins.cpp
:language: c

Debugging
+++++++++
Debugging can be achieved with any IDE of your choice which supports starting python modules (a.k.a. python -m ). The nexxT-gui start script can be replaced by python -m nexxT.core.AppConsole. See specific examples below.

Python debugging with Visual Studio Code
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To start with VS Code make sure the Python extension for VS Code is installed (`see here <https://code.visualstudio.com/docs/languages/python>`_).

Open VS Code in your source code directory via menu ("File/Open Folder") or cd in your terminal of choice to your folder and start VS Code by typing :code:`code .` (dot for the current directory).

Setting virtual environment
***************************
If you're not using venv, continue to the next section. In case you are using a virtual environment, we need to provide VS Code some information.
Open the settings.json file in your .vscode directory (or create it). Your settings should include following information:

.. code-block:: JSON
{
"python.pythonPath": "/path/to/your/python/interpreter/python.exe",
"python.venvPath": "/path/to/your/venv",
"python.terminal.activateEnvironment": true
}
The paths should be absolute. If :code:`"python.pythonPath"` is inside your venv, :code:`"python.venvPath"` is not required, VS Code will recognize it and activate venv automatically.

**Note**: Make sure that at least one .py file is opened in the editor when starting debugging, otherwise the venv may not be activated (the author does not know exactly under which circumstances this is required, but this information may safe you some time searching the internet when things don't go as expected).

With these settings at hand, venv will also be started automatically when we create a new terminal in VS Code ("Terminal/New Terminal").

Configuring launch file
***********************
The next step is to create the launch.json file for our debug session (manually or via "Run/Add configuration"). Your launch.json file in .vscode folder should look like this:

.. code-block:: JSON
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Modul",
"type": "python",
"request": "launch",
"module": "nexxT.core.AppConsole",
"justMyCode": false
}
]
}
The "module" setting is the critical part. Under the hood VS code will invoke :code:`python -m <module>`.
With the "justMyCode" setting, we can extend debugging to external code loaded by our application.

We're all set, now we can run our debug session by pressing F5 or the "Run" menu.
4 changes: 4 additions & 0 deletions nexxT/core/AppConsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ def main(withGui):
NEXXT_CEXT_PATH:
Can be set to override the default search path for the nexxT C extension.
NEXXT_BLACKLISTED_PACKAGES:
List of additional python packages (seperated by a ';') which are not unloaded by nexxT when configuration files
are switched. Use "*" or "__all__" to blacklist all modules.
""")
parser.add_argument("cfg", nargs='?', help=".json configuration file of the project to be loaded.")
parser.add_argument("-a", "--active", default=None, type=str,
Expand Down
3 changes: 2 additions & 1 deletion nexxT/core/ConfigFiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
This modules defines classes for nexxT config file handling.
"""

import copy
import json
import logging
from pathlib import Path
Expand Down Expand Up @@ -216,7 +217,7 @@ def splitCommonGuiStateSections(newGuiState, oldGuiState):
return res

guistate = {}
cfg = newCfg.copy()
cfg = copy.deepcopy(newCfg)
guistate["_guiState"] = splitCommonGuiStateSections(cfg["_guiState"], oldCfg["_guiState"])
cfg["_guiState"] = oldCfg["_guiState"]

Expand Down
6 changes: 6 additions & 0 deletions nexxT/core/Exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"""

def _factoryToString(factory):
# pylint: disable=import-outside-toplevel
# needed to avoid recursive import
from nexxT.interface.Ports import InputPortInterface, OutputPortInterface
if isinstance(factory, str):
return factory
Expand Down Expand Up @@ -95,6 +97,8 @@ class UnexpectedFilterState(NexTRuntimeError):
raised when operations are performed in unexpected filter states
"""
def __init__(self, state, operation):
# pylint: disable=import-outside-toplevel
# needed to avoid recursive import
from nexxT.interface.Filters import FilterState
super().__init__("Operation '%s' cannot be performed in state %s" % (operation, FilterState.state2str(state)))

Expand All @@ -103,6 +107,8 @@ class FilterStateMachineError(UnexpectedFilterState):
raised when a state transition is invalid.
"""
def __init__(self, oldState, newState):
# pylint: disable=import-outside-toplevel
# needed to avoid recursive import
from nexxT.interface.Filters import FilterState
super().__init__(oldState, "Transition to " + FilterState.state2str(newState))

Expand Down
2 changes: 0 additions & 2 deletions nexxT/core/FilterMockup.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ def createFilterAndUpdate(self, immediate=True):
Creates the filter, performs init() operation and updates the port information.
:return: None
"""
# TODO is it really a good idea to use queued connections for dynamic port changes?
# maybe there is a better way to prevent too many calls to _createFilterAndUpdate
if immediate:
self._createFilterAndUpdate()
self._createFilterAndUpdatePending = None
Expand Down
28 changes: 26 additions & 2 deletions nexxT/core/PluginManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import sys
import inspect
import os
import os.path
import logging
from collections import OrderedDict
Expand Down Expand Up @@ -61,6 +62,9 @@ class PythonLibrary:
LIBTYPE_MODULE = 1
LIBTYPE_ENTRY_POINT = 2

# blacklisted packages are not unloaded when closing an application.
BLACKLISTED_PACKAGES = ["h5py", "numpy", "matplotlib", "PySide2", "shiboken2"]

def __init__(self, library, libtype):
self._library = library
self._libtype = libtype
Expand Down Expand Up @@ -130,6 +134,23 @@ def __len__(self):
self._checkAvailableFilters()
return len(self._availableFilters)

@staticmethod
def blacklisted(moduleName):
"""
returns whether the module is blacklisted for unload heuristics
:param moduleName: the name of the module as a key in sys.modules
"""
pkg = PythonLibrary.BLACKLISTED_PACKAGES[:]
if "NEXXT_BLACKLISTED_PACKAGES" in os.environ:
if os.environ["NEXXT_BLACKLISTED_PACKAGES"] in ["*", "__all__"]:
return True
pkg.extend(os.environ["NEXXT_BLACKLISTED_PACKAGES"].split(";"))
for p in pkg:
if moduleName.startswith(p + ".") or moduleName == p:
return True
return False

def unload(self):
"""
Unloads this python module. During loading, transitive dependent modules are detected and they are unloaded
Expand All @@ -148,8 +169,11 @@ def unload(self):
loader = getattr(mod, '__loader__', None)
if not (fn is None or isinstance(loader, ExtensionFileLoader) or
os.path.splitext(fn)[1] in EXTENSION_SUFFIXES):
logger.internal("Unloading pure python module '%s' (%s)", m, fn)
del sys.modules[m]
if not self.blacklisted(m):
logger.internal("Unloading pure python module '%s' (%s)", m, fn)
del sys.modules[m]
else:
logger.internal("Module '%s' (%s) is blacklisted and will not be unloaded", m, fn)

class PluginManager(QObject):
"""
Expand Down
2 changes: 2 additions & 0 deletions nexxT/core/PortImpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ def setInterthreadDynamicQueue(self, enabled):
"""
if enabled != self._interthreadDynamicQueue:
state = self.environment().state()
# pylint: disable=import-outside-toplevel
# needed to avoid recursive import
from nexxT.interface.Filters import FilterState # avoid recursive import
if state not in [FilterState.CONSTRUCTING, FilterState.CONSTRUCTED,
FilterState.INITIALIZING, FilterState.INITIALIZED]:
Expand Down
3 changes: 2 additions & 1 deletion nexxT/core/SubConfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ def getThreadSet(item):
:param mockup:
:return: set of strings
"""
# avoid recursive import
# pylint: disable=import-outside-toplevel
# needed to avoid recursive import
from nexxT.core.CompositeFilter import CompositeFilter
from nexxT.core.FilterMockup import FilterMockup
if isinstance(item, FilterMockup):
Expand Down
57 changes: 29 additions & 28 deletions nexxT/core/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,36 +420,37 @@ def paintEvent(self, event):
:param event: a QPaintEvent instance
:return:
"""
self._paintEvent(event)

@handleException
def _paintEvent(self, event):
super().paintEvent(event)
painter = QPainter(self)
try:
fontMetrics = painter.fontMetrics()
lineSpacing = self.fontMetrics().lineSpacing()
y = 0
textLayout = QTextLayout(self._content, painter.font())
textLayout.setTextOption(self._textOption)
textLayout.beginLayout()
while True:
line = textLayout.createLine()
if not line.isValid():
break
line.setLineWidth(self.width())
nextLineY = y + lineSpacing
if self.height() >= nextLineY + lineSpacing:
# not the last line
elidedLine = self._content[line.textStart():line.textStart() + line.textLength()]
elidedLine = fontMetrics.elidedText(elidedLine, self._elideMode, self.width())
painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLine)
y = nextLineY
else:
# last line, check if we are to elide here to the end
lastLine = self._content[line.textStart():]
elidedLastLine = fontMetrics.elidedText(lastLine, self._elideMode, self.width())
painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine)
break
textLayout.endLayout()
except Exception as e:
logger.exception("Exception during paint: %s", e)
fontMetrics = painter.fontMetrics()
lineSpacing = self.fontMetrics().lineSpacing()
y = 0
textLayout = QTextLayout(self._content, painter.font())
textLayout.setTextOption(self._textOption)
textLayout.beginLayout()
while True:
line = textLayout.createLine()
if not line.isValid():
break
line.setLineWidth(self.width())
nextLineY = y + lineSpacing
if self.height() >= nextLineY + lineSpacing:
# not the last line
elidedLine = self._content[line.textStart():line.textStart() + line.textLength()]
elidedLine = fontMetrics.elidedText(elidedLine, self._elideMode, self.width())
painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLine)
y = nextLineY
else:
# last line, check if we are to elide here to the end
lastLine = self._content[line.textStart():]
elidedLastLine = fontMetrics.elidedText(lastLine, self._elideMode, self.width())
painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine)
break
textLayout.endLayout()

if __name__ == "__main__": # pragma: no cover
def _smokeTestBarrier():
Expand Down
2 changes: 1 addition & 1 deletion nexxT/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

from pathlib import Path
import os
from nexxT.interface import FilterSurrogate
if os.environ.get("READTHEDOCS", None) is None:
from PySide2 import QtMultimedia # needed to load corresponding DLL before loading the nexxT plugin
from nexxT.interface import FilterSurrogate

AviReader = FilterSurrogate(
"binary://" + str((Path(__file__).parent.parent / "tests" /
Expand Down
Loading

0 comments on commit 8dc1053

Please sign in to comment.