Feat: Sidebar mit Suchfilter und lxml-Parser im XslDependencyDialog
- Ein-/ausblendbare Sidebar mit tab-übergreifender Suche hinzugefügt - Graph-Suchfilter blendet nicht-betroffene XSL-Dateien aus dem Netzwerkgraph aus - Regex-basierte XSL-Abhängigkeitserkennung durch lxml-Parser ersetzt - Suchfilter wird beim Tab-Wechsel erneut angewendet Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ dependencies = [
|
|||||||
"connectorx>=0.4.0",
|
"connectorx>=0.4.0",
|
||||||
"pydantic-yaml>=1.6.0",
|
"pydantic-yaml>=1.6.0",
|
||||||
"psutil>=6.1.1",
|
"psutil>=6.1.1",
|
||||||
|
"lxml>=6.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
|||||||
@@ -47,9 +47,17 @@ class XslDependencyDialog(QDialog):
|
|||||||
self._graph_loaded = False
|
self._graph_loaded = False
|
||||||
self._temp_html_file: tempfile.NamedTemporaryFile | None = None
|
self._temp_html_file: tempfile.NamedTemporaryFile | None = None
|
||||||
|
|
||||||
|
self._sidebar_visible = False
|
||||||
|
|
||||||
self.ui = Ui_XslDependencyDialog()
|
self.ui = Ui_XslDependencyDialog()
|
||||||
self.ui.setupUi(self)
|
self.ui.setupUi(self)
|
||||||
|
|
||||||
|
# Sidebar initial ausblenden
|
||||||
|
self.ui.settingsButton.setIcon(QIcon.fromTheme("preferences-system"))
|
||||||
|
self.ui.sidebarWidget.setVisible(False)
|
||||||
|
self.ui.mainSplitter.setSizes([1000, 0])
|
||||||
|
self.ui.settingsButton.clicked.connect(self._toggle_sidebar)
|
||||||
|
|
||||||
# Netzwerkgraph-Tab ausblenden wenn WebEngine nicht verfügbar
|
# Netzwerkgraph-Tab ausblenden wenn WebEngine nicht verfügbar
|
||||||
if not HAS_WEBENGINE:
|
if not HAS_WEBENGINE:
|
||||||
self.ui.tabWidget.removeTab(1)
|
self.ui.tabWidget.removeTab(1)
|
||||||
@@ -72,6 +80,9 @@ class XslDependencyDialog(QDialog):
|
|||||||
self.ui.fileTree.currentItemChanged.connect(self._on_file_selected)
|
self.ui.fileTree.currentItemChanged.connect(self._on_file_selected)
|
||||||
self.ui.searchEdit.textChanged.connect(self._on_search_changed)
|
self.ui.searchEdit.textChanged.connect(self._on_search_changed)
|
||||||
self.ui.tabWidget.currentChanged.connect(self._on_tab_changed)
|
self.ui.tabWidget.currentChanged.connect(self._on_tab_changed)
|
||||||
|
self.ui.graphFilterConnectedCheck.toggled.connect(
|
||||||
|
lambda: self._on_search_changed(self.ui.searchEdit.text())
|
||||||
|
)
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""Räumt temporäre Dateien und QWebEngineView auf."""
|
"""Räumt temporäre Dateien und QWebEngineView auf."""
|
||||||
@@ -197,17 +208,46 @@ class XslDependencyDialog(QDialog):
|
|||||||
self.ui.depTree.addTopLevelItem(no_deps_item)
|
self.ui.depTree.addTopLevelItem(no_deps_item)
|
||||||
|
|
||||||
def _on_search_changed(self, text: str):
|
def _on_search_changed(self, text: str):
|
||||||
|
"""Filtert je nach aktivem Tab die Dateiliste oder den Netzwerkgraph."""
|
||||||
|
current_tab = self.ui.tabWidget.currentIndex()
|
||||||
|
if current_tab == 0:
|
||||||
|
self._search_tree(text)
|
||||||
|
elif current_tab == 1 and self._web_view is not None:
|
||||||
|
self._search_graph(text)
|
||||||
|
|
||||||
|
def _search_tree(self, text: str):
|
||||||
"""Filtert die Dateiliste nach dem Suchbegriff."""
|
"""Filtert die Dateiliste nach dem Suchbegriff."""
|
||||||
search_lower = text.lower()
|
search_lower = text.lower()
|
||||||
for i in range(self.ui.fileTree.topLevelItemCount()):
|
for i in range(self.ui.fileTree.topLevelItemCount()):
|
||||||
item = self.ui.fileTree.topLevelItem(i)
|
item = self.ui.fileTree.topLevelItem(i)
|
||||||
matches = search_lower in item.text(0).lower()
|
matches = not search_lower or search_lower in item.text(0).lower()
|
||||||
item.setHidden(not matches)
|
item.setHidden(not matches)
|
||||||
|
|
||||||
|
def _search_graph(self, text: str):
|
||||||
|
"""Filtert oder dimmt Knoten im Netzwerkgraph je nach Checkbox-Einstellung."""
|
||||||
|
escaped = text.replace("\\", "\\\\").replace("'", "\\'")
|
||||||
|
hide_unrelated = str(self.ui.graphFilterConnectedCheck.isChecked()).lower()
|
||||||
|
self._web_view.page().runJavaScript(f"filterNodes('{escaped}', {hide_unrelated})")
|
||||||
|
|
||||||
|
# === Sidebar ===
|
||||||
|
|
||||||
|
def _toggle_sidebar(self):
|
||||||
|
"""Blendet die Einstellungs-Sidebar ein oder aus."""
|
||||||
|
self._sidebar_visible = not self._sidebar_visible
|
||||||
|
if self._sidebar_visible:
|
||||||
|
self.ui.sidebarWidget.setVisible(True)
|
||||||
|
self.ui.mainSplitter.setSizes([750, 250])
|
||||||
|
else:
|
||||||
|
self.ui.mainSplitter.setSizes([1000, 0])
|
||||||
|
self.ui.sidebarWidget.setVisible(False)
|
||||||
|
|
||||||
# === Netzwerkgraph (vis.js) ===
|
# === Netzwerkgraph (vis.js) ===
|
||||||
|
|
||||||
def _on_tab_changed(self, index: int):
|
def _on_tab_changed(self, index: int):
|
||||||
"""Lazy-Init des Netzwerkgraphs beim ersten Tab-Wechsel."""
|
"""Lazy-Init des Netzwerkgraphs beim ersten Tab-Wechsel und Sidebar-Seite synchronisieren."""
|
||||||
|
# Sidebar-Seite synchronisieren
|
||||||
|
self.ui.settingsStack.setCurrentIndex(index)
|
||||||
|
|
||||||
if not HAS_WEBENGINE:
|
if not HAS_WEBENGINE:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -215,6 +255,11 @@ class XslDependencyDialog(QDialog):
|
|||||||
if index == 1 and not self._graph_loaded:
|
if index == 1 and not self._graph_loaded:
|
||||||
self._setup_network_graph()
|
self._setup_network_graph()
|
||||||
|
|
||||||
|
# Suchbegriff auf den neuen Tab anwenden
|
||||||
|
search_text = self.ui.searchEdit.text()
|
||||||
|
if search_text:
|
||||||
|
self._on_search_changed(search_text)
|
||||||
|
|
||||||
def _setup_network_graph(self):
|
def _setup_network_graph(self):
|
||||||
"""Erstellt QWebEngineView und lädt den vis.js Netzwerkgraph."""
|
"""Erstellt QWebEngineView und lädt den vis.js Netzwerkgraph."""
|
||||||
self._web_view = QWebEngineView(self.ui.graphContainer)
|
self._web_view = QWebEngineView(self.ui.graphContainer)
|
||||||
@@ -474,6 +519,117 @@ class XslDependencyDialog(QDialog):
|
|||||||
}});
|
}});
|
||||||
edges.update(edgeUpdates);
|
edges.update(edgeUpdates);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
// Suchfunktion: Knoten nach Label filtern
|
||||||
|
function filterNodes(searchTerm, hideUnrelated) {{
|
||||||
|
var term = searchTerm.toLowerCase();
|
||||||
|
|
||||||
|
if (hideUnrelated) {{
|
||||||
|
// Originalzustand wiederherstellen
|
||||||
|
nodes.clear();
|
||||||
|
edges.clear();
|
||||||
|
nodes.add(nodesData);
|
||||||
|
edges.add(edgesData);
|
||||||
|
allNodes = nodes.get();
|
||||||
|
|
||||||
|
if (!term) return;
|
||||||
|
|
||||||
|
// 1. Matching-Knoten finden
|
||||||
|
var matchedIds = new Set();
|
||||||
|
nodesData.forEach(function(node) {{
|
||||||
|
if (node.label.toLowerCase().indexOf(term) !== -1) {{
|
||||||
|
matchedIds.add(node.id);
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
|
||||||
|
// 2. Gerichtete Adjazenzlisten aufbauen
|
||||||
|
var fwd = {{}}; // from → to (Import-Richtung)
|
||||||
|
var bwd = {{}}; // to → from (Reverse)
|
||||||
|
edgesData.forEach(function(edge) {{
|
||||||
|
if (!fwd[edge.from]) fwd[edge.from] = [];
|
||||||
|
if (!bwd[edge.to]) bwd[edge.to] = [];
|
||||||
|
fwd[edge.from].push(edge.to);
|
||||||
|
bwd[edge.to].push(edge.from);
|
||||||
|
}});
|
||||||
|
|
||||||
|
// 3. BFS vorwärts (Nachkommen) + rückwärts (Vorfahren)
|
||||||
|
var reachable = new Set(matchedIds);
|
||||||
|
var queue = Array.from(matchedIds);
|
||||||
|
while (queue.length > 0) {{
|
||||||
|
var current = queue.shift();
|
||||||
|
(fwd[current] || []).forEach(function(n) {{
|
||||||
|
if (!reachable.has(n)) {{ reachable.add(n); queue.push(n); }}
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
queue = Array.from(matchedIds);
|
||||||
|
while (queue.length > 0) {{
|
||||||
|
var current = queue.shift();
|
||||||
|
(bwd[current] || []).forEach(function(n) {{
|
||||||
|
if (!reachable.has(n)) {{ reachable.add(n); queue.push(n); }}
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// 4. Nicht-erreichbare Knoten und Kanten entfernen
|
||||||
|
var removeNodeIds = [];
|
||||||
|
nodesData.forEach(function(node) {{
|
||||||
|
if (!reachable.has(node.id)) removeNodeIds.push(node.id);
|
||||||
|
}});
|
||||||
|
nodes.remove(removeNodeIds);
|
||||||
|
|
||||||
|
var removeEdgeIds = [];
|
||||||
|
edgesData.forEach(function(edge) {{
|
||||||
|
if (!reachable.has(edge.from) || !reachable.has(edge.to))
|
||||||
|
removeEdgeIds.push(edge.id);
|
||||||
|
}});
|
||||||
|
edges.remove(removeEdgeIds);
|
||||||
|
|
||||||
|
// Matching-Knoten hervorheben, verbundene leicht dimmen
|
||||||
|
var updates = [];
|
||||||
|
matchedIds.forEach(function(id) {{
|
||||||
|
updates.push({{ id: id, opacity: 1.0 }});
|
||||||
|
}});
|
||||||
|
reachable.forEach(function(id) {{
|
||||||
|
if (!matchedIds.has(id)) {{
|
||||||
|
updates.push({{ id: id, opacity: 0.5 }});
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
nodes.update(updates);
|
||||||
|
allNodes = nodes.get();
|
||||||
|
|
||||||
|
}} else {{
|
||||||
|
// Originalzustand wiederherstellen falls vorher gefiltert
|
||||||
|
nodes.clear();
|
||||||
|
edges.clear();
|
||||||
|
nodes.add(nodesData);
|
||||||
|
edges.add(edgesData);
|
||||||
|
allNodes = nodes.get();
|
||||||
|
|
||||||
|
// Bisheriges Verhalten: nur dimmen
|
||||||
|
var updates = [];
|
||||||
|
var matchedIds = new Set();
|
||||||
|
|
||||||
|
allNodes.forEach(function(node) {{
|
||||||
|
if (!term || node.label.toLowerCase().indexOf(term) !== -1) {{
|
||||||
|
updates.push({{ id: node.id, opacity: 1.0 }});
|
||||||
|
matchedIds.add(node.id);
|
||||||
|
}} else {{
|
||||||
|
updates.push({{ id: node.id, opacity: 0.12 }});
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
nodes.update(updates);
|
||||||
|
|
||||||
|
var allEdges = edges.get();
|
||||||
|
var edgeUpdates = [];
|
||||||
|
allEdges.forEach(function(edge) {{
|
||||||
|
if (!term || (matchedIds.has(edge.from) && matchedIds.has(edge.to))) {{
|
||||||
|
edgeUpdates.push({{ id: edge.id, color: {{ opacity: 0.7 }} }});
|
||||||
|
}} else {{
|
||||||
|
edgeUpdates.push({{ id: edge.id, color: {{ opacity: 0.05 }} }});
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
edges.update(edgeUpdates);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>"""
|
</html>"""
|
||||||
|
|||||||
+276
-145
@@ -15,157 +15,281 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<layout class="QHBoxLayout" name="toolbarLayout">
|
||||||
<property name="currentIndex">
|
<item>
|
||||||
<number>0</number>
|
<spacer name="toolbarSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="settingsButton">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>28</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Einstellungen ein-/ausblenden</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="mainSplitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="treeTab">
|
<property name="childrenCollapsible">
|
||||||
<attribute name="title">
|
<bool>true</bool>
|
||||||
<string>Baumansicht</string>
|
</property>
|
||||||
</attribute>
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<layout class="QVBoxLayout" name="treeTabLayout">
|
<property name="currentIndex">
|
||||||
<item>
|
<number>0</number>
|
||||||
<layout class="QHBoxLayout" name="searchLayout">
|
</property>
|
||||||
<item>
|
<widget class="QWidget" name="treeTab">
|
||||||
<widget class="QLabel" name="searchLabel">
|
<attribute name="title">
|
||||||
<property name="text">
|
<string>Baumansicht</string>
|
||||||
<string>Suche:</string>
|
</attribute>
|
||||||
</property>
|
<layout class="QVBoxLayout" name="treeTabLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="leftWidget" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="leftLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="leftLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>XSL-Dateien</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="fileTree">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="rootIsDecorated">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<widget class="QWidget" name="rightWidget" native="true">
|
||||||
<item>
|
<layout class="QVBoxLayout" name="rightLayout">
|
||||||
<widget class="QLineEdit" name="searchEdit">
|
<property name="leftMargin">
|
||||||
<property name="placeholderText">
|
<number>0</number>
|
||||||
<string>XSL-Datei filtern...</string>
|
</property>
|
||||||
</property>
|
<property name="topMargin">
|
||||||
<property name="clearButtonEnabled">
|
<number>0</number>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="rightLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Abhängigkeiten</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="depTree">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="rootIsDecorated">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
</item>
|
||||||
</item>
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="graphTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Netzwerkgraph</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="graphTabLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="graphContainer" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="graphContainerLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="sidebarWidget" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="sidebarLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSplitter" name="splitter">
|
<widget class="QLabel" name="sidebarLabel">
|
||||||
<property name="orientation">
|
<property name="font">
|
||||||
<enum>Qt::Horizontal</enum>
|
<font>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Einstellungen</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="leftWidget" native="true">
|
|
||||||
<layout class="QVBoxLayout" name="leftLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="leftLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>XSL-Dateien</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QTreeWidget" name="fileTree">
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionMode">
|
|
||||||
<enum>QAbstractItemView::SingleSelection</enum>
|
|
||||||
</property>
|
|
||||||
<property name="rootIsDecorated">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">1</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="rightWidget" native="true">
|
|
||||||
<layout class="QVBoxLayout" name="rightLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="rightLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Abhängigkeiten</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QTreeWidget" name="depTree">
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="rootIsDecorated">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">1</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="graphTab">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Netzwerkgraph</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="graphTabLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="graphContainer" native="true">
|
<widget class="QLabel" name="searchLabel">
|
||||||
<layout class="QVBoxLayout" name="graphContainerLayout">
|
<property name="text">
|
||||||
<property name="leftMargin">
|
<string>Suche:</string>
|
||||||
<number>0</number>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
<property name="topMargin">
|
</item>
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<widget class="QLineEdit" name="searchEdit">
|
||||||
<property name="rightMargin">
|
<property name="placeholderText">
|
||||||
<number>0</number>
|
<string>XSL-Datei filtern...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="clearButtonEnabled">
|
||||||
<number>0</number>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</layout>
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QStackedWidget" name="settingsStack">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="treeSettingsPage">
|
||||||
|
<layout class="QVBoxLayout" name="treeSettingsLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="treeSettingsLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Baumansicht-Einstellungen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="treeSettingsSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="graphSettingsPage">
|
||||||
|
<layout class="QVBoxLayout" name="graphSettingsLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="graphSettingsLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Graph-Einstellungen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="graphFilterConnectedCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Nur betroffene Dateien anzeigen</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Entfernt alle XSL-Dateien aus dem Graph, die nicht zum Suchbegriff passen und nicht direkt oder indirekt mit passenden Dateien verbunden sind</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="graphSettingsSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -174,6 +298,12 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="statusLabel">
|
<widget class="QLabel" name="statusLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
@@ -182,10 +312,10 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Close</set>
|
<set>QDialogButtonBox::StandardButton::Close</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="centerButtons">
|
<property name="centerButtons">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@@ -194,6 +324,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>buttonBox</sender>
|
<sender>buttonBox</sender>
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|||||||
QFont, QFontDatabase, QGradient, QIcon,
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
from PySide6.QtWidgets import (QAbstractButton, QAbstractItemView, QApplication, QDialog,
|
from PySide6.QtWidgets import (QAbstractButton, QAbstractItemView, QApplication, QCheckBox,
|
||||||
QDialogButtonBox, QHBoxLayout, QHeaderView, QLabel,
|
QDialog, QDialogButtonBox, QHBoxLayout, QHeaderView,
|
||||||
QLineEdit, QSizePolicy, QSplitter, QTabWidget,
|
QLabel, QLineEdit, QPushButton, QSizePolicy,
|
||||||
|
QSpacerItem, QSplitter, QStackedWidget, QTabWidget,
|
||||||
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget)
|
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget)
|
||||||
|
|
||||||
class Ui_XslDependencyDialog(object):
|
class Ui_XslDependencyDialog(object):
|
||||||
def setupUi(self, XslDependencyDialog):
|
def setupUi(self, XslDependencyDialog):
|
||||||
if not XslDependencyDialog.objectName():
|
if not XslDependencyDialog.objectName():
|
||||||
@@ -26,31 +28,35 @@ class Ui_XslDependencyDialog(object):
|
|||||||
XslDependencyDialog.resize(1000, 700)
|
XslDependencyDialog.resize(1000, 700)
|
||||||
self.verticalLayout = QVBoxLayout(XslDependencyDialog)
|
self.verticalLayout = QVBoxLayout(XslDependencyDialog)
|
||||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
self.tabWidget = QTabWidget(XslDependencyDialog)
|
self.toolbarLayout = QHBoxLayout()
|
||||||
|
self.toolbarLayout.setObjectName(u"toolbarLayout")
|
||||||
|
self.toolbarSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||||||
|
|
||||||
|
self.toolbarLayout.addItem(self.toolbarSpacer)
|
||||||
|
|
||||||
|
self.settingsButton = QPushButton(XslDependencyDialog)
|
||||||
|
self.settingsButton.setObjectName(u"settingsButton")
|
||||||
|
self.settingsButton.setMaximumSize(QSize(28, 28))
|
||||||
|
self.settingsButton.setFlat(True)
|
||||||
|
|
||||||
|
self.toolbarLayout.addWidget(self.settingsButton)
|
||||||
|
|
||||||
|
|
||||||
|
self.verticalLayout.addLayout(self.toolbarLayout)
|
||||||
|
|
||||||
|
self.mainSplitter = QSplitter(XslDependencyDialog)
|
||||||
|
self.mainSplitter.setObjectName(u"mainSplitter")
|
||||||
|
self.mainSplitter.setOrientation(Qt.Orientation.Horizontal)
|
||||||
|
self.mainSplitter.setChildrenCollapsible(True)
|
||||||
|
self.tabWidget = QTabWidget(self.mainSplitter)
|
||||||
self.tabWidget.setObjectName(u"tabWidget")
|
self.tabWidget.setObjectName(u"tabWidget")
|
||||||
self.treeTab = QWidget()
|
self.treeTab = QWidget()
|
||||||
self.treeTab.setObjectName(u"treeTab")
|
self.treeTab.setObjectName(u"treeTab")
|
||||||
self.treeTabLayout = QVBoxLayout(self.treeTab)
|
self.treeTabLayout = QVBoxLayout(self.treeTab)
|
||||||
self.treeTabLayout.setObjectName(u"treeTabLayout")
|
self.treeTabLayout.setObjectName(u"treeTabLayout")
|
||||||
self.searchLayout = QHBoxLayout()
|
|
||||||
self.searchLayout.setObjectName(u"searchLayout")
|
|
||||||
self.searchLabel = QLabel(self.treeTab)
|
|
||||||
self.searchLabel.setObjectName(u"searchLabel")
|
|
||||||
|
|
||||||
self.searchLayout.addWidget(self.searchLabel)
|
|
||||||
|
|
||||||
self.searchEdit = QLineEdit(self.treeTab)
|
|
||||||
self.searchEdit.setObjectName(u"searchEdit")
|
|
||||||
self.searchEdit.setClearButtonEnabled(True)
|
|
||||||
|
|
||||||
self.searchLayout.addWidget(self.searchEdit)
|
|
||||||
|
|
||||||
|
|
||||||
self.treeTabLayout.addLayout(self.searchLayout)
|
|
||||||
|
|
||||||
self.splitter = QSplitter(self.treeTab)
|
self.splitter = QSplitter(self.treeTab)
|
||||||
self.splitter.setObjectName(u"splitter")
|
self.splitter.setObjectName(u"splitter")
|
||||||
self.splitter.setOrientation(Qt.Horizontal)
|
self.splitter.setOrientation(Qt.Orientation.Horizontal)
|
||||||
self.leftWidget = QWidget(self.splitter)
|
self.leftWidget = QWidget(self.splitter)
|
||||||
self.leftWidget.setObjectName(u"leftWidget")
|
self.leftWidget.setObjectName(u"leftWidget")
|
||||||
self.leftLayout = QVBoxLayout(self.leftWidget)
|
self.leftLayout = QVBoxLayout(self.leftWidget)
|
||||||
@@ -67,7 +73,7 @@ class Ui_XslDependencyDialog(object):
|
|||||||
self.fileTree.setHeaderItem(__qtreewidgetitem)
|
self.fileTree.setHeaderItem(__qtreewidgetitem)
|
||||||
self.fileTree.setObjectName(u"fileTree")
|
self.fileTree.setObjectName(u"fileTree")
|
||||||
self.fileTree.setAlternatingRowColors(True)
|
self.fileTree.setAlternatingRowColors(True)
|
||||||
self.fileTree.setSelectionMode(QAbstractItemView.SingleSelection)
|
self.fileTree.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
self.fileTree.setRootIsDecorated(True)
|
self.fileTree.setRootIsDecorated(True)
|
||||||
|
|
||||||
self.leftLayout.addWidget(self.fileTree)
|
self.leftLayout.addWidget(self.fileTree)
|
||||||
@@ -112,18 +118,87 @@ class Ui_XslDependencyDialog(object):
|
|||||||
self.graphTabLayout.addWidget(self.graphContainer)
|
self.graphTabLayout.addWidget(self.graphContainer)
|
||||||
|
|
||||||
self.tabWidget.addTab(self.graphTab, "")
|
self.tabWidget.addTab(self.graphTab, "")
|
||||||
|
self.mainSplitter.addWidget(self.tabWidget)
|
||||||
|
self.sidebarWidget = QWidget(self.mainSplitter)
|
||||||
|
self.sidebarWidget.setObjectName(u"sidebarWidget")
|
||||||
|
self.sidebarLayout = QVBoxLayout(self.sidebarWidget)
|
||||||
|
self.sidebarLayout.setObjectName(u"sidebarLayout")
|
||||||
|
self.sidebarLabel = QLabel(self.sidebarWidget)
|
||||||
|
self.sidebarLabel.setObjectName(u"sidebarLabel")
|
||||||
|
font = QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
self.sidebarLabel.setFont(font)
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.tabWidget)
|
self.sidebarLayout.addWidget(self.sidebarLabel)
|
||||||
|
|
||||||
|
self.searchLabel = QLabel(self.sidebarWidget)
|
||||||
|
self.searchLabel.setObjectName(u"searchLabel")
|
||||||
|
|
||||||
|
self.sidebarLayout.addWidget(self.searchLabel)
|
||||||
|
|
||||||
|
self.searchEdit = QLineEdit(self.sidebarWidget)
|
||||||
|
self.searchEdit.setObjectName(u"searchEdit")
|
||||||
|
self.searchEdit.setClearButtonEnabled(True)
|
||||||
|
|
||||||
|
self.sidebarLayout.addWidget(self.searchEdit)
|
||||||
|
|
||||||
|
self.settingsStack = QStackedWidget(self.sidebarWidget)
|
||||||
|
self.settingsStack.setObjectName(u"settingsStack")
|
||||||
|
self.treeSettingsPage = QWidget()
|
||||||
|
self.treeSettingsPage.setObjectName(u"treeSettingsPage")
|
||||||
|
self.treeSettingsLayout = QVBoxLayout(self.treeSettingsPage)
|
||||||
|
self.treeSettingsLayout.setObjectName(u"treeSettingsLayout")
|
||||||
|
self.treeSettingsLabel = QLabel(self.treeSettingsPage)
|
||||||
|
self.treeSettingsLabel.setObjectName(u"treeSettingsLabel")
|
||||||
|
|
||||||
|
self.treeSettingsLayout.addWidget(self.treeSettingsLabel)
|
||||||
|
|
||||||
|
self.treeSettingsSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
||||||
|
|
||||||
|
self.treeSettingsLayout.addItem(self.treeSettingsSpacer)
|
||||||
|
|
||||||
|
self.settingsStack.addWidget(self.treeSettingsPage)
|
||||||
|
self.graphSettingsPage = QWidget()
|
||||||
|
self.graphSettingsPage.setObjectName(u"graphSettingsPage")
|
||||||
|
self.graphSettingsLayout = QVBoxLayout(self.graphSettingsPage)
|
||||||
|
self.graphSettingsLayout.setObjectName(u"graphSettingsLayout")
|
||||||
|
self.graphSettingsLabel = QLabel(self.graphSettingsPage)
|
||||||
|
self.graphSettingsLabel.setObjectName(u"graphSettingsLabel")
|
||||||
|
|
||||||
|
self.graphSettingsLayout.addWidget(self.graphSettingsLabel)
|
||||||
|
|
||||||
|
self.graphFilterConnectedCheck = QCheckBox(self.graphSettingsPage)
|
||||||
|
self.graphFilterConnectedCheck.setObjectName(u"graphFilterConnectedCheck")
|
||||||
|
self.graphFilterConnectedCheck.setChecked(True)
|
||||||
|
|
||||||
|
self.graphSettingsLayout.addWidget(self.graphFilterConnectedCheck)
|
||||||
|
|
||||||
|
self.graphSettingsSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
||||||
|
|
||||||
|
self.graphSettingsLayout.addItem(self.graphSettingsSpacer)
|
||||||
|
|
||||||
|
self.settingsStack.addWidget(self.graphSettingsPage)
|
||||||
|
|
||||||
|
self.sidebarLayout.addWidget(self.settingsStack)
|
||||||
|
|
||||||
|
self.mainSplitter.addWidget(self.sidebarWidget)
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.mainSplitter)
|
||||||
|
|
||||||
self.statusLabel = QLabel(XslDependencyDialog)
|
self.statusLabel = QLabel(XslDependencyDialog)
|
||||||
self.statusLabel.setObjectName(u"statusLabel")
|
self.statusLabel.setObjectName(u"statusLabel")
|
||||||
|
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.statusLabel.setSizePolicy(sizePolicy)
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.statusLabel)
|
self.verticalLayout.addWidget(self.statusLabel)
|
||||||
|
|
||||||
self.buttonBox = QDialogButtonBox(XslDependencyDialog)
|
self.buttonBox = QDialogButtonBox(XslDependencyDialog)
|
||||||
self.buttonBox.setObjectName(u"buttonBox")
|
self.buttonBox.setObjectName(u"buttonBox")
|
||||||
self.buttonBox.setOrientation(Qt.Horizontal)
|
self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
|
||||||
self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
|
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Close)
|
||||||
self.buttonBox.setCenterButtons(True)
|
self.buttonBox.setCenterButtons(True)
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.buttonBox)
|
self.verticalLayout.addWidget(self.buttonBox)
|
||||||
@@ -133,6 +208,7 @@ class Ui_XslDependencyDialog(object):
|
|||||||
self.buttonBox.rejected.connect(XslDependencyDialog.reject)
|
self.buttonBox.rejected.connect(XslDependencyDialog.reject)
|
||||||
|
|
||||||
self.tabWidget.setCurrentIndex(0)
|
self.tabWidget.setCurrentIndex(0)
|
||||||
|
self.settingsStack.setCurrentIndex(0)
|
||||||
|
|
||||||
|
|
||||||
QMetaObject.connectSlotsByName(XslDependencyDialog)
|
QMetaObject.connectSlotsByName(XslDependencyDialog)
|
||||||
@@ -140,12 +216,23 @@ class Ui_XslDependencyDialog(object):
|
|||||||
|
|
||||||
def retranslateUi(self, XslDependencyDialog):
|
def retranslateUi(self, XslDependencyDialog):
|
||||||
XslDependencyDialog.setWindowTitle(QCoreApplication.translate("XslDependencyDialog", u"XSL-Abh\u00e4ngigkeitsgraph", None))
|
XslDependencyDialog.setWindowTitle(QCoreApplication.translate("XslDependencyDialog", u"XSL-Abh\u00e4ngigkeitsgraph", None))
|
||||||
self.searchLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Suche:", None))
|
#if QT_CONFIG(tooltip)
|
||||||
self.searchEdit.setPlaceholderText(QCoreApplication.translate("XslDependencyDialog", u"XSL-Datei filtern...", None))
|
self.settingsButton.setToolTip(QCoreApplication.translate("XslDependencyDialog", u"Einstellungen ein-/ausblenden", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.settingsButton.setText("")
|
||||||
self.leftLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"XSL-Dateien", None))
|
self.leftLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"XSL-Dateien", None))
|
||||||
self.rightLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Abh\u00e4ngigkeiten", None))
|
self.rightLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Abh\u00e4ngigkeiten", None))
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.treeTab), QCoreApplication.translate("XslDependencyDialog", u"Baumansicht", None))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.treeTab), QCoreApplication.translate("XslDependencyDialog", u"Baumansicht", None))
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.graphTab), QCoreApplication.translate("XslDependencyDialog", u"Netzwerkgraph", None))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.graphTab), QCoreApplication.translate("XslDependencyDialog", u"Netzwerkgraph", None))
|
||||||
|
self.sidebarLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Einstellungen", None))
|
||||||
|
self.searchLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Suche:", None))
|
||||||
|
self.searchEdit.setPlaceholderText(QCoreApplication.translate("XslDependencyDialog", u"XSL-Datei filtern...", None))
|
||||||
|
self.treeSettingsLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Baumansicht-Einstellungen", None))
|
||||||
|
self.graphSettingsLabel.setText(QCoreApplication.translate("XslDependencyDialog", u"Graph-Einstellungen", None))
|
||||||
|
self.graphFilterConnectedCheck.setText(QCoreApplication.translate("XslDependencyDialog", u"Nur betroffene Dateien anzeigen", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.graphFilterConnectedCheck.setToolTip(QCoreApplication.translate("XslDependencyDialog", u"Entfernt alle XSL-Dateien aus dem Graph, die nicht zum Suchbegriff passen und nicht direkt oder indirekt mit passenden Dateien verbunden sind", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.statusLabel.setText("")
|
self.statusLabel.setText("")
|
||||||
# retranslateUi
|
# retranslateUi
|
||||||
|
|
||||||
|
|||||||
+26
-9
@@ -7,13 +7,28 @@ Der Graph wird im Speicher gehalten und bei Änderungen (mtime) automatisch inva
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import time
|
||||||
|
from lxml import etree
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Regex für xsl:import und xsl:include href-Attribute
|
_XSL_NAMESPACE = "http://www.w3.org/1999/XSL/Transform"
|
||||||
_IMPORT_INCLUDE_PATTERN = re.compile(r'<xsl:(?:import|include)\s+href=["\']([^"\']+)["\']', re.IGNORECASE)
|
|
||||||
|
|
||||||
|
def _parse_import_include_hrefs(content: str) -> list[str]:
|
||||||
|
"""Parst XSL-Dateiinhalt und gibt alle href-Werte von xsl:import/xsl:include zurück."""
|
||||||
|
try:
|
||||||
|
root = etree.fromstring(content.encode("utf-8"))
|
||||||
|
except etree.XMLSyntaxError:
|
||||||
|
return []
|
||||||
|
hrefs = []
|
||||||
|
for tag in ("import", "include"):
|
||||||
|
for elem in root.iter(f"{{{_XSL_NAMESPACE}}}{tag}"):
|
||||||
|
href = elem.get("href")
|
||||||
|
if href:
|
||||||
|
hrefs.append(href)
|
||||||
|
return hrefs
|
||||||
|
|
||||||
|
|
||||||
class XslDependencyGraph:
|
class XslDependencyGraph:
|
||||||
@@ -84,8 +99,7 @@ class XslDependencyGraph:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
# Finde alle import/include-Referenzen
|
# Finde alle import/include-Referenzen
|
||||||
for match in _IMPORT_INCLUDE_PATTERN.finditer(content):
|
for href in _parse_import_include_hrefs(content):
|
||||||
href = match.group(1)
|
|
||||||
referenced_path = (xsl_file.parent / href).resolve()
|
referenced_path = (xsl_file.parent / href).resolve()
|
||||||
|
|
||||||
if referenced_path.exists() and referenced_path not in visited:
|
if referenced_path.exists() and referenced_path not in visited:
|
||||||
@@ -138,12 +152,14 @@ class XslDependencyGraph:
|
|||||||
if not xsl_root_dir.exists():
|
if not xsl_root_dir.exists():
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
for xsl_file in sorted(xsl_root_dir.rglob("*.xsl")):
|
for xsl_file in sorted(xsl_root_dir.rglob("*.xsl")):
|
||||||
xsl_file = xsl_file.resolve()
|
xsl_file = xsl_file.resolve()
|
||||||
deps = self.get_dependencies(xsl_file)
|
deps = self.get_dependencies(xsl_file)
|
||||||
graph[xsl_file] = deps
|
graph[xsl_file] = deps
|
||||||
|
elapsed = time.perf_counter() - start
|
||||||
|
|
||||||
logger.info(f"Vollständiger XSL-Graph aufgebaut: {len(graph)} Dateien")
|
logger.info(f"Vollständiger XSL-Graph aufgebaut: {len(graph)} Dateien in {elapsed:.3f}s")
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
def _get_direct_dependencies(self, xsl_file: Path) -> set[Path]:
|
def _get_direct_dependencies(self, xsl_file: Path) -> set[Path]:
|
||||||
@@ -168,8 +184,7 @@ class XslDependencyGraph:
|
|||||||
logger.warning(f"Konnte XSL-Datei nicht lesen: {xsl_file} ({e})")
|
logger.warning(f"Konnte XSL-Datei nicht lesen: {xsl_file} ({e})")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
for match in _IMPORT_INCLUDE_PATTERN.finditer(content):
|
for href in _parse_import_include_hrefs(content):
|
||||||
href = match.group(1)
|
|
||||||
referenced_path = (xsl_file.parent / href).resolve()
|
referenced_path = (xsl_file.parent / href).resolve()
|
||||||
if referenced_path.exists():
|
if referenced_path.exists():
|
||||||
result.add(referenced_path)
|
result.add(referenced_path)
|
||||||
@@ -191,11 +206,13 @@ class XslDependencyGraph:
|
|||||||
if not xsl_root_dir.exists():
|
if not xsl_root_dir.exists():
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
for xsl_file in sorted(xsl_root_dir.rglob("*.xsl")):
|
for xsl_file in sorted(xsl_root_dir.rglob("*.xsl")):
|
||||||
xsl_file = xsl_file.resolve()
|
xsl_file = xsl_file.resolve()
|
||||||
graph[xsl_file] = self._get_direct_dependencies(xsl_file)
|
graph[xsl_file] = self._get_direct_dependencies(xsl_file)
|
||||||
|
elapsed = time.perf_counter() - start
|
||||||
|
|
||||||
logger.info(f"Direkter XSL-Graph aufgebaut: {len(graph)} Dateien")
|
logger.info(f"Direkter XSL-Graph aufgebaut: {len(graph)} Dateien in {elapsed:.3f}s")
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
def invalidate(self, xsl_file: Path | None = None):
|
def invalidate(self, xsl_file: Path | None = None):
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ version = "1.0.0"
|
|||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "connectorx" },
|
{ name = "connectorx" },
|
||||||
|
{ name = "lxml" },
|
||||||
{ name = "polars", extra = ["connectorx", "pyarrow"] },
|
{ name = "polars", extra = ["connectorx", "pyarrow"] },
|
||||||
{ name = "psutil" },
|
{ name = "psutil" },
|
||||||
{ name = "pydantic-settings" },
|
{ name = "pydantic-settings" },
|
||||||
@@ -55,6 +56,7 @@ dev = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "connectorx", specifier = ">=0.4.0" },
|
{ name = "connectorx", specifier = ">=0.4.0" },
|
||||||
|
{ name = "lxml", specifier = ">=6.0.2" },
|
||||||
{ name = "polars", extras = ["connectorx", "pyarrow"], specifier = ">=1.37.0" },
|
{ name = "polars", extras = ["connectorx", "pyarrow"], specifier = ">=1.37.0" },
|
||||||
{ name = "psutil", specifier = ">=6.1.1" },
|
{ name = "psutil", specifier = ">=6.1.1" },
|
||||||
{ name = "pydantic-settings", specifier = ">=2.12.0" },
|
{ name = "pydantic-settings", specifier = ">=2.12.0" },
|
||||||
@@ -69,6 +71,68 @@ dev = [
|
|||||||
{ name = "ruff", specifier = ">=0.14.11" },
|
{ name = "ruff", specifier = ">=0.14.11" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lxml"
|
||||||
|
version = "6.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/15/d4a377b385ab693ce97b472fe0c77c2b16ec79590e688b3ccc71fba19884/lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe", size = 8659801, upload-time = "2025-09-22T04:02:30.113Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c8/e8/c128e37589463668794d503afaeb003987373c5f94d667124ffd8078bbd9/lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d", size = 4659403, upload-time = "2025-09-22T04:02:32.119Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/ce/74903904339decdf7da7847bb5741fc98a5451b42fc419a86c0c13d26fe2/lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d", size = 4966974, upload-time = "2025-09-22T04:02:34.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/d3/131dec79ce61c5567fecf82515bd9bc36395df42501b50f7f7f3bd065df0/lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5", size = 5102953, upload-time = "2025-09-22T04:02:36.054Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3a/ea/a43ba9bb750d4ffdd885f2cd333572f5bb900cd2408b67fdda07e85978a0/lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0", size = 5055054, upload-time = "2025-09-22T04:02:38.154Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/23/6885b451636ae286c34628f70a7ed1fcc759f8d9ad382d132e1c8d3d9bfd/lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba", size = 5352421, upload-time = "2025-09-22T04:02:40.413Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/5b/fc2ddfc94ddbe3eebb8e9af6e3fd65e2feba4967f6a4e9683875c394c2d8/lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0", size = 5673684, upload-time = "2025-09-22T04:02:42.288Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/9c/47293c58cc91769130fbf85531280e8cc7868f7fbb6d92f4670071b9cb3e/lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d", size = 5252463, upload-time = "2025-09-22T04:02:44.165Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/da/ba6eceb830c762b48e711ded880d7e3e89fc6c7323e587c36540b6b23c6b/lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37", size = 4698437, upload-time = "2025-09-22T04:02:46.524Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/24/7be3f82cb7990b89118d944b619e53c656c97dc89c28cfb143fdb7cd6f4d/lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9", size = 5269890, upload-time = "2025-09-22T04:02:48.812Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1b/bd/dcfb9ea1e16c665efd7538fc5d5c34071276ce9220e234217682e7d2c4a5/lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917", size = 5097185, upload-time = "2025-09-22T04:02:50.746Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/04/a60b0ff9314736316f28316b694bccbbabe100f8483ad83852d77fc7468e/lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f", size = 4745895, upload-time = "2025-09-22T04:02:52.968Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d6/bd/7d54bd1846e5a310d9c715921c5faa71cf5c0853372adf78aee70c8d7aa2/lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8", size = 5695246, upload-time = "2025-09-22T04:02:54.798Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/32/5643d6ab947bc371da21323acb2a6e603cedbe71cb4c99c8254289ab6f4e/lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a", size = 5260797, upload-time = "2025-09-22T04:02:57.058Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/da/34c1ec4cff1eea7d0b4cd44af8411806ed943141804ac9c5d565302afb78/lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c", size = 5277404, upload-time = "2025-09-22T04:02:58.966Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/57/4eca3e31e54dc89e2c3507e1cd411074a17565fa5ffc437c4ae0a00d439e/lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b", size = 3670072, upload-time = "2025-09-22T04:03:38.05Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e3/e0/c96cf13eccd20c9421ba910304dae0f619724dcf1702864fd59dd386404d/lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed", size = 4080617, upload-time = "2025-09-22T04:03:39.835Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/5d/b3f03e22b3d38d6f188ef044900a9b29b2fe0aebb94625ce9fe244011d34/lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8", size = 3754930, upload-time = "2025-09-22T04:03:41.565Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5e/5c/42c2c4c03554580708fc738d13414801f340c04c3eff90d8d2d227145275/lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d", size = 8910380, upload-time = "2025-09-22T04:03:01.645Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/4f/12df843e3e10d18d468a7557058f8d3733e8b6e12401f30b1ef29360740f/lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba", size = 4775632, upload-time = "2025-09-22T04:03:03.814Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/0c/9dc31e6c2d0d418483cbcb469d1f5a582a1cd00a1f4081953d44051f3c50/lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601", size = 4975171, upload-time = "2025-09-22T04:03:05.651Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/2b/9b870c6ca24c841bdd887504808f0417aa9d8d564114689266f19ddf29c8/lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed", size = 5110109, upload-time = "2025-09-22T04:03:07.452Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/0c/4f5f2a4dd319a178912751564471355d9019e220c20d7db3fb8307ed8582/lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37", size = 5041061, upload-time = "2025-09-22T04:03:09.297Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/64/554eed290365267671fe001a20d72d14f468ae4e6acef1e179b039436967/lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338", size = 5306233, upload-time = "2025-09-22T04:03:11.651Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/31/1d748aa275e71802ad9722df32a7a35034246b42c0ecdd8235412c3396ef/lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9", size = 5604739, upload-time = "2025-09-22T04:03:13.592Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8f/41/2c11916bcac09ed561adccacceaedd2bf0e0b25b297ea92aab99fd03d0fa/lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd", size = 5225119, upload-time = "2025-09-22T04:03:15.408Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/05/4e5c2873d8f17aa018e6afde417c80cc5d0c33be4854cce3ef5670c49367/lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d", size = 4633665, upload-time = "2025-09-22T04:03:17.262Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/c9/dcc2da1bebd6275cdc723b515f93edf548b82f36a5458cca3578bc899332/lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9", size = 5234997, upload-time = "2025-09-22T04:03:19.14Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/e2/5172e4e7468afca64a37b81dba152fc5d90e30f9c83c7c3213d6a02a5ce4/lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e", size = 5090957, upload-time = "2025-09-22T04:03:21.436Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/b3/15461fd3e5cd4ddcb7938b87fc20b14ab113b92312fc97afe65cd7c85de1/lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d", size = 4764372, upload-time = "2025-09-22T04:03:23.27Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/33/f310b987c8bf9e61c4dd8e8035c416bd3230098f5e3cfa69fc4232de7059/lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec", size = 5634653, upload-time = "2025-09-22T04:03:25.767Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/ff/51c80e75e0bc9382158133bdcf4e339b5886c6ee2418b5199b3f1a61ed6d/lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272", size = 5233795, upload-time = "2025-09-22T04:03:27.62Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/4d/4856e897df0d588789dd844dbed9d91782c4ef0b327f96ce53c807e13128/lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f", size = 5257023, upload-time = "2025-09-22T04:03:30.056Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/85/86766dfebfa87bea0ab78e9ff7a4b4b45225df4b4d3b8cc3c03c5cd68464/lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312", size = 3911420, upload-time = "2025-09-22T04:03:32.198Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fe/1a/b248b355834c8e32614650b8008c69ffeb0ceb149c793961dd8c0b991bb3/lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca", size = 4406837, upload-time = "2025-09-22T04:03:34.027Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/aa/df863bcc39c5e0946263454aba394de8a9084dbaff8ad143846b0d844739/lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c", size = 3822205, upload-time = "2025-09-22T04:03:36.249Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "macholib"
|
name = "macholib"
|
||||||
version = "1.16.4"
|
version = "1.16.4"
|
||||||
|
|||||||
Reference in New Issue
Block a user