dataladcmd_ui.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. from types import MappingProxyType
  2. from typing import (
  3. Dict,
  4. )
  5. from PySide6.QtCore import (
  6. QObject,
  7. Signal,
  8. Slot,
  9. )
  10. from PySide6.QtGui import QAction
  11. from PySide6.QtWidgets import (
  12. QDialogButtonBox,
  13. QFormLayout,
  14. QLabel,
  15. QScrollArea,
  16. QWidget,
  17. )
  18. from .param_form_utils import populate_form_w_params
  19. from .api_utils import get_cmd_displayname
  20. from .active_suite import spec as active_suite
  21. class GooeyDataladCmdUI(QObject):
  22. configured_dataladcmd = Signal(str, MappingProxyType)
  23. def __init__(self, app, ui_parent: QWidget):
  24. super().__init__()
  25. self._app = app
  26. self._ui_parent = ui_parent
  27. # start out disabled, there will be no populated form
  28. self._ui_parent.setDisabled(True)
  29. self._pform = None
  30. self._cmd_title = None
  31. @property
  32. def pwidget(self):
  33. return self._ui_parent
  34. @property
  35. def pform(self):
  36. if self._pform is None:
  37. pw = self.pwidget
  38. # make sure all expected UI blocks are present
  39. self._cmd_title = pw.findChild(QLabel, 'cmdTabTitle')
  40. scrollarea_content = pw.findChild(QScrollArea).widget()
  41. buttonbox = pw.findChild(QDialogButtonBox, 'cmdTabButtonBox')
  42. for w in (self._cmd_title, scrollarea_content, buttonbox):
  43. assert w
  44. # create main form layout for the parameters to appear in
  45. form_layout = QFormLayout(scrollarea_content)
  46. form_layout.setObjectName('cmdTabFormLayout')
  47. self._pform = form_layout
  48. # connect the dialog interaction with slots in this instance
  49. # we run the retrieval helper on ok/run
  50. buttonbox.accepted.connect(self._retrieve_input)
  51. # we disable the UI (however that might look like) on cancel
  52. buttonbox.rejected.connect(self.disable)
  53. return self._pform
  54. @Slot(str, dict)
  55. def configure(
  56. self,
  57. api=None,
  58. cmdname: str = None,
  59. cmdkwargs: Dict or None = None):
  60. if cmdkwargs is None:
  61. cmdkwargs = dict()
  62. # figure out the object that emitted the signal triggering
  63. # this slot execution. Will be None for a regular method call.
  64. # we can use this to update the method parameter values
  65. # with information from menu-items, or tree nodes clicked
  66. sender = self.sender()
  67. if sender is not None and isinstance(sender, QAction):
  68. if api is None:
  69. api = sender.data().get('__api__')
  70. if cmdname is None:
  71. cmdname = sender.data().get('__cmd_name__')
  72. # pull in any signal-provided kwargs for the command
  73. # unless they have been also specified directly to the method
  74. cmdkwargs = {
  75. k: v for k, v in sender.data().items()
  76. if k not in ('__cmd_name__', '__api__')
  77. and k not in cmdkwargs
  78. }
  79. assert cmdname is not None, \
  80. "GooeyDataladCmdUI.configure() called without command name"
  81. self._app.get_widget('contextTabs').setCurrentWidget(self.pwidget)
  82. self.reset_form()
  83. populate_form_w_params(
  84. api,
  85. self._app.rootpath,
  86. self.pform,
  87. cmdname,
  88. cmdkwargs,
  89. )
  90. # set title afterwards, form might just have been created first, lazily
  91. self._cmd_title.setText(
  92. # remove potential shortcut marker
  93. get_cmd_displayname(api, cmdname).replace('&', '')
  94. )
  95. self._cmd_title.setToolTip(f'API command: `{cmdname}`')
  96. # deposit the command name in the widget, to be retrieved later by
  97. # retrieve_parameters()
  98. self.pform.datalad_cmd_name = cmdname
  99. # make sure the UI is visible
  100. self.pwidget.setEnabled(True)
  101. @Slot()
  102. def _retrieve_input(self):
  103. from .param_widgets import _NoValue
  104. params = dict()
  105. for i in range(self.pform.rowCount()):
  106. # the things is wrapped into a QWidgetItem layout class, hence .wid
  107. field_widget = self.pform.itemAt(i, QFormLayout.FieldRole).wid
  108. # _get_datalad_param_spec() is our custom private adhoc method
  109. # expected to return a dict with a parameter setting, or an
  110. # empty dict, when the default shall be used.
  111. params.update({
  112. k: v for k, v in field_widget.get_gooey_param_spec().items()
  113. if v is not _NoValue
  114. })
  115. # take a peek, TODO remove
  116. from pprint import pprint
  117. pprint(params)
  118. self.disable()
  119. self.configured_dataladcmd.emit(
  120. self.pform.datalad_cmd_name,
  121. MappingProxyType(params),
  122. )
  123. @Slot()
  124. def disable(self):
  125. """Disable UI when no longer needed for configuration"""
  126. # only disaable, not hide, to keep the info what ran (was configured)
  127. # accessible. Widget empties itself on reconfigure
  128. self.pwidget.setDisabled(True)
  129. def reset_form(self):
  130. if self._cmd_title:
  131. self._cmd_title.setText('')
  132. for i in range(self.pform.rowCount() - 1, -1, -1):
  133. # empty the form layout (deletes all widgets)
  134. self.pform.removeRow(i)
  135. self.disable()