-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add gui for editing custom variables added in hab==0.35.0
- Loading branch information
1 parent
123fdb0
commit 0d7d0d4
Showing
11 changed files
with
554 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from Qt import QtWidgets | ||
|
||
from .. import utils | ||
from ..widgets.custom_variable_editor import CustomVariableEditor | ||
|
||
|
||
class EditCustomVariablesAction(QtWidgets.QAction): | ||
"""A QAction that allows the user to edit custom variables. | ||
Args: | ||
resolver (hab.Resolver): The resolver used for settings. | ||
hab_widget (QWidget): The URI widget menu operations are performed on. | ||
verbosity (int, optional): The current verbosity setting. | ||
parent (Qt.QtWidgets.QWidget, optional): Define a parent for this widget. | ||
""" | ||
|
||
def __init__(self, resolver, hab_widget, verbosity=0, parent=None): | ||
super().__init__( | ||
utils.Paths.icon("pencil-box-outline.svg"), | ||
"Edit Custom Variables", | ||
parent, | ||
) | ||
self.hab_widget = hab_widget | ||
self.resolver = resolver | ||
self.verbosity = verbosity | ||
self.setObjectName("edit_custom_variables") | ||
|
||
self.triggered.connect(self.edit_custom_variables) | ||
|
||
def edit_custom_variables(self): | ||
dlg = CustomVariableEditor.create_dialog( | ||
self.resolver, verbosity=self.verbosity, parent=self.parent() | ||
) | ||
dlg.exec_() | ||
|
||
# Ensure the hab_gui respects any changes the user may have made | ||
self.hab_widget.refresh_cache() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .custom_variable_editor import CustomVariableEditor # noqa: F401 |
163 changes: 163 additions & 0 deletions
163
hab_gui/widgets/custom_variable_editor/custom_variable_editor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import logging | ||
|
||
import hab | ||
from Qt import QtCore, QtWidgets | ||
|
||
from ... import utils | ||
from .file_tree_widget_item import FileTreeWidgetItem | ||
from .variable_tree_widget_item import VariableTreeWidgetItem | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class CustomVariableEditor(QtWidgets.QWidget): | ||
"""Create a window that can view and edit custom variables in hab configs/distros. | ||
Args: | ||
resolver (hab.Resolver): The resolver to change verbosity settings on. | ||
verbosity (int): Change the verbosity setting to this value. If None is passed, | ||
all results are be shown without any filtering. | ||
parent (Qt.QtWidgets.QWidget, optional): Define a parent for this widget. | ||
""" | ||
|
||
def __init__(self, resolver, verbosity=0, parent=None): | ||
super().__init__(parent) | ||
self._refresh_on_show = True | ||
self.resolver = resolver | ||
self.verbosity = verbosity | ||
utils.load_ui(__file__, self) | ||
self.setWindowIcon(utils.Paths.icon("habihat.svg")) | ||
|
||
self.uiAddVariableBTN.setIcon(utils.Paths.icon("plus-thick.svg")) | ||
self.uiEditCurrentItemBTN.setIcon(utils.Paths.icon("pencil-box-outline.svg")) | ||
self.uiResetBTN.setIcon(utils.Paths.icon("refresh.svg")) | ||
self.uiRemoveVariableBTN.setIcon(utils.Paths.icon("minus-thick.svg")) | ||
self.uiSaveBTN.setIcon(utils.Paths.icon("content-save.svg")) | ||
|
||
# Configure editing of widget items. _is_refreshing is used to only | ||
# prevent updating the model while refreshing, not other signals. | ||
self._is_refreshing = False | ||
self.uiVariableTREE.model().dataChanged.connect(self.editing_finished) | ||
|
||
def add_variable(self): | ||
"""Add a new variable to the selected FileTreeWidgetItem""" | ||
item = self.uiVariableTREE.currentItem() | ||
if "Undefined" in item.parser.variables: | ||
QtWidgets.QMessageBox.information( | ||
self, | ||
"Variable already defined", | ||
"You already have a variable named Undefined. Change the name " | ||
"of that variable before adding a new one.", | ||
) | ||
else: | ||
item.parser.variables["Undefined"] = "Undefined" | ||
VariableTreeWidgetItem(item, "Undefined") | ||
# Mark the item as dirty | ||
item.dirty = True | ||
|
||
@property | ||
def dirty(self): | ||
"""Generator that yields any FileTreeWidgetItem's that are modified.""" | ||
for index in range(self.uiVariableTREE.topLevelItemCount()): | ||
child = self.uiVariableTREE.topLevelItem(index) | ||
if child.dirty: | ||
yield child | ||
|
||
def edit_cell(self): | ||
"""Edit the currently selected cell""" | ||
index = self.uiVariableTREE.currentIndex() | ||
self.uiVariableTREE.edit(index) | ||
|
||
def editing_finished(self, top_left, bottom_right, roles): | ||
if self._is_refreshing: | ||
return | ||
|
||
if QtCore.Qt.EditRole in roles: | ||
item = self.uiVariableTREE.itemFromIndex(top_left) | ||
column = top_left.column() | ||
if column == 0: | ||
item.variable_name = item.text(column) | ||
elif column == 1: | ||
item.value = item.text(column) | ||
|
||
def current_changed(self, current=None, previous=None): | ||
"""Enable buttons based on the current selection.""" | ||
item = self.uiVariableTREE.currentItem() | ||
is_file = isinstance(item, FileTreeWidgetItem) | ||
self.uiAddVariableBTN.setEnabled(is_file) | ||
self.uiEditCurrentItemBTN.setEnabled(not is_file) | ||
self.uiRemoveVariableBTN.setEnabled(not is_file) | ||
|
||
def refresh(self): | ||
self._is_refreshing = True | ||
try: | ||
self.uiVariableTREE.clear() | ||
|
||
for forest in (self.resolver.configs, self.resolver.distros): | ||
for row in self.resolver.dump_forest(forest, attr=None): | ||
parser = row.node | ||
if isinstance(parser, hab.parsers.DistroVersion): | ||
if parser.load(parser.filename).get("variable_editor", False): | ||
FileTreeWidgetItem(self.uiVariableTREE, parser) | ||
|
||
self.uiVariableTREE.expandAll() | ||
self.uiVariableTREE.resizeColumnToContents(0) | ||
|
||
self.current_changed() | ||
finally: | ||
self._is_refreshing = False | ||
|
||
@property | ||
def refresh_on_show(self): | ||
"""Should this automatically refresh when the widget is shown.""" | ||
return self._refresh_on_show | ||
|
||
@refresh_on_show.setter | ||
def refresh_on_show(self, state): | ||
self._refresh_on_show = state | ||
|
||
def remove_variable(self): | ||
"""Remove the currently selected variable""" | ||
item = self.uiVariableTREE.currentItem() | ||
item.remove_variable() | ||
parent = item.parent() | ||
idx = parent.indexOfChild(item) | ||
parent.takeChild(idx) | ||
parent.dirty = True | ||
|
||
def reset(self): | ||
"""Revert any un-saved changes.""" | ||
self.resolver.clear_caches() | ||
self.refresh() | ||
|
||
def save(self): | ||
"""Save all changes to disk""" | ||
for parser in self.dirty: | ||
parser.save() | ||
|
||
# Re-display the saved data | ||
self.reset() | ||
|
||
def showEvent(self, event): # noqa: N802 | ||
super().showEvent(event) | ||
if self.refresh_on_show: | ||
self.refresh() | ||
|
||
@classmethod | ||
def create_dialog(cls, resolver, verbosity=0, title="Edit Variables", parent=None): | ||
"""Create a simple standalone QDialog containing this widget. | ||
Args: | ||
resolver (hab.Resolver): The resolver to change verbosity settings on. | ||
verbosity (int): Change the verbosity setting to this value. If None is passed, | ||
all results are be shown without any filtering. | ||
title (str, optional): The window title of the created dialog. | ||
parent (Qt.QtWidgets.QWidget, optional): Define a parent for this widget. | ||
""" | ||
dlg = QtWidgets.QDialog(parent=parent) | ||
dlg.setWindowTitle(title) | ||
dlg.setWindowIcon(utils.Paths.icon("pencil-box-outline.svg")) | ||
layout = QtWidgets.QVBoxLayout(dlg) | ||
dlg.uiVariableWGT = cls(resolver, verbosity=verbosity, parent=dlg) | ||
layout.addWidget(dlg.uiVariableWGT) | ||
return dlg |
88 changes: 88 additions & 0 deletions
88
hab_gui/widgets/custom_variable_editor/file_tree_widget_item.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import json | ||
|
||
import hab.utils | ||
from Qt import QtCore, QtWidgets | ||
|
||
from .variable_tree_widget_item import VariableTreeWidgetItem | ||
|
||
|
||
class FileTreeWidgetItem(QtWidgets.QTreeWidgetItem): | ||
def __init__(self, parent, parser): | ||
super().__init__(parent) | ||
self.parser = parser | ||
# Add a tracking variable to tell if the parser is dirty | ||
if not hasattr(self.parser, "dirty"): | ||
self.parser.dirty = False | ||
|
||
# Add a child item that shows the filename. It should not be editable. | ||
self.filename_item = QtWidgets.QTreeWidgetItem(self) | ||
self.filename_item.setFlags(QtCore.Qt.NoItemFlags) | ||
|
||
self.refresh() | ||
|
||
@property | ||
def dirty(self): | ||
return self.parser.dirty | ||
|
||
@dirty.setter | ||
def dirty(self, state): | ||
changed = state != self.parser.dirty | ||
self.parser.dirty = state | ||
if changed: | ||
self.setText(0, self.name) | ||
|
||
@property | ||
def name(self): | ||
name = self.parser.name | ||
if self.dirty: | ||
return f"{name}*" | ||
return name | ||
|
||
def refresh(self): | ||
self.setText(0, self.name) | ||
self.filename_item.setText(0, "Filename") | ||
self.filename_item.setText(1, str(self.parser.filename)) | ||
|
||
# self.takeChildren() | ||
# for variable_name in self.parser.variables: | ||
# VariableTreeWidgetItem(self, variable_name) | ||
|
||
for index, variable_name in enumerate(self.parser.variables): | ||
# Get the existing variable item if possible. Index 0 is the | ||
# filename item. | ||
item = self.child(index + 1) | ||
if item: | ||
item.variable_name = variable_name | ||
item.refresh() | ||
else: | ||
# Otherwise add a new item | ||
VariableTreeWidgetItem(self, variable_name) | ||
|
||
# If any variables were removed, remove their tree widget items | ||
variable_count = len(self.parser.variables) + 1 | ||
for _ in range(variable_count, self.childCount() + 1): | ||
self.removeChild(self.child(variable_count)) | ||
|
||
def save(self): | ||
"""Save the variable changes to disk. | ||
NOTE: This saves the data as regular json data not json5. Any comments, | ||
etc will be cleared by calling this method. | ||
Returns: | ||
bool: Returns if this was dirty and updated data was saved to disk. | ||
""" | ||
if not self.dirty: | ||
return False | ||
|
||
# Reload data from disk | ||
raw_data = hab.utils.load_json_file(self.parser.filename) | ||
|
||
# Update the variables section with the changes. | ||
raw_data["variables"] = self.parser.variables | ||
|
||
# Save changes over top of the existing file. | ||
with self.parser.filename.open("w") as fle: | ||
json.dump(raw_data, fle, indent=4, cls=hab.utils.HabJsonEncoder) | ||
|
||
return True |
Oops, something went wrong.