-
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 UI to enable optional distros when launching
This is a gui interface allowing configs to define common optional distros and allowing a user to enable those distros. This interfaces with the `--requirement` hab cli argument.
- Loading branch information
1 parent
fc39f82
commit bc11cc3
Showing
10 changed files
with
378 additions
and
5 deletions.
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
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.
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
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
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
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,73 @@ | ||
from hab.solvers import Solver | ||
from hab.utils import NotSet | ||
from Qt.QtCore import QTimer | ||
|
||
from .. import utils | ||
from .name_picker import NamePicker | ||
|
||
|
||
class DistroPicker(NamePicker): | ||
"""A widget for picking from a list of optional distros. | ||
This displays the `optional_distros` config setting. Any distros checked use | ||
`hab.Resolver.forced_requirements` to load those distros. See the cli argument | ||
`--requirement` for more info. | ||
""" | ||
|
||
pref_name = "distro_picker" | ||
|
||
def __init__(self, settings, title="Options", label="Distro", parent=None): | ||
super().__init__(settings, title=title, label=label, parent=parent) | ||
# This widget needs to update the hab resolver before other widgets | ||
# are updated. This signal is used to update forced_requirements. | ||
self.settings.uri_changing.connect(self.refresh) | ||
|
||
def item_changed(self, item, column): | ||
"""Called when a item is modified, saves the user prefs when a checked | ||
state is updated and updates the displayed aliases.""" | ||
super().item_changed(item, column) | ||
# Ensure the UI is updated with the new forced_requirements | ||
self.update_requirements() | ||
QTimer.singleShot(0, self.uri_changed) | ||
|
||
def reset_to_default(self): | ||
"""Reset the checked state of names to the default values, clearing | ||
saved user_prefs for the current URI. | ||
""" | ||
super().reset_to_default() | ||
# Refresh the alias button widget | ||
self.update_requirements() | ||
self.uri_changed() | ||
|
||
def update_requirements(self): | ||
# Ensure the UI is updated with the new forced_requirements | ||
selected = self.selected() | ||
forced_requirements = Solver.simplify_requirements(list(selected)) | ||
|
||
# Preserve any requirements passed via the cli. | ||
cli_reqs = self.settings.resolver.__forced_requirements__ | ||
if cli_reqs: | ||
# If the same distro is specified, the GUI's requirement should win. | ||
forced_requirements = dict(cli_reqs, **forced_requirements) | ||
|
||
self.settings.resolver.forced_requirements = forced_requirements | ||
|
||
def uri_changed(self): | ||
"""Work function that forces the gui to update its aliases.""" | ||
|
||
# The refresh method is called when this signal is emitted, don't | ||
# double process it by blocking signals. | ||
with utils.block_signals([self.name_tree]): | ||
self.settings.uri_changed.emit(self.settings.uri) | ||
|
||
def refresh(self, uri): | ||
resolver = self.settings.resolver | ||
cfg = resolver.resolve(uri) | ||
optional = cfg.optional_distros | ||
if optional is NotSet: | ||
optional = {} | ||
self.set_names(optional, uri=uri) | ||
|
||
# Ensure the alias_buttons widget has the updated requirements before | ||
# it refreshes from the `uri_changed` signal emited later. | ||
self.update_requirements() |
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,142 @@ | ||
from hab.parsers import HabBase | ||
from Qt import QtCore, QtWidgets | ||
|
||
from .. import utils | ||
|
||
|
||
class NamePicker(QtWidgets.QGroupBox): | ||
"""A widget for selecting from a set of names with a description. | ||
Provides a list view of names the user can check. A description can be shown | ||
next to each name. | ||
""" | ||
|
||
pref_name = None | ||
"""Defines the name of the user_pref key used to store selected names. If not | ||
specified then saving is disabled for this class. If enabled then user_prefs | ||
are saved when the check state of any item in this tree is updated. The saved | ||
names are stored per modified URI, see :py:meth:`standardize_uri` for details. | ||
""" | ||
|
||
def __init__(self, settings, title="Options", label="Distro", parent=None): | ||
super().__init__(parent=parent) | ||
self.settings = settings | ||
self.default_selection = set() | ||
utils.load_ui(__file__, self) | ||
self.setTitle(title) | ||
self.name_tree.setHeaderLabel(label) | ||
self.reset_to_default_btn.setIcon(utils.Paths.icon("arrow-left-top-bold")) | ||
self.name_tree.itemChanged.connect(self.item_changed) | ||
self.reset_to_default_btn.released.connect(self.reset_to_default) | ||
|
||
def item_changed(self, item, column): | ||
"""Called when a item is modified, saves the user prefs when a checked | ||
state is updated.""" | ||
if column != 0: | ||
return | ||
self.save_user_selection() | ||
|
||
def names(self): | ||
"""Returns a dict of the current state of this widget. | ||
The key is the name shown in column 1. The value is a 2 item list containing | ||
the description shown to the user in column 2 and if name is checked by | ||
default. | ||
""" | ||
ret = {} | ||
for index in range(self.name_tree.topLevelItemCount()): | ||
item = self.name_tree.topLevelItem(index) | ||
checked = item.checkState(0) == QtCore.Qt.Checked | ||
ret[item.text(0)] = [item.text(1), checked] | ||
return ret | ||
|
||
def set_names(self, names, uri=None): | ||
self.name_tree.clear() | ||
# Reset the default selection | ||
self.default_selection = set() | ||
with utils.block_signals([self.name_tree]): | ||
for name, settings in names.items(): | ||
item = QtWidgets.QTreeWidgetItem(self.name_tree, [name, settings[0]]) | ||
item.setToolTip(1, settings[0]) | ||
# Build the `default_selection` set for the current URI | ||
if len(settings) > 1 and settings[1]: | ||
self.default_selection.add(name) | ||
item.setCheckState(0, QtCore.Qt.Unchecked) | ||
|
||
self.name_tree.resizeColumnToContents(0) | ||
user_selection = self.user_selection(uri) | ||
if user_selection is None: | ||
self.set_selected(self.default_selection) | ||
self.reset_to_default_btn.setDisabled(True) | ||
else: | ||
self.set_selected(user_selection) | ||
self.reset_to_default_btn.setDisabled(False) | ||
|
||
def reset_to_default(self): | ||
"""Reset the checked state of names to the default values, clearing | ||
saved user_prefs for the current URI. | ||
""" | ||
self.set_selected(self.default_selection) | ||
self.save_user_selection(reset=True) | ||
|
||
def selected(self): | ||
"""Returns the checked names as a list.""" | ||
ret = set() | ||
for index in range(self.name_tree.topLevelItemCount()): | ||
item = self.name_tree.topLevelItem(index) | ||
if item.checkState(0) == QtCore.Qt.Checked: | ||
ret.add(item.text(0)) | ||
return ret | ||
|
||
def set_selected(self, selected): | ||
"""Update the checked state to just these names.""" | ||
with utils.block_signals([self.name_tree]): | ||
for index in range(self.name_tree.topLevelItemCount()): | ||
item = self.name_tree.topLevelItem(index) | ||
name = item.text(0) | ||
item.setCheckState( | ||
0, QtCore.Qt.Checked if name in selected else QtCore.Qt.Unchecked | ||
) | ||
|
||
def sizeHint(self): # noqa: N802 | ||
return QtCore.QSize(0, 160) | ||
|
||
def standardize_uri(self, uri): | ||
"""Modify the URI for saving in user_prefs. This implementation discards | ||
all but the top level item.""" | ||
return uri.split(HabBase.separator)[0] | ||
|
||
def user_selection(self, uri): | ||
"""Returns the names selected for the current URI saved in user_prefs | ||
as a dict or None if user_prefs are disabled. | ||
""" | ||
if not self.pref_name: | ||
return None | ||
|
||
if uri is None: | ||
uri = self.settings.uri | ||
|
||
uri = self.standardize_uri(uri) | ||
return self.settings.user_pref(self.pref_name, {}).get(uri, None) | ||
|
||
def save_user_selection(self, reset=False): | ||
"""Saves the currently selected names into user_prefs if enabled. | ||
Also always updates the enabled state of the reset button. | ||
""" | ||
uri = self.standardize_uri(self.settings.uri) | ||
user_selections = self.settings.user_pref(self.pref_name, {}) | ||
selected = self.selected() | ||
is_default = selected == self.default_selection | ||
if reset and is_default: | ||
# Only remove the URI from user_prefs if reset was pressed so we | ||
# can save having all optional dependencies disabled. | ||
if uri in user_selections: | ||
del user_selections[uri] | ||
self.reset_to_default_btn.setDisabled(True) | ||
else: | ||
user_selections[uri] = list(selected) | ||
self.reset_to_default_btn.setDisabled(False) | ||
|
||
if self.pref_name: | ||
self.settings.set_user_pref(self.pref_name, user_selections) |
Oops, something went wrong.