123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- ########################################################################
- # Searx-Qt - Lightweight desktop application for Searx.
- # Copyright (C) 2020-2022 CYBERDEViL
- #
- # This file is part of Searx-Qt.
- #
- # Searx-Qt is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Searx-Qt is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- #
- ########################################################################
- from PyQt5.QtWidgets import (
- QToolButton,
- QFrame,
- QHBoxLayout,
- QLabel,
- QMenu,
- QWidgetAction,
- QCheckBox
- )
- from PyQt5.QtCore import Qt
- from PyQt5.QtGui import QCursor
- from searxqt.translations import _
- class Button(QToolButton):
- """ QToolButton with option to set text at creation.
- """
- def __init__(self, text="", parent=None):
- QToolButton.__init__(self, parent=parent)
- self.setText(text)
- class CheckboxOptionsButton(QFrame):
- def __init__(self, labelName="", parent=None):
- QFrame.__init__(self, parent=parent)
- # Keep a list of disabled keys set with setKeyDisabled()
- self.__disabledKeys = []
- # Full label will be shown when this is True, character limited label
- # will be shown when this is False.
- self.__expanded = True
- # The character limit when the state of the label is collapsed (
- # self.__expanded = False)
- self.__collapsedCharLimit = 32
- layout = QHBoxLayout(self)
- layout.setContentsMargins(3, 3, 3, 3)
- self._labelName = labelName
- self.setFrameShape(QFrame.StyledPanel)
- openButton = Button("+", self)
- layout.addWidget(openButton, 0, Qt.AlignLeft | Qt.AlignTop)
- self._label = QLabel("", self)
- self._label.setWordWrap(True)
- layout.addWidget(self._label, 0, Qt.AlignLeft | Qt.AlignTop)
- openButton.clicked.connect(self.__toggleMenu)
- self.__genLabel()
- def saveSettings(self):
- """ Returns current widget state in a dict (serialised)
- """
- return {
- 'expanded': self.__expanded
- }
- def loadSettings(self, data):
- self.__expanded = data.get('expanded', True)
- self.__genLabel()
- def keyDisabled(self, key):
- """
- @return: Returns True if the given key is disabled. Flase if
- enabled.
- @rtype: bool
- """
- return bool(key in self.__disabledKeys)
- def setKeyDisabled(self, key):
- """ Disable the given key from being toggled.
- """
- self.__disabledKeys.append(key)
- def setKeyEnabled(self, key):
- """ Re-enable the given key so the user may toggle it again.
- """
- self.__disabledKeys.remove(key)
- def reGenerate(self):
- self.__genLabel()
- @property
- def labelName(self):
- return self._labelName
- @labelName.setter
- def labelName(self, name):
- self._labelName = name
- def __genLabel(self):
- """ Join all checked option names into a comma separated string.
- """
- str_ = _("All")
- checkedOptions = self.getCheckedOptionNames()
- if checkedOptions:
- str_ = ", ".join(checkedOptions)
- self._label.setToolTip(str_)
- # When the label state is collapsed (self.__expanded == false) the
- # character limit (self.__collapsedCharLimit) should be honored.
- if not self.__expanded and len(str_) > self.__collapsedCharLimit:
- str_ = str_[0:self.__collapsedCharLimit - 1]
- str_ += "..."
- self._label.setText("<b>" + self._labelName + "</b>: " + str_)
- def __addWidgets(self, menu):
- for key, name, state in self.getOptions():
- action = QWidgetAction(menu)
- widget = QCheckBox(name, menu)
- widget.setTristate(False)
- widget.setChecked(state)
- action.setDefaultWidget(widget)
- if key in self.__disabledKeys:
- widget.setEnabled(False)
- widget.stateChanged.connect(
- lambda state, key=key:
- self.__checkBoxStateChanged(key, state)
- )
- menu.addAction(action)
- def __toggleMenu(self):
- menu = QMenu(self)
- menu.setStyleSheet("QMenu { menu-scrollable: 1; }")
- self.addCustomWidgetsTop(menu)
- self.__addWidgets(menu)
- menu.exec(QCursor.pos())
- def __toggleLabelState(self):
- """ Toggles the expanded/collapsed state of the label.
- """
- if self.__expanded:
- self.__expanded = False
- else:
- self.__expanded = True
- self.__genLabel()
- def __checkBoxStateChanged(self, key, state):
- self.optionToggled(key, state == 2)
- self.__genLabel()
- """ Methods below may be reimplemented.
- """
- def addCustomWidgetsTop(self, menu):
- pass
- def hasEnabledCheckedKeys(self):
- """ Returns True when there are checked options that are enabled for the
- user to uncheck. False when not. The add button it's context menu
- makes use of this to determine to show or not (if there are options
- to uncheck when `clear all` is triggered.)
- @rtype: bool
- """
- for key, name, state in self.getOptions():
- if state and not self.keyDisabled(key):
- return True
- return False
- def uncheckAllEnabledKeys(self):
- """ Unchecks all checked keys that are enabled.
- """
- for key, name, state in self.getOptions():
- if state and not self.keyDisabled(key):
- self.optionToggled(key, False)
- self.__genLabel()
- """ Methods below should be reimplemented.
- """
- def getCheckedOptionNames(self):
- """ Should return a list with checked option names. This will
- be used to generate the label.
- @return: should return a list with strings.
- @rtype: list
- """
- # Re-implement this!
- return []
- def getOptions(self):
- """ Should return a list with options tuple(key, name, state)
- This will be used to generate the options.
- """
- # Re-implement this!
- return []
- def optionToggled(self, key, state):
- # Re-implement to do stuff here.
- return
- """ QFrame re-implementations
- """
- def contextMenuEvent(self, event):
- menu = QMenu(self)
- # Uncheck all action
- clearAction = menu.addAction(_("Uncheck all"))
- if not self.hasEnabledCheckedKeys():
- clearAction.setEnabled(False)
- else:
- clearAction.triggered.connect(self.uncheckAllEnabledKeys)
- # Hide action (Hides this widget)
- hideAction = menu.addAction(_("Hide"))
- hideAction.triggered.connect(self.hide)
- menu.exec_(self.mapToGlobal(event.pos()))
- def mousePressEvent(self, event):
- """ Toggle the expanded/collapsed state of the label.
- """
- if event.button() == Qt.LeftButton:
- self.__toggleLabelState()
- event.accept()
- else:
- event.ignore()
|