How to create a Widget using .ui file? - python

I'd like to create a widget inside a MainWindow that gets its design from a file created with QtDesigner.
the .ui file I created is test.ui
I have a MainWindow instance inheriting from QMainWindow that will create a stacked widget with many widgets inside it. For simplicity in this example, it will create just a widget as central widget.
The My Widget instance inside the MainWindow is the one that has to grab its design from the .ui file
As it is, the application shows a blank window
Here is the code:
from PySide2.QtWidgets import QMainWindow, QApplication, QDesktopWidget, QWidget
from PySide2.QtCore import QCoreApplication, Qt
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile
class MyWidget(QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
ui_file = QFile("test.ui")
ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui_dim_percentage = 70/100
self.initUI()
def initUI(self):
self.center()
self.home_widget = MyWidget(self)
self.setCentralWidget(self.home_widget)
self.show()
def center(self): # resizes the UI to a percentage of the screen and centers the widget
screen_size = QDesktopWidget().screenGeometry()
self.resize(screen_size.width()*self.ui_dim_percentage, screen_size.height()*self.ui_dim_percentage)
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def main():
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
How should I correct the code?

The problem is that you are confusing concepts, when you use QUiLoader you are creating the window that in this case is the window variable that is a local variable, that is, the .ui does not fill in MyWidget.
The solution is not to create the MyWidget class, just use the window variable.
from PySide2.QtWidgets import QMainWindow, QApplication, QDesktopWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
def create_widget():
ui_file = QFile("test.ui")
if ui_file.open(QFile.ReadOnly):
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
return window
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui_dim_percentage = 70 / 100
self.initUI()
def initUI(self):
self.center()
self.home_widget = create_widget()
self.setCentralWidget(self.home_widget)
self.show()
# resizes the UI to a percentage of the screen and centers the widget
def center(self):
screen_size = QDesktopWidget().screenGeometry()
self.resize(
screen_size.width() * self.ui_dim_percentage,
screen_size.height() * self.ui_dim_percentage,
)
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def main():
import sys
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Have you tried to convert your ui-file?
You can use pyuic4 command on shell:
pyuic4 test.ui -o test.py

Related

PyQt5 Layouts not moving button [duplicate]

I'm making an application in PyQt4 and this is my code so far:
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUi()
def initUi(self):
self.setWindowTitle('Main Menu')
self.setFixedSize(1200, 625)
self.firstWidgets()
self.show()
def firstWidgets(self):
self.vbox1 = QtGui.QVBoxLayout()
self.task1 = QtGui.QLabel('Check 1', self)
self.task1CB = QtGui.QCheckBox(self)
self.hbox1 = QtGui.QHBoxLayout()
self.hbox1.addWidget(self.task1)
self.hbox1.addWidget(self.task1CB)
self.vbox1.addLayout(self.hbox1)
self.setLayout(self.vbox1)
def main():
application = QtGui.QApplication(sys.argv)
gui = MainWindow()
sys.exit(application.exec_())
if __name__=='__main__':
main()
My problem is in MainWindow.firstWidgets(). I try to set a layout but I get an error even though that's my first time using .setLayout for that form, which confuses me.
QWidget::setLayout: Attempting to set QLayout "" on MainWindow "",
which already has a layout
You can't set a QLayout directly on the QMainWindow. You need to create a QWidget and set it as the central widget on the QMainWindow and assign the QLayout to that.
wid = QtGui.QWidget(self)
self.setCentralWidget(wid)
layout = QtGui.QVBoxLayout()
wid.setLayout(layout)
NOTE: This is for Qt4 -- see the other answer on this question for the Qt5 updated code.
Just an update to Brenden Abel's answer:
QWidget and QVBoxLayout (for Python3, PyQt5) are now contained in the PyQt5.QtWidgets module and not the PyQt5.QtGui module.
So updated code:
wid = QtWidgets.QWidget(self)
self.setCentralWidget(wid)
layout = QtWidgets.QVBoxLayout()
wid.setLayout(layout)
This is an example using PyQt5
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('My App')
# Cannot set QxxLayout directly on the QMainWindow
# Need to create a QWidget and set it as the central widget
widget = QWidget()
layout = QVBoxLayout()
b1 = QPushButton('Red' ); b1.setStyleSheet("background-color: red;")
b2 = QPushButton('Blue' ); b2.setStyleSheet("background-color: blue;")
b3 = QPushButton('Yellow'); b3.setStyleSheet("background-color: yellow;")
layout.addWidget(b1)
layout.addWidget(b2)
layout.addWidget(b3)
widget.setLayout(layout)
self.setCentralWidget(widget)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Add more than one Qmenu from other classes in MainWindow

I want a single menubar in my main window and be able to set the menus in the menubar from additional classes. Using the setMenuWidget command will overwrite the first menu option as shown in the code. In the classes where I set up the menu I think I may need to just set up a menu rather than a menubar, then set up the menubar in the main window.
This is what I would l like, which can be achieved by populating a single menubar in a class, though I am trying to avoid this method.
Instead only the second menu is show
import sys
from PyQt5.QtWidgets import QAction, QApplication, QMainWindow
from PyQt5 import QtCore, QtGui, QtWidgets
class ToolBar0(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self)
bar = self.menuBar() # don't think I need a menubar here
file_menu = bar.addMenu('menu1')
one = QAction('one', self)
two = QAction('two', self)
file_menu.addAction(one)
file_menu.addAction(two)
class ToolBar1(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self)
bar = self.menuBar() # don't think I need a menubar here
file_menu = bar.addMenu('menu2')
one = QAction('one', self)
two = QAction('two', self)
file_menu.addAction(one)
file_menu.addAction(two)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self, parent=None)
#should a menubar be set up here?
#For seting widgets in main window
self.Tool_Bar0 = ToolBar0(self)
self.setMenuWidget(self.Tool_Bar0)
###menu_bar0 is over written
self.Tool_Bar1 = ToolBar1(self)
#self.setMenuWidget(self.Tool_Bar1)
if __name__ == '__main__':
app = QApplication(sys.argv)
# creating main window
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
You could use a base class with a method to return either a list of QMenu items containing QAction items or a list of QAction items and then render them in your QMainWindow toolbar in whichever way you want, here is an example:
import sys
from PyQt5.QtWidgets import QAction, QApplication, QMainWindow, QMenu
class WindowWithToolbar:
def __init__(self):
super().__init__()
def menu_items(self)->list:
pass
class Window1(WindowWithToolbar, QMainWindow):
def __init__(self):
WindowWithToolbar.__init__(self)
QMainWindow.__init__(self)
# New menu with actions
self.menu = QMenu('one')
self.menu.addActions([QAction('two', self), QAction('three', self)])
def menu_items(self):
return [self.menu]
class Window2(WindowWithToolbar, QMainWindow):
def __init__(self):
WindowWithToolbar.__init__(self)
QMainWindow.__init__(self)
def menu_items(self):
# Only actions
return [QAction('three', self), QAction('four', self)]
class MainWindow(WindowWithToolbar, QMainWindow):
def __init__(self):
QMainWindow.__init__(self, parent=None)
self.window1 = Window1()
self.window2 = Window2()
self.menu = QMenu('File')
self.helloAction = QAction('Hello')
self.menu.addAction(self.helloAction)
self._build_menu()
def menu_items(self)->list:
return [self.menu]
def _build_menu(self):
self._add_menu_items(self)
self._add_menu_items(self.window1)
self._add_menu_items(self.window2)
def _add_menu_items(self, windowWithToolbar: WindowWithToolbar):
for menu_item in windowWithToolbar.menu_items():
if isinstance(menu_item, QMenu):
self.menuBar().addMenu(menu_item)
elif isinstance(menu_item, QAction):
self.menuBar().addAction(menu_item)
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())

Open a file from main window to a new window in PyQt5 (in different files)

I have two files, one for my main window, which has one image and one button and one for a new window. What I want it to do is that when I push the button from my main window, it lets me load a file and show it in a TextEdit widget in the new window
so here I have the files I'm using:
MainWindow.py
import sys
import os
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QTextEdit, QHBoxLayout, QLabel, QMainWindow, QAction, QFileDialog
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.img = QLabel()
self.relleno=QLabel()
self.btn_load = QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_layout = QHBoxLayout()
v_layout = QVBoxLayout()
h_final = QHBoxLayout()
h_layout.addWidget(self.img)
v_layout.addWidget(self.btn_load)
h_final.addLayout(h_layout)
h_final.addLayout(v_layout)
self.btn_load.clicked.connect(self.loadafile)
self.setLayout(h_final)
self.setWindowTitle('This is main window')
self.setGeometry(600,150,self.width,self.height)
self.show()
def loadafile(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename[0], 'r') as f:
file_text = f.read()
return file_text
def main():
app = QApplication(sys.argv)
main = Window()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
NewWindow.py
import os
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout
from MainWindow import loadafile
info=loadafile()
class SecondWindow(QWidget):
def __init__(self):
super(SecondWindow, self).__init__()
self.text = QTextEdit(self)
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout()
v_layout.addWidget(self.text)
self.setLayout(v_layout)
self.setText(info)
self.setWindowTitle('Opened Text')
self.show()
app = QApplication(sys.argv)
shower = SecondWindow()
sys.exit(app.exec_())
I think the loadafile does return my file_text variable but I don't know how to open the new window from there. I think I need to use a destructor for main window and then show the new window but I'm not sure of how to do this (This is the first time I try OOP)
A program is not a set of files, especially in OOP a program is the interactions of objects. And the objects interact if they have the same scope, so both windows must be created in one place so that the information from one pass to the other.
On the other hand in Qt there is a fundamental concept that is the signals, this functionality allows to notify the change of a state to another object without a lot of dependency, so in this case I will create a signal that transmits the text to the other object.
NewWindow.py
from PyQt5 import QtWidgets
class SecondWindow(QtWidgets.QWidget):
def __init__(self):
super(SecondWindow, self).__init__()
self.text = QtWidgets.QTextEdit(self)
self.init_ui()
def init_ui(self):
v_layout = QtWidgets.QVBoxLayout(self)
v_layout.addWidget(self.text)
self.setWindowTitle('Opened Text')
self.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
shower = SecondWindow()
sys.exit(app.exec_())
MainWindow.py
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from NewWindow import SecondWindow
class Window(QtWidgets.QWidget):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.img =QtWidgets.QLabel()
self.relleno=QtWidgets.QLabel()
self.btn_load = QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_final = QtWidgets.QHBoxLayout(self)
h_final.addWidget(self.img)
h_final.addWidget(self.btn_load)
self.btn_load.clicked.connect(self.loadafile)
self.setWindowTitle('This is main window')
self.setGeometry(600,150,self.width,self.height)
self.show()
#QtCore.pyqtSlot()
def loadafile(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename, 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
s = SecondWindow()
main.textChanged.connect(s.text.append)
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Open and save image file using menubar in PyQt5

I've written the following code to open image file using a menubar in PyQt5. It is able to select the file but not able to display it in the window. I've successfully opened text file but not able to do the same for images. Can you please rectify my error?
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QLabel, QFileDialog, QAction
from PyQt5.QtGui import QIcon, QPixmap
class MainWindow(QMainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
menubar = self.menuBar()
fileMenu = menubar.addMenu('File')
editMenu = menubar.addMenu('Edit')
self.resize(500, 500)
dlg = QFileDialog(self)
openAction = QAction('Open Image', self)
openAction.triggered.connect(self.openImage)
fileMenu.addAction(openAction)
closeAction = QAction('Exit', self)
closeAction.triggered.connect(self.close)
fileMenu.addAction(closeAction)
def openImage(self):
# This function is called when the user clicks File->Open Image.
label = QLabel(self)
filename = QFileDialog.getOpenFileName()
imagePath = filename[0]
print(imagePath)
pixmap = QPixmap(imagePath)
label.setPixmap(pixmap)
self.resize(pixmap.width(),pixmap.height())
self.show()
def main():
app = QApplication(sys.argv)
win = MainWindow()
win.show()
app.exec_()
if __name__ == '__main__':
sys.exit(main())
When you call show() the widget makes children visible, in your case QLabel is not a child when that method is used, so a trivial but partial solution is to make it visible:
def openImage(self):
label = QLabel(self)
label.show()
# or
# self.show()
But in the case of QMainWindow is not suitable, QMainWindow is a very special widgets because it has a definite structure as shown in the following image:
As you can see, QLabel is the centralWidget and create it only once, and then you only have to replace the QPixmap if you select a new image:
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel, QFileDialog, QAction
from PyQt5.QtGui import QPixmap
class MainWindow(QMainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
menubar = self.menuBar()
fileMenu = menubar.addMenu('File')
editMenu = menubar.addMenu('Edit')
self.resize(500, 500)
openAction = QAction('Open Image', self)
openAction.triggered.connect(self.openImage)
fileMenu.addAction(openAction)
closeAction = QAction('Exit', self)
closeAction.triggered.connect(self.close)
fileMenu.addAction(closeAction)
self.label = QLabel()
self.setCentralWidget(self.label)
def openImage(self):
imagePath, _ = QFileDialog.getOpenFileName()
pixmap = QPixmap(imagePath)
self.label.setPixmap(pixmap)
self.resize(pixmap.size())
self.adjustSize()
def main():
app = QApplication(sys.argv)
win = MainWindow()
win.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())

Qt Multiple GraphicsEffects

Is there a way in Qt5 to apply a QGraphicsEffect to a widget, even when one of its parent widgets already has a QGraphicsEffect applied to it?
When i try the following:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class ParentWidget(QWidget):
def __init__(self,parent):
super().__init__(parent)
effect = QGraphicsBlurEffect(self)
self.setGraphicsEffect(effect)
effect.setBlurRadius(0)
class ChildWidget(ParentWidget):
def __init__(self,parent):
super().__init__(parent)
self.layout = QGridLayout(self)
widget = QWidget()
widget.setObjectName('reviewControlArea')
effect = QGraphicsOpacityEffect(widget)
widget.setGraphicsEffect(effect)
self.layout.addWidget(widget)
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
cw = ChildWidget(MainWindow)
MainWindow.setCentralWidget(cw)
MainWindow.show()
sys.exit(app.exec_())
The stdout says:
QPainter::begin: A paint device can only be painted by one painter at a time.

Categories