def _load_labels(self): labels = self.github.get_labels() model = QStringListModel() completer_model = model compl = QCompleter() compl.setModel(model) compl.setCaseSensitivity(Qt.CaseInsensitive) compl.setMaxVisibleItems(50) compl.setModelSorting(QCompleter.CaseInsensitivelySortedModel) compl.setCompletionMode(QCompleter.UnfilteredPopupCompletion) completer_model.setStringList(labels) self.LabelsLineEdit.setCompleter(compl) self.LabelsLineEdit.setText("")
class Search_QLineEdit(QLineEdit): """ Defines a `QLineEdit <http://doc.qt.nokia.com/qlinedit.html>`_ subclass providing a search field with clearing capabilities. """ def __init__(self, parent=None, uiSearchImage=None, uiSearchClickedImage=None, uiClearImage=None, uiClearClickedImage=None): """ Initializes the class. :param parent: Widget parent. :type parent: QObject :param uiSearchImage: Search button image path. :type uiSearchImage: unicode :param uiSearchClickedImage: Search button clicked image path. :type uiSearchClickedImage: unicode :param uiClearImage: Clear button image path. :type uiClearImage: unicode :param uiClearClickedImage: Clear button clicked image path. :type uiClearClickedImage: unicode """ LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__)) QLineEdit.__init__(self, parent) # --- Setting class attributes. --- self.__uiSearchImage = None self.uiSearchImage = uiSearchImage or umbra.ui.common.getResourcePath("images/Search_Glass.png") self.__uiSearchClickedImage = None self.uiSearchClickedImage = uiSearchClickedImage or umbra.ui.common.getResourcePath( "images/Search_Glass_Clicked.png") self.__uiClearImage = None self.uiClearImage = uiClearImage or umbra.ui.common.getResourcePath("images/Search_Clear.png") self.__uiClearClickedImage = None self.uiClearClickedImage = uiClearClickedImage or umbra.ui.common.getResourcePath( "images/Search_Clear_Clicked.png") self.__searchActiveLabel = Active_QLabel(self, QPixmap(self.__uiSearchImage), QPixmap(self.__uiSearchImage), QPixmap(self.__uiSearchClickedImage)) self.__searchActiveLabel.setObjectName("Search_Field_activeLabel") self.__searchActiveLabel.showEvent = lambda event: reduce(lambda *args: None, (self.__setStyleSheet(), Active_QLabel.showEvent(self.__searchActiveLabel, event))) self.__searchActiveLabel.hideEvent = lambda event: reduce(lambda *args: None, (self.__setStyleSheet(), Active_QLabel.hideEvent(self.__searchActiveLabel, event))) self.__clearButton = QToolButton(self) self.__clearButton.setObjectName("Clear_Field_button") self.__completer = QCompleter() self.setCompleter(self.__completer) self.__completerVisibleItemsCount = 16 Search_QLineEdit.__initializeUi(self) self.__setClearButtonVisibility(self.text()) # Signals / Slots. self.__clearButton.clicked.connect(self.clear) self.textChanged.connect(self.__setClearButtonVisibility) #****************************************************************************************************************** #*** Attributes properties. #****************************************************************************************************************** @property def uiSearchImage(self): """ Property for **self.__uiSearchImage** attribute. :return: self.__uiSearchImage. :rtype: unicode """ return self.__uiSearchImage @uiSearchImage.setter @foundations.exceptions.handleExceptions(AssertionError) def uiSearchImage(self, value): """ Setter for **self.__uiSearchImage** attribute. :param value: Attribute value. :type value: unicode """ if value is not None: assert type(value) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format( "uiSearchImage", value) assert os.path.exists(value), "'{0}' attribute: '{1}' file doesn't exists!".format("uiSearchImage", value) self.__uiSearchImage = value @uiSearchImage.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def uiSearchImage(self): """ Deleter for **self.__uiSearchImage** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiSearchImage")) @property def uiSearchClickedImage(self): """ Property for **self.__uiSearchClickedImage** attribute. :return: self.__uiSearchClickedImage. :rtype: unicode """ return self.__uiSearchClickedImage @uiSearchClickedImage.setter @foundations.exceptions.handleExceptions(AssertionError) def uiSearchClickedImage(self, value): """ Setter for **self.__uiSearchClickedImage** attribute. :param value: Attribute value. :type value: unicode """ if value is not None: assert type(value) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format( "uiSearchClickedImage", value) assert os.path.exists(value), "'{0}' attribute: '{1}' file doesn't exists!".format( "uiSearchClickedImage", value) self.__uiSearchClickedImage = value @uiSearchClickedImage.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def uiSearchClickedImage(self): """ Deleter for **self.__uiSearchClickedImage** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiSearchClickedImage")) @property def uiClearImage(self): """ Property for **self.__uiClearImage** attribute. :return: self.__uiClearImage. :rtype: unicode """ return self.__uiClearImage @uiClearImage.setter @foundations.exceptions.handleExceptions(AssertionError) def uiClearImage(self, value): """ Setter for **self.__uiClearImage** attribute. :param value: Attribute value. :type value: unicode """ if value is not None: assert type(value) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format( "uiClearImage", value) assert os.path.exists(value), "'{0}' attribute: '{1}' file doesn't exists!".format( "uiClearImage", value) self.__uiClearImage = value @uiClearImage.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def uiClearImage(self): """ Deleter for **self.__uiClearImage** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiClearImage")) @property def uiClearClickedImage(self): """ Property for **self.__uiClearClickedImage** attribute. :return: self.__uiClearClickedImage. :rtype: unicode """ return self.__uiClearClickedImage @uiClearClickedImage.setter @foundations.exceptions.handleExceptions(AssertionError) def uiClearClickedImage(self, value): """ Setter for **self.__uiClearClickedImage** attribute. :param value: Attribute value. :type value: unicode """ if value is not None: assert type(value) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format( "uiClearClickedImage", value) assert os.path.exists(value), "'{0}' attribute: '{1}' file doesn't exists!".format( "uiClearClickedImage", value) self.__uiClearClickedImage = value @uiClearClickedImage.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def uiClearClickedImage(self): """ Deleter for **self.__uiClearClickedImage** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiClearClickedImage")) @property def searchActiveLabel(self): """ Property for **self.__searchActiveLabel** attribute. :return: self.__searchActiveLabel. :rtype: QPushButton """ return self.__searchActiveLabel @searchActiveLabel.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def searchActiveLabel(self, value): """ Setter for **self.__searchActiveLabel** attribute. :param value: Attribute value. :type value: QPushButton """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "searchActiveLabel")) @searchActiveLabel.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def searchActiveLabel(self): """ Deleter for **self.__searchActiveLabel** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "searchActiveLabel")) @property def clearButton(self): """ Property for **self.__clearButton** attribute. :return: self.__clearButton. :rtype: QPushButton """ return self.__clearButton @clearButton.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def clearButton(self, value): """ Setter for **self.__clearButton** attribute. :param value: Attribute value. :type value: QPushButton """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "clearButton")) @clearButton.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def clearButton(self): """ Deleter for **self.__clearButton** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "clearButton")) @property def completer(self): """ Property for **self.__completer** attribute. :return: self.__completer. :rtype: QCompleter """ return self.__completer @completer.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def completer(self, value): """ Setter for **self.__completer** attribute. :param value: Attribute value. :type value: QCompleter """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "completer")) @completer.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def completer(self): """ Deleter for **self.__completer** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "completer")) @property def completerVisibleItemsCount(self): """ Property for **self.__completerVisibleItemsCount** attribute. :return: self.__completerVisibleItemsCount. :rtype: int """ return self.__completerVisibleItemsCount @completerVisibleItemsCount.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def completerVisibleItemsCount(self, value): """ Setter for **self.__completerVisibleItemsCount** attribute. :param value: Attribute value. :type value: int """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "completerVisibleItemsCount")) @completerVisibleItemsCount.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def completerVisibleItemsCount(self): """ Deleter for **self.__completerVisibleItemsCount** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "completerVisibleItemsCount")) #****************************************************************************************************************** #*** Class methods. #****************************************************************************************************************** def resizeEvent(self, event): """ Reimplements the :meth:`QLineEdit.QResizeEvent` method. :param event: Resize event. :type event: QResizeEvent """ frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) searchActiveLabelSize = self.__searchActiveLabel.sizeHint() self.__searchActiveLabel.move(self.rect().left() + frameWidth, (self.rect().bottom() - searchActiveLabelSize.height()) / 2 + frameWidth / 2) clearButtonSize = self.__clearButton.sizeHint() self.__clearButton.move(self.rect().right() - frameWidth - clearButtonSize.width(), (self.rect().bottom() - clearButtonSize.height()) / 2 + frameWidth / 2) def __initializeUi(self): """ Initializes the Widget ui. """ self.__clearButton.setCursor(Qt.ArrowCursor) if self.__uiClearImage and self.__uiClearClickedImage: pixmap = QPixmap(self.__uiClearImage) clickedPixmap = QPixmap(self.__uiClearClickedImage) self.__clearButton.setIcon(QIcon(pixmap)) self.__clearButton.setMaximumSize(pixmap.size()) # Signals / Slots. self.__clearButton.pressed.connect(functools.partial(self.__clearButton.setIcon, QIcon(clickedPixmap))) self.__clearButton.released.connect(functools.partial(self.__clearButton.setIcon, QIcon(pixmap))) else: self.__clearButton.setText("Clear") self.__setStyleSheet() frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) self.setMinimumSize(max(self.minimumSizeHint().width(), self.__clearButton.sizeHint().height() + frameWidth * 2), max(self.minimumSizeHint().height(), self.__clearButton.sizeHint().height() + frameWidth * 2)) self.__completer.setCaseSensitivity(Qt.CaseInsensitive) self.__completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.__completer.setMaxVisibleItems(self.__completerVisibleItemsCount) def __setStyleSheet(self): """ Sets the Widget stylesheet. """ frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) self.setStyleSheet(QString("QLineEdit {{ padding-left: {0}px; padding-right: {1}px; }}".format( self.__searchActiveLabel.sizeHint().width() if self.__searchActiveLabel.isVisible() else 0 + frameWidth, self.__clearButton.sizeHint().width() + frameWidth))) def __setClearButtonVisibility(self, text): """ Sets the clear button visibility. :param text: Current field text. :type text: QString """ if text: self.__clearButton.show() else: self.__clearButton.hide()
class LineEdit_Autocomplete(QLineEdit): AutoComplete = pyqtSignal(QString) def __init__(self, *args, **kwargs): QLineEdit.__init__(self, *args, **kwargs) self.completions = [] # All available completions self.googlesuggestions = [] # This will be filled automatically if GOOGLESEARCH is True self.partials = [] # Parsed completions according to partially typed word self.staticsuggestions = [] # Static suggestions list self.cursorpos = 0 self.wordstart = -1 # Start of word where cursor positioned self.wordend = -1 # End of word where cursor is positioned self.lastindex = -1 # Last used completion of available completions ######################## Completer for first part (without trigger) self.completer = QCompleter() self.completer.setParent(self) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.popup().setMinimumHeight(40) # Keep a minumum heigt if the completer is shown self.setCompleter(self.completer) self.model = QStringListModel() self.model.setParent(self.completer) self.completer.setModel(self.model) self.completer.setMaxVisibleItems(6) self.__set_data(self.model) self.setCompletionDialogFoam() # Set to standard (PopupCompletion) self.setKeyWordForCompletion() # default keyword = "whitespace" - " " self.Timer = QTimer() # Setup singleshot Timer for GoogleSuggestions self.Timer.setSingleShot(True) self.setGOOGLESEARCH_attr = False # initial Settings self.languageForGOOGLESEARCH = "en" self.textEdited.connect(self.onTextEdit) # define connections self.Timer.timeout.connect(self.googlesearch) self.toolButton = None def set_data(self): """ :Triggers the internal __set_data Methode """ self.__set_data(self.model) def __set_data(self, model): """ :param model: QStringlistmodel which is used :Is adding all suggestions which are available at the time of call to the model which manage the input for the used completer (Popup, Inline or Unfiltered Popup Completion """ #print("Setup Completer with available completions") stringlist = [] for item in self.staticsuggestions: stringlist.append(item) for item in self.googlesuggestions: stringlist.append(item) model.setStringList(stringlist) def setCompletionDialogFoam(self, QCompleter_Mode=QCompleter.PopupCompletion): ''' Possible Options: QCompleter.UnfilteredPopupCompletion QCompleter.PopupCompletion QCompleter.InlineCompletion ''' self.completer.setCompletionMode(QCompleter_Mode) def setGOOGLESEARCH(self, bool): ''' With this attribute the "google-suggest" function can be activated, and deactivated.... ''' self.setGOOGLESEARCH_attr = bool def setLanguageForGOOGLESEARCH(self, language="en"): self.languageForGOOGLESEARCH = language @pyqtSlot(QString) def onTextEdit(self, Text=None): ''' If Googelsearch is set to "True" this will trigger the suggestions after 500ms, when the user do not enter anything. ''' if self.setGOOGLESEARCH_attr: self.Timer.start(500) # Timer is connected to "googlesearch" @pyqtSlot() def googlesearch(self): ''' Is collecting the current text entered and is requesting a suggestion-list This list is replacing the current self.googlesuggest ''' #print("Googlesearch") query = self.text() text = unicode(query) #convert QString to str. text = text.encode('utf-8') if text.find(self.keyWord) != -1: self.googlesuggestions = [] self.__set_data(self.model) return result = self.get_querySuggestionList(text, self.languageForGOOGLESEARCH) self.googlesuggestions = [] for suggestion in result: self.googlesuggestions.append(unicode(suggestion)) #print(self.googlesuggestions) self.__set_data(self.model) def addCompletion(self, word): ''' Adds a Word to the custom completions-list ''' if not word in self.completions: self.completions.append(word) def delCompletion(self, word): ''' Removes a Word from the custom completions-list ''' if word in self.completions: self.completions.remove(word) def setCompletions(self, items=[]): ''' items is a List of Strings ''' self.completions = items def setKeyWordForCompletion(self, Regex_String=".*", Wordseparator=" "): ''' Regex_String is something like this: ".*@.+" Per default, the Completion-List will be applied for evey Word entered ''' self.keyWordREGEX = Regex_String #".*@.+" self.keyWord = Wordseparator # " @" ... is watching for a "@" with a whitespace in front of it def getWords(self): #print('Func "getWords"') text = unicode(self.text())#.encode('utf-8') #print(text) if self.lastindex == -1: self.cursorpos = self.cursorPosition() self.wordstart = text.rfind(self.keyWord, 0, self.cursorpos) + 1 # all, which is in front of " @" is ONE word. and is ignored. #print(self.wordstart) #print(self.cursorpos) pattern = text[self.wordstart:self.cursorpos] #xyz = text.split(" ") #pattern = xyz[len(xyz) -1] #pattern = "".join(pattern) #print(pattern) self.partials = [item for item in self.completions if item.lower().startswith(pattern.lower())] #print(self.partials) return self.partials def applyCompletion(self, text): #oldlen = len(str(self.text())) #print('Laenge ohne Umwandlung "old":',oldlen) old = unicode(self.text())#.encode('utf-8') #print('Laenge nach Umwandlung "%s" ist %d Zeichen lang:' %(old, len(old))) self.wordend = old.find(" ", self.cursorpos) if self.wordend == -1: self.wordend = len(old) new = unicode(old[:self.wordstart]) + text + unicode(old[self.wordend:]) #beide decodieren ? self.setText(new) self.setSelection(len(unicode(old)), len(new)) #decode ? def get_querySuggestionList(self, query, language='de'): ''' Takes a "query" and a "language"-definition (de = deutsch, en = englisch) and returns a list with search-suggestions by Google.com Example: myreturn = get_querySuggestionList("die", "de") print(myreturn) [u'die bahn', u'die zeit', u'die welt', u'die bestimmunng] please note, that the result is in unicode ! ''' querynew = query.replace(" ", "%20") # replacing all whitespaces wit "%20" if query == "": #print("EMPTY") return [] link = ('http://suggestqueries.google.com/complete/search?hl=%s&client=chrome&q=%s&hjson=t&cp3' %(language, querynew)) #print("Asking Google for suggestions...") try: j = json.loads(unicode(urllib2.urlopen(link, timeout=400).read(), "ISO-8859-1")) if len(j) > 0: results = j[1] # result for query "die" = [u'die bahn', u'die zeit', u'die welt', u'die bestimmun...] else: results = [] #for suggestion in results: #print(suggestion.encode('utf-8')) # for german umlauts #print("Suggestions received") except: #print("There was a Problem during response ... received an empty list :-(") results = [] return results # result for query "die" = [u'die bahn', u'die zeit', u'die welt', u'die bestimmun...] def event(self, event): if event.type() == QEvent.KeyPress: if event.key() in (Qt.Key_Shift, Qt.Key_Control, Qt.Key_AltGr, Qt.Key_Alt): return True regex = QRegExp(self.keyWordREGEX, Qt.CaseInsensitive, QRegExp.RegExp) if regex.exactMatch(self.text()) and not (event.key() == Qt.Key_Backspace): # 16777219 is the backspace #if event.key() != Qt.Key_Backspace and event.key() != Qt.Key_Space: QLineEdit.event(self, event) self.getWords() if not len(self.completions): return True if self.text() == "": return True if len(self.partials) >= 1: autocomplete = self.partials[0] self.applyCompletion(autocomplete) signalAttach = QString(autocomplete) self.AutoComplete.emit(signalAttach) #if autocomplete got a "@" in it, only the part, starting with "@..." will be sent return True else: signalAttach = QString('NONE') self.AutoComplete.emit(signalAttach) return True else: return QLineEdit.event(self, event) else: return QLineEdit.event(self, event) ####################### New in V2 ############### Add a clickable Butten into the LineEdit.... def setSpecialToolButton(self, iconpath=None, Alignment=Qt.AlignRight, tooltip=None): self.toolButton = QToolButton(self) self.toolButton.setCursor(Qt.PointingHandCursor) self.toolButton.setFocusPolicy(Qt.NoFocus) if iconpath is not None: self.toolButton.setIcon(QIcon(iconpath)) self.toolButton.setStyleSheet("background: transparent; border: none;") layout = QHBoxLayout(self) layout.addWidget(self.toolButton,0,Alignment) layout.setSpacing(0) layout.setMargin(5) # ToolTip if tooltip is not None: self.toolButton.setToolTip(tooltip) def setSpecialToolButton_newIcon(self, iconpath): if self.toolButton is not None: self.toolButton.setIcon(QIcon(iconpath))