Ir para conteúdo
klonder

QCombobox em PyQt5 e autocomplete com filtro de resultados

Recommended Posts

Olá a todos(as)!

Estava há 3 dias pesquisando sobre como resolver um problema que estava enfrentando em meu sistema, que consistia no seguinte:

Implantar um Combobox com dados vindos do banco. Esse combobox deveria ser editável e apresentar os valores em seu popup suspenso. À medida em que o usuário fosse digitando, o autocomplete faria a busca em todos os valores já cadastrados e mostraria os compatíveis (procedimento até então comum em qualquer autocomplete). Todavia, o combobox só poderia perder o foco caso o texto inserido fosse exatamente igual a um dos valores existentes no popup, ou seja, não poderia ser  inserido nada que já não estivesse cadastrado anteriormente e presente na lista de valores.

Após muito pesquisar, cheguei a um resultado muito satisfatório que compartilho com vocês, visto que varri a internet inteira à procura desse código e não encontrei em lugar nenhum. PyQt5 é extremamente pobre em tutoriais, principalmente em português.

 

Segue o código que poderá ajudar quem precise:

from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QComboBox, QLineEdit, QCompleter
from PyQt5.QtCore import QSize, Qt, QSortFilterProxyModel, QStringListModel

class Ui_Autocomplete(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 200, 800, 600)
        self.setWindowTitle('Autocomplete com combobox')
  
        self.cboxEstado = QComboBox(self)
        self.cboxEstado.move(50, 50)
        self.cboxEstado.addItem("Acre")
        self.cboxEstado.addItem("Amazonas")
        self.cboxEstado.addItem("Amapá")
        self.cboxEstado.addItem("Pará")
        self.cboxEstado.addItem("Rondônia")
        self.cboxEstado.addItem("Roraima")
        self.cboxEstado.addItem("Tocantins")

        self.cboxEstado.addItem("Distrito Federal")
        self.cboxEstado.addItem("Goiás")
        self.cboxEstado.addItem("Mato Grosso")
        self.cboxEstado.addItem("Mato Grosso do Sul")

        self.cboxEstado.addItem("Alagoas")
        self.cboxEstado.addItem("Bahia")
        self.cboxEstado.addItem("Ceará")
        self.cboxEstado.addItem("Maranhão")
        self.cboxEstado.addItem("Piauí")
        self.cboxEstado.addItem("Pernambuco")
        self.cboxEstado.addItem("Paraíba")
        self.cboxEstado.addItem("Rio Grande do Norte")
        self.cboxEstado.addItem("Sergipe")

        self.cboxEstado.addItem("Paraná")
        self.cboxEstado.addItem("Rio Grande do Sul")
        self.cboxEstado.addItem("Santa Catarina")

        self.cboxEstado.addItem("Espírito Santo")
        self.cboxEstado.addItem("Minas Gerais")
        self.cboxEstado.addItem("São Paulo")
        self.cboxEstado.addItem("Rio de Janeiro")
        
        self.cboxEstado.setEditable(True)
        self.cboxEstado.completer().setCompletionMode(QCompleter.PopupCompletion)
        self.cboxEstado.completer().setFilterMode(Qt.MatchContains)
        self.cboxEstado.completer().setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.cboxEstado.focusOutEvent = self.pedeuFoco

        self.leRegiao = QLineEdit(self)
        self.leRegiao.move(250,50)
        self.leRegiao.resize(200, 22)

    def pedeuFoco(self,event):
        occur = 0
        for i in range(len(self.cboxEstado)):
            if self.cboxEstado.currentText() == self.cboxEstado.itemText(i):
                occur = occur+1
        if occur == 0:
            self.cboxEstado.lineEdit().setFocus()
            self.cboxEstado.lineEdit().selectAll()
        else:
            #Retira o cursor do widget
            QComboBox.focusOutEvent(self.cboxEstado,event)
        
if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    mainWin = Ui_Autocomplete()
    mainWin.show()
    sys.exit(app.exec_())

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Aproveitando o exemplo acima, deixo outra possibilidade para controle de eventos focusIn/focusOut. Com o método focusChanged signal of QApplication, foi necessário fazer algumas adaptações, mas deixei o código comentado para facilitar a referência:

 

from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QComboBox, QLineEdit, QCompleter
from PyQt5.QtCore import QSize, Qt, QSortFilterProxyModel, QStringListModel

class Ui_Autocomplete(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 200, 800, 600)
        self.setWindowTitle('Autocomplete com combobox')
  
        self.cboxEstado = QComboBox(self)
        self.cboxEstado.move(50, 50)
        self.cboxEstado.addItem("Acre")
        self.cboxEstado.addItem("Amazonas")
        self.cboxEstado.addItem("Amapá")
        self.cboxEstado.addItem("Pará")
        self.cboxEstado.addItem("Rondônia")
        self.cboxEstado.addItem("Roraima")
        self.cboxEstado.addItem("Tocantins")

        self.cboxEstado.addItem("Distrito Federal")
        self.cboxEstado.addItem("Goiás")
        self.cboxEstado.addItem("Mato Grosso")
        self.cboxEstado.addItem("Mato Grosso do Sul")

        self.cboxEstado.addItem("Alagoas")
        self.cboxEstado.addItem("Bahia")
        self.cboxEstado.addItem("Ceará")
        self.cboxEstado.addItem("Maranhão")
        self.cboxEstado.addItem("Piauí")
        self.cboxEstado.addItem("Pernambuco")
        self.cboxEstado.addItem("Paraíba")
        self.cboxEstado.addItem("Rio Grande do Norte")
        self.cboxEstado.addItem("Sergipe")

        self.cboxEstado.addItem("Paraná")
        self.cboxEstado.addItem("Rio Grande do Sul")
        self.cboxEstado.addItem("Santa Catarina")

        self.cboxEstado.addItem("Espírito Santo")
        self.cboxEstado.addItem("Minas Gerais")
        self.cboxEstado.addItem("São Paulo")
        self.cboxEstado.addItem("Rio de Janeiro")
        
        self.cboxEstado.setEditable(True)
        self.cboxEstado.completer().setCompletionMode(QCompleter.PopupCompletion)
        self.cboxEstado.completer().setFilterMode(Qt.MatchContains)
        self.cboxEstado.completer().setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        #self.cboxEstado.focusOutEvent = self.pedeuFoco
        QtWidgets.qApp.focusChanged.connect(self.on_focusChanged)
        
        self.leRegiao = QLineEdit(self)
        self.leRegiao.move(250,50)
        self.leRegiao.resize(200, 22)

        
    def on_focusChanged(self, old, now):
        if now == self.leRegiao:
            self.leRegiao.setText(self.cboxEstado.currentText())

        if old == self.cboxEstado:
            occur = 0
            for i in range(len(self.cboxEstado)):
                if self.cboxEstado.currentText() == self.cboxEstado.itemText(i):
                    occur = occur+1
            if occur == 0:
                self.leRegiao.setText("")
                self.cboxEstado.lineEdit().setFocus()
                self.cboxEstado.lineEdit().selectAll()
            #else:
                #Retira o cursor do widget
                #QComboBox.focusOutEvent(self.cboxEstado,event)

            
    #def pedeuFoco(self,event):
        #occur = 0
        #for i in range(len(self.cboxEstado)):
            #if self.cboxEstado.currentText() == self.cboxEstado.itemText(i):
                #occur = occur+1
        #if occur == 0:
            #self.cboxEstado.lineEdit().setFocus()
            #self.cboxEstado.lineEdit().selectAll()
        #else:
            ##Retira o cursor do widget
            #QComboBox.focusOutEvent(self.cboxEstado,event)
        
if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    mainWin = Ui_Autocomplete()
    mainWin.show()
    sys.exit(app.exec_())

 

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora

  • Conteúdo Similar

    • Por clovis.sardinha
      Tenho uma consulta de autocomplete no bd que funciona no servidor local e não roda no servidor da web. 
      Ao enviar a consulta no servidor local  aparece no console :Fetch terminou o carregamento: GET ".../Cidade?cidade=sao%20paulo". A pesquisa é feita normalmente.
      Quando mando a mesma pesquisa para o servidor web(locaweb) aparece no console: Fetch terminou o carregamento: GET "..../Cidade?cidade=sao%2520paul".
      O número 25 aparece só no servidor web. Pelo que pesquisei 25 significa %, ou seja, está duplicando o caractere %. 
      Não consegui utilizar nenhuma função para evitar que isto ocorra. Alguém sabe se há alguma configuração no servidor web que possa ser alterada para evitar essa duplicação?
       
    • Por clovis.sardinha
      Estou em um impasse pois sei pouco de javascript. Estou fazendo um autocomplete com CI4 e Javascript.
      Consigo gerar, através do controllers/model do CI4 um arquivo em json, mas não consigo retorná-lo para o javascript para poder mostrar as opções para consulta. 
      O console.log mostra que estou obtendo o  json() { [native code] }.
      Segue os dois arquivos para ver se alguém me ajuda.
      //arquivo cidade.js async function carregar_cidade(valor) { if (valor.length >= 3) { //console.log("Pesquisar:" + valor); const dados = fetch('Testes/?cidade='+valor, { method: "get", headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest" } }); const resposta = (await dados).json; console.log(resposta); var html = "<ul class='list-group position-fixed'>"; html += "<li class='list-group-item'>" + resposta['cid_nome'] + "</li>"; html += "</ul>"; } } <?php //arquivo Testes.php namespace App\Controllers; use App\Models\CidadeModel; /** NÃO MANDAR PARA O SERVIDOR - APENAS TESTES DE FUNÇÕES E OUTROS ELEMENTOS DO CI4 */ class Testes extends BaseController{ protected $tbCidades; public function __construct(){ $this->tbCidades = new CidadeModel(); } public function index(){ $request = \Config\Services::request(); $client = \Config\Services::curlrequest(); $cidades=[]; if($get=$request->getGet()){ $cities=$get['cidade']; $cidadeFiltrada=$this->tbCidades->getCidByName($cities); $cidades= json_encode($cidadeFiltrada); //dd($cidades); } echo view('Testes/testes'); } public function salvar(){ $request = \Config\Services::request(); if($post=$request->getPost()){ dd($post); } } } <!doctype html> <html lang="pt-br"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> <title>Autocomplete</title> </head> <body> <div class="container"> <h1 class="mt-4 mb-4">Formulário</h1> <form class="row g-3"> <div class="col-12"> <label for="cidade" class="form-label">Cidade</label> <input type="text" name="cidade" class="form-control" id="cidade" placeholder="Pesquisar cidade" onkeyup="carregar_cidade(this.value)"> </div> <span ></span> </form> </div> <!-- Optional JavaScript; choose one of the two! --> <!-- Option 1: Bootstrap Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> <script src="assets/js/cidade.js"></script> </body> </html>
    • Por Marcosvn
      Quero desenvolver um site semelhante a esse: https://www.catalogosofertas.com.br/ 
       
      No qual eles enviam diversas ofertas e encartes recentes de lojas em geral e supermercados. Eles possuem muito conteúdo que são atualizados frequentemente, no entanto, muitos dos supermercados, por exemplo, não possuem os folhetos de cada cidade em seus sites. De modo que pudesse criar um scraper pra ficar acompanhando as atualizações. 
       
      Ao mesmo tempo não imagino que eles tenha muitos funcionários fazendo isso, pois são muitas lojas e supermercados que o encarte vence em alguns dias. Oq me faz pensar que manualmente é inviável manter as atualizações.
       
      Queria saber de vocês, que tem experiência com a área ou mesmo que já tenham trabalhado em projetos semelhantes, como posso me organizar para fazer isso acontecer assim como no site de exemplo: https://www.catalogosofertas.com.br/ 
       
      Como posso fazer isso ? Realmente teria q ficar criando diversos scraper e códigos pra ficar acompanhando os encartes de cada loja e supermercado?
       
      Qual dica, sugestão , orientação poderá me ser muito útil, pois estou confuso sobre como fazer isso acontecer.
    • Por Bruno Soares Slva
      Boa tarde pessoal,
      Estou com problemas para criar um robô. O mesmo esta dando na linha em negrito. Segue abaixo o erro que está aparecendo. Alguma informação. Estou usando o VSCode versão: 1.73.0, Python versão 3.8.0, chromedriver versão 18 (antepenúltima). Segue código e erro abaixo:
       
      Erro mostrado: 
       
      Windows PowerShell
      Copyright (C) Microsoft Corporation. Todos os direitos reservados.        
      Experimente a nova plataforma cruzada PowerShell https://aka.ms/pscore6   
      PS C:\Users\soare\Documents\Estudos\Python\projectCourse>  & 'C:\Users\soare\AppData\Local\Programs\Python\Python38\python.exe' 'c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '52080' '--' 'c:\Users\soare\Documents\Estudos\Python\projectCourse\projeto1\roboweb1.py'
      Traceback (most recent call last):
        File "C:\Users\soare\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 192, in _run_module_as_main
          return _run_code(code, main_globals, None,
        File "C:\Users\soare\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 85, in _run_code
          exec(code, run_globals)
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy\__main__.py", line 39, in <module>
          cli.main()
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 430, in main
          run()
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 284, in run_file
          runpy.run_path(target, run_name="__main__")
      PS C:\Users\soare\Documents\Estudos\Python\projectCourse>  c:; cd 'c:\Users\soare\Documents\Estudos\Python\projectCourse'; & 'C:\Users\soare\AppData\Local\Programs\Python\Python38\python.exe' 'c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '52168' '--' 'c:\Users\soare\Documents\Estudos\Python\projectCourse\projeto1\roboweb1.py' 
      Traceback (most recent call last):
        File "C:\Users\soare\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 192, in _run_module_as_main
          return _run_code(code, main_globals, None,
        File "C:\Users\soare\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 85, in _run_code
          exec(code, run_globals)
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy\__main__.py", line 39, in <module>
          cli.main()
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 430, in main
          run()
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 284, in run_file
          runpy.run_path(target, run_name="__main__")
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 320, in run_path
          code, fname = _get_code_from_file(run_name, path_name)
        File "c:\Users\soare\.vscode\extensions\ms-python.python-2022.16.1\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 294, in _get_code_from_file
          code = compile(f.read(), fname, 'exec')
        File "c:\Users\soare\Documents\Estudos\Python\projectCourse\projeto1\roboweb1.py", line 17
          driver = webdriver.Chrome('C:\Users\soare\Documents\Estudos\Python\chromedriver', options=options)
                                    ^
      SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
      PS C:\Users\soare\Documents\Estudos\Python\projectCourse> 
       
       
      Obsrvação: 
      O erro está caindo nesta linha
      driver = webdriver.Chrome('C:\Users\soare\Documents\Estudos\Python\chromedriver', options=options)
       
       
       
      Codigo:
          from selenium import webdriver from selenium.webdriver.common.keys import Keys import time   options = webdriver.ChromeOptions() options.add_argument('--disable-logging') options.add_argument('--log-level=3')     driver = webdriver.Chrome('C:\Users\soare\Documents\Estudos\Python\chromedriver', options=options) driver.get("https://registro.br/")   pesquisa = driver.find_elements_by_id('is-avail-field') pesquisa.clear() pesquisa.send_keys('roboscompython.com.br') pesquisa.send_keys(Keys.RETURN)   time.sleep(8) driver.close()

         
       
       
       
       
       
       
       
       
       
    • Por Rafael Castelhano
      Boa tarde,
       
      Estou tentando criar uma imagem dinamicamente por javascript e carregá-la no input do formulário, apesar da imagem ser gerada corretamente, ao validar o formulário no python é retornado um erro alegando que o arquivo de imagem é inválido ou está corrompido.
       
      Segue gist com o código:
      https://gist.github.com/castelhano/bd72947e13421606453798bbf219cc74
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.