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("")
Exemple #2
0
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))