buttons.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. ########################################################################
  2. # Searx-Qt - Lightweight desktop application for Searx.
  3. # Copyright (C) 2020-2022 CYBERDEViL
  4. #
  5. # This file is part of Searx-Qt.
  6. #
  7. # Searx-Qt is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # Searx-Qt is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ########################################################################
  21. from PyQt5.QtWidgets import (
  22. QToolButton,
  23. QFrame,
  24. QHBoxLayout,
  25. QLabel,
  26. QMenu,
  27. QWidgetAction,
  28. QCheckBox
  29. )
  30. from PyQt5.QtCore import Qt
  31. from PyQt5.QtGui import QCursor
  32. from searxqt.translations import _
  33. class Button(QToolButton):
  34. """ QToolButton with option to set text at creation.
  35. """
  36. def __init__(self, text="", parent=None):
  37. QToolButton.__init__(self, parent=parent)
  38. self.setText(text)
  39. class CheckboxOptionsButton(QFrame):
  40. def __init__(self, labelName="", parent=None):
  41. QFrame.__init__(self, parent=parent)
  42. # Keep a list of disabled keys set with setKeyDisabled()
  43. self.__disabledKeys = []
  44. # Full label will be shown when this is True, character limited label
  45. # will be shown when this is False.
  46. self.__expanded = True
  47. # The character limit when the state of the label is collapsed (
  48. # self.__expanded = False)
  49. self.__collapsedCharLimit = 32
  50. layout = QHBoxLayout(self)
  51. layout.setContentsMargins(3, 3, 3, 3)
  52. self._labelName = labelName
  53. self.setFrameShape(QFrame.StyledPanel)
  54. openButton = Button("+", self)
  55. layout.addWidget(openButton, 0, Qt.AlignLeft | Qt.AlignTop)
  56. self._label = QLabel("", self)
  57. self._label.setWordWrap(True)
  58. layout.addWidget(self._label, 0, Qt.AlignLeft | Qt.AlignTop)
  59. openButton.clicked.connect(self.__toggleMenu)
  60. self.__genLabel()
  61. def saveSettings(self):
  62. """ Returns current widget state in a dict (serialised)
  63. """
  64. return {
  65. 'expanded': self.__expanded
  66. }
  67. def loadSettings(self, data):
  68. self.__expanded = data.get('expanded', True)
  69. self.__genLabel()
  70. def keyDisabled(self, key):
  71. """
  72. @return: Returns True if the given key is disabled. Flase if
  73. enabled.
  74. @rtype: bool
  75. """
  76. return bool(key in self.__disabledKeys)
  77. def setKeyDisabled(self, key):
  78. """ Disable the given key from being toggled.
  79. """
  80. self.__disabledKeys.append(key)
  81. def setKeyEnabled(self, key):
  82. """ Re-enable the given key so the user may toggle it again.
  83. """
  84. self.__disabledKeys.remove(key)
  85. def reGenerate(self):
  86. self.__genLabel()
  87. @property
  88. def labelName(self):
  89. return self._labelName
  90. @labelName.setter
  91. def labelName(self, name):
  92. self._labelName = name
  93. def __genLabel(self):
  94. """ Join all checked option names into a comma separated string.
  95. """
  96. str_ = _("All")
  97. checkedOptions = self.getCheckedOptionNames()
  98. if checkedOptions:
  99. str_ = ", ".join(checkedOptions)
  100. self._label.setToolTip(str_)
  101. # When the label state is collapsed (self.__expanded == false) the
  102. # character limit (self.__collapsedCharLimit) should be honored.
  103. if not self.__expanded and len(str_) > self.__collapsedCharLimit:
  104. str_ = str_[0:self.__collapsedCharLimit - 1]
  105. str_ += "..."
  106. self._label.setText("<b>" + self._labelName + "</b>: " + str_)
  107. def __addWidgets(self, menu):
  108. for key, name, state in self.getOptions():
  109. action = QWidgetAction(menu)
  110. widget = QCheckBox(name, menu)
  111. widget.setTristate(False)
  112. widget.setChecked(state)
  113. action.setDefaultWidget(widget)
  114. if key in self.__disabledKeys:
  115. widget.setEnabled(False)
  116. widget.stateChanged.connect(
  117. lambda state, key=key:
  118. self.__checkBoxStateChanged(key, state)
  119. )
  120. menu.addAction(action)
  121. def __toggleMenu(self):
  122. menu = QMenu(self)
  123. menu.setStyleSheet("QMenu { menu-scrollable: 1; }")
  124. self.addCustomWidgetsTop(menu)
  125. self.__addWidgets(menu)
  126. menu.exec(QCursor.pos())
  127. def __toggleLabelState(self):
  128. """ Toggles the expanded/collapsed state of the label.
  129. """
  130. if self.__expanded:
  131. self.__expanded = False
  132. else:
  133. self.__expanded = True
  134. self.__genLabel()
  135. def __checkBoxStateChanged(self, key, state):
  136. self.optionToggled(key, state == 2)
  137. self.__genLabel()
  138. """ Methods below may be reimplemented.
  139. """
  140. def addCustomWidgetsTop(self, menu):
  141. pass
  142. def hasEnabledCheckedKeys(self):
  143. """ Returns True when there are checked options that are enabled for the
  144. user to uncheck. False when not. The add button it's context menu
  145. makes use of this to determine to show or not (if there are options
  146. to uncheck when `clear all` is triggered.)
  147. @rtype: bool
  148. """
  149. for key, name, state in self.getOptions():
  150. if state and not self.keyDisabled(key):
  151. return True
  152. return False
  153. def uncheckAllEnabledKeys(self):
  154. """ Unchecks all checked keys that are enabled.
  155. """
  156. for key, name, state in self.getOptions():
  157. if state and not self.keyDisabled(key):
  158. self.optionToggled(key, False)
  159. self.__genLabel()
  160. """ Methods below should be reimplemented.
  161. """
  162. def getCheckedOptionNames(self):
  163. """ Should return a list with checked option names. This will
  164. be used to generate the label.
  165. @return: should return a list with strings.
  166. @rtype: list
  167. """
  168. # Re-implement this!
  169. return []
  170. def getOptions(self):
  171. """ Should return a list with options tuple(key, name, state)
  172. This will be used to generate the options.
  173. """
  174. # Re-implement this!
  175. return []
  176. def optionToggled(self, key, state):
  177. # Re-implement to do stuff here.
  178. return
  179. """ QFrame re-implementations
  180. """
  181. def contextMenuEvent(self, event):
  182. menu = QMenu(self)
  183. # Uncheck all action
  184. clearAction = menu.addAction(_("Uncheck all"))
  185. if not self.hasEnabledCheckedKeys():
  186. clearAction.setEnabled(False)
  187. else:
  188. clearAction.triggered.connect(self.uncheckAllEnabledKeys)
  189. # Hide action (Hides this widget)
  190. hideAction = menu.addAction(_("Hide"))
  191. hideAction.triggered.connect(self.hide)
  192. menu.exec_(self.mapToGlobal(event.pos()))
  193. def mousePressEvent(self, event):
  194. """ Toggle the expanded/collapsed state of the label.
  195. """
  196. if event.button() == Qt.LeftButton:
  197. self.__toggleLabelState()
  198. event.accept()
  199. else:
  200. event.ignore()