[TUTORIAL] Python e Qt 5 [Parte 2]

Desta parte 2 vamos ver como criar interfaces utilizando o Qt Designe e o Qt Creator. Em seguida vamos ver como ler/manipular o arquivo *.ui gerado por essas ferramentas.

A ideia é criar uma interface com o mesmo designe e funcionalidades que fizemos no GTK+ 3, basicamente vamos seguir o seguinte modelo:

builder-app-running

Como foi dito na parte 1 é comum vermos tutoriais onde o designe da interface é feito diretamente no código Python, um exemplo desta forma de criar a interface seria:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Python e PyQt5.

Criando a interface no Python.
"""
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLineEdit, QLabel,
                             QPushButton, QApplication)


class MeuAplicativo(QWidget):
    """Classe"""

    def __init__(self):
        """Construtor."""
        super().__init__()
        # Janela principal.
        self.setWindowTitle('Olá Mundo')
        self.setObjectName('Widget')
        self.resize(400, 300)

        # Gerenciador de layout.
        verticalLayout = QVBoxLayout()
        verticalLayout.setObjectName('verticalLayout')
        self.setLayout(verticalLayout)

        # Widgets.
        self.lineEdit = QLineEdit()
        self.lineEdit.setObjectName('lineEdit')
        self.lineEdit.setPlaceholderText('Digite algo')
        verticalLayout.addWidget(self.lineEdit)

        self.label = QLabel()
        self.label.setObjectName('label')
        self.label.setText('Este texto será alterado!')
        self.label.setAlignment(Qt.AlignCenter)
        verticalLayout.addWidget(self.label)

        self.pushButton = QPushButton()
        self.pushButton.setObjectName('pushButton')
        self.pushButton.setText('Clique Aqui')
        self.pushButton.clicked.connect(self._on_button_clicked)
        verticalLayout.addWidget(self.pushButton)

    def _on_button_clicked(self):
        """Método é executado quando o botão é pressionado."""
        # Coletando o valor do campo de entrada de texto.
        text = self.lineEdit.text()
        # Verificando se algo foi digitado.
        if text:
            self.label.setText(text)
        else:
            self.label.setText('Digite algo no campo de texto :)')


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    ui = MeuAplicativo()
    ui.show()
    sys.exit(app.exec_())

python-designe-code

Essa é uma forma totalmente válida de se desenvolver um aplicativo, todavia temos como limitação o fato de não poder separar a lógica da parte visual, além de limitar a possibilidade de se trabalhar com uma equipe para o designe e uma para a lógica.

Agora vamos ver uma outra possibilidade para o código a cima. Desta vez separando o designer da parte lógica.

Qt Designer

Se você optar por utilizar o Qt Designer, basta executar ele que o mesmo irá solicitar o tipo de janela que se deseja criar:

Eu escolhi o tipo Widget por que não será um interface que utiliza menus e area de status.

Assim como no Gnome Glade, tente desenhar a interface até chegar em um resultado próximo a:

qt-designer-widget-preview

Para visualizar o resultado do que está sendo criado basta utilizar o atalho Ctrl + r.

Importante notar que os widgets criados tem um campo chamado objectName este campo deve ser único, visto que iremos acessar os widgets por este nome:

Caso não queira desenhar a interface crie um arquivo chamado mainwindow-designer.ui e cole o seguinte código XML:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Olá Mundo</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLineEdit" name="lineEdit">
     <property name="inputMask">
      <string/>
     </property>
     <property name="text">
      <string/>
     </property>
     <property name="placeholderText">
      <string>Digite algo</string>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QLabel" name="label">
     <property name="text">
      <string>Este texto será alterado!</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QPushButton" name="pushButton">
     <property name="text">
      <string>Clique Aqui</string>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

Qt Creator

Já com o Qt Creator vamos precisar criar um novo projeto, para isso vamos no menu superior e clicamos em file &gt; New File or Project, na janela que se abre vamos escolher Application e Qt Widgets Applicativon:

Na tela seguinte podemos definir o nome do projeto e o local dele. clique em Next:

Agora temos a tela onde definimos o kit. Caso seja necessário crie um. clique em Next:

Na tela que segue podemos definir informações sobre a class (classe), contudo isso não é importante, visto que não vamos utilizar C/C++, apenas clique em Next:

Por fim temos a ultima tela. clique em Finish:

Com o projeto criado clique sobre a pasta forms e clique no arquivo mainwindow.ui, com isso o modo de designe será aberto:

Agora basta criar a interface, como fizemos com o Qt Designer:

Caso você queira visualizar o resultado enquanto cria a interface basta utilizar o atalho Ctrl + r.

> OBS: Vale notar que o Qt Creator gera uma janela do tipo MainWindow, contudo isso não é um problema, uma vez que nossos widgets terão o mesmo objectName.

Caso não queira tentar desenhar a interface crie um arquivo chamado mainwindow-creator.ui e cole o seguinte código XML:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Olá Mundo</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QLineEdit" name="lineEdit">
      <property name="placeholderText">
       <string>Digite algo</string>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QLabel" name="label">
      <property name="text">
       <string>Este texto será alterado!</string>
      </property>
      <property name="alignment">
       <set>Qt::AlignCenter</set>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>Clique Aqui</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

Interfaces criadas, agora podemos avançar para o código em Python.

Python

> OBS: Eu realmente recomendo que você crie um ambiente virtual e instale as depêndencias neste ambiente, contudo não é obrigatorio é apenas uma recomendação.

Vamos começar criando a pasta do projeto, eu criei um pasta com o nome ola-mundo, sinta-se avontade para criar uma pasta com o nome que preferir.

Essa pasta terá a seguinte estrutura:

ola-mundo
├── forms
│   ├── mainwindow-creator.ui
│   └── mainwindow-designer.ui
├── icons
│   ├── icon.ico
│   └── icon.png
├── MainWindow.py
└── Pipfile

Estrutura do projeto

> OBS: O arquivo Pipfile é porque eu estou utilizando o pipenv para gestão do meu ambiente virtual. Se você não utilizar o pipenv este arquivo não irá existir e não é necessário.

> OBS: Tenho 2 aquivos de interface dentro da pasta forms porque criei uma interface com o Qt Creator e outra com o Qt Designe.

Agora que temos um estrutura básica do projeto vamos instalar o PyQt5, se você estiver utilizando um ambiente virtual (lembre-se de ativar o ambiente!):

pip install pyqt5

Se estiver instalando diretamente no Python do S.O.:

sudo pip3 install pyqt5

> OBS: Caso você tenha mais de uma versão do Python 3 no sistema lembre-se de verificar em qual a instalação está sendo feita ou utilize: python3.x -m pip install pyqt5. Substitua o x pela versão de Python que deseja.

Com o PyQt5 instalado vamos abrir/criar o arquivo MainWindow.py e digitar o seguinte código:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Python e PyQt5.

Acessando/interagindo com um arquivo ``*.ui`` (XML).
"""
from PyQt5.QtWidgets import QApplication
from PyQt5.uic import loadUi


class MeuAplicativo:
    """Classe."""

    def __init__(self, window):
        """Construtor."""
        # Widgets
        self.label = window.label
        self.line_edit = window.lineEdit
        self.push_button = window.pushButton
        # Conectando um método ao evento de clique do botão.
        self.push_button.clicked.connect(self._on_button_clicked)

    def _on_button_clicked(self):
        """Método é executado quando o botão é pressionado."""
        # Coletando o valor do campo de entrada de texto.
        text = self.line_edit.text()
        # Verificando se algo foi digitado.
        if text:
            self.label.setText(text)
        else:
            self.label.setText('Digite algo no campo de texto :)')


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    # Lendo o arquivo de interface.
    # window = loadUi('forms/mainwindow-creator.ui')
    window = loadUi('forms/mainwindow-designer.ui')
    ui = MeuAplicativo(window=window)
    window.show()
    sys.exit(app.exec_())

Ao executar o código a cima temos:

python-designe-code

Nossa interface criada no Qt Designer ou Qt Creator integrada no Python.

O que vejo como principal beneficio deste método é que temos uma boa separação da logica de programação do desginer da interface, favorecendo inclusive o trabalho onde podemos ter um time cuidado da interface e outro da lógica.

Bom com isso temos um pequena introdução a criação de interfaces gráficas com Python e Qt 5. Talvez haja uma parte 3 com alguns código de exemplo para o PySide2 ou algo assim :smiley: .

E claro se encontrar algum erro ou dificuldade entre em contato para que o material possa ser atualizado.

10 curtidas

Esse tipo de postagem é bom demais…
é isso que precisamos aqui

2 curtidas

Como faz com gtk? Tô aprendendo python aos pouquinhos

1 curtida

Muito interessante e completo o Tutorial!! Parabéns @natorsc !!

@filipemosca tem um tutorial completo sobre PyGTK em português aqui:
https://python-gtk-3-tutorial.readthedocs.io/pt_BR/latest/

Eu estou aprendendo Vala-GTK e de vez em quando vou na documentação do python pra esclarecer as coisas, já que documentação de Vala é escarsa.

2 curtidas

Bom dia. Como faço para criar um app com vários forms?
Devo criar um arquivo “.ui” para cada form, gerar o “.py” correspondente e importar no python?
Ex:

pyuic5 form1.ui -o form2.py
pyuic5 form2.ui -o form2.py

from PyQt5.QtWidgets import QMainWindow, QApplication
from form1 import form1
from form2 import form2

class MyApp (QMainWindow, form1, form2)

1 curtida

Olá @Roberto_d_Avila

Você pode trabalhar criando os arquivos de interface e convertendo eles.

Uma vantagem dessa técnica é ter um bom linter (verificação de erros) quando se está editando o código Python.

A desvantagem é que sempre que você tiver uma alteração na interface terá que converter novamente a mesma e tem que tomar o cuidado de realizar as alteração sempre no arquivo de interface, visto que se a alteração for feita no código Python o arquivo de interface estará desatualizado.

A sua lógica está corretíssima cada arquivo de interface será uma tela e cada tela representa uma classe (objeto). depois basta importar e instanciar cada classe em uma variável.

Fiz um exemplo, contudo deixei todas as classes no mesmo arquivo para facilitar a execução de quem quiser testar:

# -*- coding: utf-8 -*-
"""Abrindo múltiplas janelas."""

from PySide2.QtWidgets import (QApplication, QLabel, QMainWindow,
                               QPushButton, QVBoxLayout, QWidget)


class Form1(QWidget):
    """Janela 1.

    Essa classe poderia estar outro arquivo e ser importado.
    """

    def __init__(self):
        super().__init__()
        self.setWindowTitle('Este é o form 1')

        layout = QVBoxLayout()
        self.setLayout(layout)

        self.label = QLabel('Este é o form 1')
        layout.addWidget(self.label)


class Form2(QWidget):
    """Janela 2.

    Essa classe poderia estar outro arquivo e ser importado.
    """

    def __init__(self):
        super().__init__()
        self.setWindowTitle('Este é o form 2')

        layout = QVBoxLayout()
        self.setLayout(layout)

        self.label = QLabel('Este é o form 2')
        layout.addWidget(self.label)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        screen_size = app.primaryScreen().geometry()
        width = screen_size.width()
        height = screen_size.height()
        self.resize(int(width / 2), int(height / 2))
        self.setMinimumSize(int(width / 3), int(height / 3))
        self.setWindowTitle('Janela Principal')

        # Instância da classe Form1().
        self.form1 = Form1()
        # Instância da classe Form2().
        self.form2 = Form2()

        vbox = QVBoxLayout()
        button1 = QPushButton("Abrir Form1()")
        button1.clicked.connect(self.open_form1)
        vbox.addWidget(button1)

        button2 = QPushButton("Abrir Form2()")
        button2.clicked.connect(self.open_form2)
        vbox.addWidget(button2)

        w = QWidget()
        w.setLayout(vbox)
        self.setCentralWidget(w)

    def open_form1(self, checked):
        if self.form1.isVisible():
            self.form1.hide()
        else:
            self.form1.show()

    def open_form2(self, checked):
        if self.form2.isVisible():
            self.form2.hide()
        else:
            self.form2.show()


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    mainwindow = MainWindow()
    mainwindow.show()
    sys.exit(app.exec_())

Outra possibilidade é fazer a leitura dos arquivos de interface (XML ou QML), dessa forma você tem como vantagens:

  • A separação do design da interface da lógica de negocio (lógica do aplicativo).
  • Você pode ter uma equipe trabalhando no design do aplicativo e outra na parte de lógica do negocio.

A principal desvantagem está no fato do Qt permitir que essa leitura e atribuição de widgets seja feita de muitas formas, tanto que você verá tutoriais fazendo isso de formas diferentes.

Segue um exemplo também:

Form1:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Este é o form 1</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
     <property name="text">
      <string>Este é o form 1</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

Form2:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Este é o form 2</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
     <property name="text">
      <string>Este é o form 2</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

Janela principal:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Janela Principal</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QLabel" name="label">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="layoutDirection">
       <enum>Qt::LeftToRight</enum>
      </property>
      <property name="text">
       <string>Exemplo</string>
      </property>
      <property name="alignment">
       <set>Qt::AlignCenter</set>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="btn_open_form1">
      <property name="text">
       <string>Abrir form 1</string>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="btn_open_form2">
      <property name="text">
       <string>Abrir Form 2</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>29</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Código Python que faz a leitura dos arquivos de interface:

# -*- coding: utf-8 -*-
"""."""
from PySide2.QtCore import QObject, QCoreApplication, Qt
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QWidget, QMainWindow


class Form1(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Este é o form 1')
        QUiLoader().load('./Form1.ui', self)
        self.show()


class Form2(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Este é o form 2')
        self.window = QUiLoader().load('./Form2.ui', self)
        self.show()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.window = QUiLoader().load('./MainWindow.ui')

        # Instância da classe Form1().
        self.form1 = None
        # Instância da classe Form2().
        self.form2 = None

        btn_open_form1 = self.window.findChild(QObject, 'btn_open_form1')
        btn_open_form1.clicked.connect(self.open_form1)

        btn_open_form2 = self.window.findChild(QObject, 'btn_open_form2')
        btn_open_form2.clicked.connect(self.open_form2)

        self.window.show()

    def open_form1(self, checked):
        if self.form1 is None:
            self.form1 = Form1()
        self.form1.show()

    def open_form2(self, checked):
        if self.form2 is None:
            self.form2 = Form2()
        self.form1.show()


if __name__ == "__main__":
    import sys

    QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
    app = QApplication(sys.argv)
    mainwindow = MainWindow()
    sys.exit(app.exec_())

Tentei criar um gif do código acima em execução, contudo não ficou legal, segue link para um video privado:

https://youtu.be/oNT1kQAiAOk

O meu conteudo de Qt e alguns outros não está muito atualizado no GithHub, segue link de alguns exemplos de código com Qt que estão no meu site pessoal:

https://codigoninja.dev/listings/qt/

Quaquer duvida sinta-se a vontade para entrar em contato aqui pelo forum ou redes sociais.

1 curtida

Agradeço imensamente sua resposta. Ajudou muito!!!

Obrigado.

1 curtida

Gostaria de saber como eu posso colocar uma imagem com opacidade no fundo de uma janela e deixar os botões, campos de texto e combo boxes utilizáveis? Eu consegui colocar a imagem transparente, mas ela está sobre os outros widgets o que dificulta o uso deles.
O código está assim:


import sys
from PIL import ImageGrab
from Codes.Cad_Trajes import *
from Codes.Cad_Usr import *
from Codes.Rent import *
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication, QTabWidget, QVBoxLayout, QLabel, QGraphicsOpacityEffect

class MainWindow(QWidget):
    def __init__(self):
	super(MainWindow, self).__init__( )
	img = ImageGrab.grab( ).size
	wid = img[0]
	hg = img[1]
	left = wid // 4
	top = int(hg * 0.1)
	width = wid // 2
	height = int(hg * 0.8)

	picLabel = QLabel(self)
	picLayout = QVBoxLayout( )
	pic = QPixmap("Pics/E_Trajes.svg")
	picLabel.setPixmap(pic.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation))
	picLabel.setHidden(False)
	effect = QGraphicsOpacityEffect( )
	effect.setOpacity(.09)
	picLabel.setGraphicsEffect(effect)
	picLayout.addWidget(picLabel, alignment=Qt.AlignCenter)

	ct = CadTrajes( )
	cadusr = CadUsr( )
	rent = Renting( )
	box = QVBoxLayout( )
	tab = QTabWidget( )
	tab.currentWidget( )
	tab.addTab(cadusr, " CLIENTES (CADASTRO E EDIÇÃO) ")
	tab.addTab(rent, " ALUGUEL DE TRAJES ")
	tab.addTab(ct, " TRAJES (CADASTRO E EDIÇÃO) ")
	tab.setLayout(picLayout)
	box.addWidget(tab)
	self.setWindowTitle("ELEGANT TRAJES")
	self.setGeometry(left, top, width, height)
	self.setWindowIcon(QIcon("Pics/E_Trajes_01.svg"))
	self.setStyleSheet(open('CSS/etrajes.css').read( ))
	self.setLayout(box)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mw = MainWindow( )
    mw.show( )
    app.exec_( )

Ele está funcionando, abre normal, mas como a imagem está por cima dos widgets, eu não consigo usar nada da interface usando o mouse. Como eu faria pra colocar essa imagem como plano de fundo da aba, mas sem interferir nos outros widgets?

1 curtida

Ola @ACMM .

Faz um teste, tenta definir a imagem utilizado o método setStyleSheet().

Ex:

nome-do-widget.setStyleSheet("background-image: url(image.png)")

Se funcionar me avisa, ainda não testei essa possibilidade.

1 curtida

@natorsc
Opa! Tudo certo? Serviria, sim, se eu redimensionasse a imagem manualmente e deixasse um tamanho fixo direto no arquivo da imagem pois ela tem um tamanho de 128x128px e ela é também usada como a logo que fica na parte superior da tela, mas como eu quero que a imagem se redimensione baseada no tamanho do widget , fica complicado fazer isso. Bom, pelo menos, eu ainda não descobri como fazer isso. A imagem esta pegando as dimensões da tela como mostra no seguinte trecho do código:


picLabel.setPixmap(pic.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation))

Bom, eu já havia tentado essa dica e, infelizmente, não funcionou como deveria. Colocou a imagem na tela, mas não ocupou o espaço todo se limitando aos 128x128px do tamanho da imagem.
Deu pra entender?

1 curtida

@ACMM

Nesse caso eu acredito que a melhor forma seja utilizando border-image.

Só que ao invés de utilizar setStyleSheet() pode ser interessante trabalhar diretamente no css da janela principal.

Assim vamos chegar neste resultado:

pyqt6-background-image

Eu peguei um computador emprestado aqui :joy: e acabei fazendo um exemplo com PyQt6 porque já tinha essa base de código pronta no meu Github:

# -*- coding: utf-8 -*-
"""Python e Qt 6: PyQt6 QMainWindow() adicionando imagem no background."""

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (QApplication, QLabel, QLineEdit, QMainWindow,
                             QPushButton, QVBoxLayout, QWidget)


class MainWindow(QMainWindow):

    def __init__(self, application):
        super().__init__()
        self.application = application

        vbox = QVBoxLayout()
        central_widget = QWidget()
        central_widget.setLayout(vbox)
        self.setCentralWidget(central_widget)

        line_edit = QLineEdit()
        vbox.addWidget(line_edit)

        label = QLabel()
        label.setText('Um texto qualquer.')
        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        vbox.addWidget(label)

        push_button = QPushButton()
        push_button.setText('Clique Aqui!')
        vbox.addWidget(push_button)

        self.show()


if __name__ == "__main__":
    import sys

    # Imagem fica fixa e centralizada.
    stylesheet_01 = """
    MainWindow {
        background-image: url('terno.png'); 
        background-repeat: no-repeat; 
        background-position: center;
    }
    """

    # Imagem será redimensionada para o tamanho da janela.
    stylesheet_02 = """
    MainWindow {
        border-image: url('terno.png'); 
    }
    """

    application = QApplication(sys.argv)
    application.setStyleSheet(stylesheet_02)

    window = MainWindow(application=application)

    sys.exit(application.exec())

Faça mais alguns testes para ver se essa ideia atende as suas necessidades.

Se não funcionar vai avisando que vamos tentando pensar em novas possibilidades :blush:

1 curtida

Eu fiz uma adaptação pra minha aplicação e ficou assim:

class MainWindow(QMainWindow):
	def __init__(self, app):
		super( ).__init__( )
		self.app = app
        self.app.setObjectName("main")
		ct = CadTrajes( )
		cadusr = CadUsr( )
		rent = Renting( )
		img = ImageGrab.grab().size
		wid = img[0]
		hg = img[1]
		left = wid // 4
		top = int(hg * 0.1)
		width = wid // 2
		height = int(hg * 0.8)

		vbox = QVBoxLayout( )
		main = QWidget( )
		main.setLayout(vbox)
		self.setCentralWidget(main)

		tab = QTabWidget( )
		tab.addTab(cadusr, " CLIENTES (CADASTRO E EDIÇÃO) ")
		tab.addTab(rent, " ALUGUEL DE TRAJES ")
		tab.addTab(ct, " TRAJES (CADASTRO E EDIÇÃO) ")
		vbox.addWidget(tab)
		self.setWindowTitle("ELEGANT TRAJES")
		self.setGeometry(left, top, width, height)
		self.setWindowIcon(QIcon("Pics/E_Trajes_01.svg"))
		self.setStyleSheet(open('CSS/etrajes.css').read( ))
		#label.setAlignment(Qt.AlignmentFlag.AlignCenter)
		self.show( )


if __name__ == "__main__":
	app = QApplication(sys.argv)
	window = MainWindow(app = app)
	sys.exit(app.exec( ))

Mas, infelizmente, ainda não funcionou.
Eu coloquei a parte do CSS (stylesheet_02) direto no arquivo etrajes.css e fiz a chamada na classe principal, mas mesmo assim não funciona. Eu fiz um teste usando o teu exemplo e funcionou, mas na minha aplicação não vai de jeito nenhum. Sera que é porquê os outros widgets vem de arquivos diferentes? Bom, o que eu estou fazendo de errado?

Depois de muita pesquisa, raiva, vontade de desistir e dor de cabeça, eu consegui chegar no resultado que eu queria. A interface está como foi mostrado na foto alguns posts acima e agora eu tenho acesso a todos os widgets. Ficou assim:

import sys
from PIL import ImageGrab
from Codes.Cad_Trajes import *
from Codes.Cad_Usr import *
from Codes.Rent import *
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QWidget, QApplication, QTabWidget, QVBoxLayout, QLabel, QGraphicsOpacityEffect

class Picture(QWidget):
	def __init__(self, pic, *args, **kwargs):
		super( ).__init__(*args, **kwargs)
		img = ImageGrab.grab( ).size
		wid = img[0]
		hg = img[1]
		width = wid // 2
		height = int(hg * 0.8)
		self.setGeometry(0, 0, width, height)
		self.pic = pic
		self.picLayout = QVBoxLayout( )
		self.picLayout.setContentsMargins(0,0,0,0)
		self.picLayout.setSpacing(0)
		picLabel = QLabel(self)
		pic = QPixmap(self.pic)
		picLabel.setPixmap(pic.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation))
		picLabel.setHidden(False)
		picLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
		self.setStyleSheet(open('CSS/etrajes.css').read( ))
		self.picLayout.addWidget(picLabel)
		effect = QGraphicsOpacityEffect( )
		effect.setOpacity(.1)
		picLabel.setGraphicsEffect(effect)
		self.setLayout(self.picLayout)

class Overlay(QWidget):
	def __init__(self, *args, **kwargs):
		super( ).__init__(*args, **kwargs)

		ct = CadTrajes( )
		cadusr = CadUsr( )
		rent = Renting( )
		
		img = ImageGrab.grab( ).size
		wid = img[0]
		hg = img[1]
		left = 0
		top = 0
		width = wid // 2
		height = int(hg * 0.8)
		self.setGeometry(left, top, width, height)
		box = QVBoxLayout( )
		tab = QTabWidget( )
		tab.addTab(cadusr, " CLIENTES (CADASTRO E EDIÇÃO) ")
		tab.addTab(rent, " ALUGUEL DE TRAJES ")
		tab.addTab(ct, " TRAJES (CADASTRO E EDIÇÃO) ")
		tab.setStyleSheet("background-color: transparent;")
		box.addWidget(tab)
		self.setWindowTitle("ELEGANT TRAJES")
		self.setGeometry(left, top, width, height)
		self.setWindowIcon(QIcon("Pics/E_Trajes_01.svg"))
		self.setStyleSheet(open('CSS/etrajes.css').read( ))
		self.setLayout(box)

class MainWindow(QWidget):
	def __init__(self, *args, **kwargs):
		super( ).__init__(*args, **kwargs)
		img = ImageGrab.grab( ).size
		wid = img[0]
		hg = img[1]
		left = wid // 4
		top = int(hg * 0.1)
		width = wid // 2
		height = int(hg * 0.8)
		self.layout = QVBoxLayout( )
		self.layout_2 = QVBoxLayout( )
		self.pic = Picture("Pics/E_Trajes.svg", parent = self)
		self.overlay = Overlay(parent = self)
		self.setStyleSheet(open('CSS/etrajes.css').read( ))
		self.setGeometry(left, top, width, height)

if __name__ == "__main__":
	app = QApplication(sys.argv)
	mw = MainWindow( )
	mw.show( )
	app.exec_( )

FIca aí como exemplo pra quem está tentando fazer algo desse tipo.
Valeu pela força @natorsc

1 curtida

Parabéns, excelente aprendizado!

:clap: :clap: :clap: :clap: :clap:

1 curtida

Solucionei esse “problema” da conversão através de um script que realizar a leitura dos arquivos .ui dentro de uma pasta e converte todos.

Script para conversão de .ui para .py

Depois criei o arquivo run.py que serve para executar os comandos como se estivesse no terminal.

É uma boa estratégia para utilizar para não utilizar o carregamento dinâmico dos arquivos .ui, assim ganhar um pouco de performance na aplicação e tempo no desenvolvimento.

Sou fã do seu trabalho, parabéns!
Abraços.

2 curtidas

Fantástico! :clap: :clap:

O seu raciocínio está corretíssimo e o mesmo se aplica a em diversos sistemas de empacotamento como o Flatpak, onde você acabaria descrevendo essa fase de conversão com meson por exemplo.

Recentemente tornei público um repositório sobre PySide6: https://github.com/natorsc/gui-python-pyside-qt.

2 curtidas