How can I avoid having PyQT5 Central Widget covering up MenuBar? - python

The central widget in my QMainWindow keeps covering up the QMenuBar I want. How do I avoid this?
If I comment out the pushbutton, I can see the menu bar using the code below.
from PyQt5 import QtWidgets
class Test:
def __init__(self):
self.app = QtWidgets.QApplication([])
self.win = QtWidgets.QMainWindow()
self.win.resize(100,100)
menu_bar = QtWidgets.QMenuBar(self.win)
file_menu = menu_bar.addMenu('&File')
pb = QtWidgets.QPushButton('push me!')
# self.win.setCentralWidget(pb)
self.win.show()
self.app.exec()
if __name__ == '__main__':
Test()
Shouldn't the QMainWindow manage to separate them according to this?

You must set the QMenuBar in the QMainWindow using setMenuBar():
from PyQt5 import QtWidgets
class Test:
def __init__(self):
self.app = QtWidgets.QApplication([])
self.win = QtWidgets.QMainWindow()
self.win.resize(100,100)
menu_bar = QtWidgets.QMenuBar(self.win)
self.win.setMenuBar(menu_bar)
file_menu = menu_bar.addMenu('&File')
pb = QtWidgets.QPushButton('push me!')
self.win.setCentralWidget(pb)
self.win.show()
self.app.exec()
if __name__ == '__main__':
Test()

Related

How to destroy a QApplication and then run a new one without exiting the python script?

I want to create a QApplication which is then exited using a keyboard shortcut. Then the python script should call another QApplication.
My issues currently is that I get this error when the second QApplication is about to run:
app2 = QApplication()
RuntimeError: Please destroy the QApplication singleton before creating a new QApplication instance.
I have the following structure:
| main.py
| Q1.py
| Q2.py
This is main.py:
import Q1 as record
import Q2 as display
def main():
record.main()
display.main()
if __name__ == "__main__":
main()
This is Q1 which creates the problem:
import sys
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
from PySide2 import QtMultimedia as qtmm
class MainWindow(qtw.QMainWindow):
def __init__(self):
super().__init__()
#Create Window layout with a sound widget
soundboard = qtw.QWidget()
soundboard.setLayout(qtw.QGridLayout())
self.setCentralWidget(soundboard)
sw = SoundWidget()
soundboard.layout().addWidget(sw)
#Window Dimensions
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.MinimumExpanding)
# Code ends here
self.show()
class SendOrderButton(qtw.QPushButton):
button_stylesheet = 'background-color: blue; color: white;'
def __init__(self):
super().__init__('Send Order')
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
self.setStyleSheet(self.button_stylesheet)
#self.clicked.connect(qtc.QCoreApplication.instance().quit)
def press_button(self):
if self.isEnabled():
self.setEnabled(False)
self.setText('Send Order')
else:
self.setEnabled(True)
self.setText('Sent')
class SoundWidget(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QGridLayout())
#Send Order Button
self.sendorder_button = SendOrderButton()
self.sendorder_button.setShortcut(qtg.QKeySequence('Tab'))
self.layout().addWidget(self.sendorder_button, 5, 0, 1, 2)
self.sendorder_button.clicked.connect(qtc.QCoreApplication.instance().quit)
def main():
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
This is Q2.py which has the second QApplication:
import sys
from PySide2.QtCore import (QAbstractTableModel, Slot)
from PySide2.QtWidgets import (QAction, QApplication, QMainWindow,QWidget)
class MainWindow(QMainWindow):
def __init__(self, widget):
QMainWindow.__init__(self)
# Exit QAction
exit_action = QAction("Exit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.exit_app)
#Slot()
def exit_app(self, checked):
sys.exit()
class CustomTableModel(QAbstractTableModel):
def __init__(self, data=None):
QAbstractTableModel.__init__(self)
class Widget(QWidget):
def __init__(self):
QWidget.__init__(self)
# Getting the Model
self.model = CustomTableModel()
def main():
app2 = QApplication()
widget = Widget()
window2 = MainWindow(widget)
window2.show()
sys.exit(app2.exec_())
if __name__ == "__main__":
app = QApplication()
widget = Widget()
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())
As noted in the comments a Qt application can only and should have a QApplication (you might not follow this rule but nothing guarantees that it works correctly) so you will have to restructure your code.
Assuming that you want the Q1 window to be first and when that window is closed then the Q2 window opens that does not imply at any time that you have to use several QApplication. The idea is to know when a window is closed and to be notified of it, to know when a window is closed then you must override the closeEvent method of the window and to make the notification you must send a signal.
Considering the above, the solution is:
├── main.py
├── Q1.py
└── Q2.py
main.py
import sys
from PySide2 import QtWidgets as qtw
import Q1 as record
import Q2 as display
def main():
app = qtw.QApplication(sys.argv)
w1 = record.get_mainwindow()
w2 = display.get_mainwindow()
w1.closed.connect(w2.show)
w1.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Q1.py
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
class MainWindow(qtw.QMainWindow):
closed = qtc.Signal()
def __init__(self):
super().__init__()
# Create Window layout with a sound widget
soundboard = qtw.QWidget()
soundboard.setLayout(qtw.QGridLayout())
self.setCentralWidget(soundboard)
sw = SoundWidget()
soundboard.layout().addWidget(sw)
# Window Dimensions
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.MinimumExpanding)
sw.sendorder_button.clicked.connect(self.close)
def closeEvent(self, event):
self.closed.emit()
super().closeEvent(event)
class SendOrderButton(qtw.QPushButton):
button_stylesheet = "background-color: blue; color: white;"
def __init__(self):
super().__init__("Send Order")
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
self.setStyleSheet(self.button_stylesheet)
def press_button(self):
if self.isEnabled():
self.setEnabled(False)
self.setText("Send Order")
else:
self.setEnabled(True)
self.setText("Sent")
class SoundWidget(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QGridLayout())
# Send Order Button
self.sendorder_button = SendOrderButton()
self.sendorder_button.setShortcut(qtg.QKeySequence("Tab"))
self.layout().addWidget(self.sendorder_button, 5, 0, 1, 2)
def get_mainwindow():
window = MainWindow()
return window
if __name__ == "__main__":
import sys
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Q2.py
from PySide2 import QtCore as qtc
from PySide2 import QtWidgets as qtw
class MainWindow(qtw.QMainWindow):
def __init__(self, widget):
super().__init__()
file_menu = self.menuBar().addMenu("&File")
# Exit QAction
exit_action = qtw.QAction("Exit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
class CustomTableModel(qtc.QAbstractTableModel):
pass
class Widget(qtw.QWidget):
def __init__(self):
super().__init__()
# Getting the Model
self.model = CustomTableModel()
def get_mainwindow():
widget = Widget()
window2 = MainWindow(widget)
return window2
if __name__ == "__main__":
import sys
app = qtw.QApplication()
widget = Widget()
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())

Common buttons and positioning in PyQt5's QStackedLayout

I am creating a Python PyQt5 app for a university project, which uses QStackedLayout to keep the app single-windowed.
First question: how can I create buttons that appear on every window and have the same function and property without having to recreate them in every window's UI setup (like in the code)?
Second question: after switching windows, how can I open the newly opened window at the previous's position? Right now they just open at the centre of the screen. I assume pos() or QPoint should be used but I can not figure out how.
import sys
from PyQt5.QtWidgets import (QApplication,
QMainWindow,
QPushButton,
QWidget,
QStackedLayout)
class Ui(QWidget):
def setupUi(self, Main):
self.application_width = 200
self.application_height = 200
self.stack = QStackedLayout()
self.window_1 = QWidget()
self.window_2 = QWidget()
self.window_1_UI()
self.window_2_UI()
self.stack.addWidget(self.window_1)
self.stack.addWidget(self.window_2)
def window_1_UI(self):
self.window_1.setFixedSize(self.application_width, self.application_height)
self.window_1.setWindowTitle("1")
'''REPLACE THIS BUTTON'''
self.window_1_button = QPushButton("Change window", self.window_1)
def window_2_UI(self):
self.window_2.setFixedSize(self.application_width, self.application_height)
self.window_2.setWindowTitle("2")
'''AND REPLACE THIS BUTTON'''
self.window_2_button = QPushButton("Change window", self.window_2)
class Main(QMainWindow, Ui):
def __init__(self):
super(Main, self).__init__()
self.setupUi(self)
self.window_1_button.clicked.connect(self.change_window)
self.window_2_button.clicked.connect(self.change_window)
def change_window(self):
if self.stack.currentIndex() == 0:
self.stack.setCurrentIndex(1)
else:
self.stack.setCurrentIndex(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
M = Main()
sys.exit(app.exec())
Put your button outside your widgets (in your Main widget). The Ui class should manage the stacked layout.
For example:
class Ui(QWidget):
def setupUi(self, Main):
self.stack = QStackedLayout()
self.window_1 = QWidget()
self.window_2 = QWidget()
self.window_1_UI()
self.window_2_UI()
self.stack.addWidget(self.window_1)
self.stack.addWidget(self.window_2)
# Only one button
self.btn = QPushButton("Change window", self)
# Create the central widget of your Main Window
self.main_widget = QWidget()
layout = QVBoxLayout(self.main_widget)
layout.addLayout(self.stack)
layout.addWidget(self.btn)
self.setCentralWidget(self.main_widget)
self.btn.clicked.connect(self.change_window)
def change_window(self):
if self.stack.currentIndex() == 0:
self.stack.setCurrentIndex(1)
else:
self.stack.setCurrentIndex(0)
def window_1_UI(self):
label = QLabel("In Window 1", self.window_1)
def window_2_UI(self):
label = QLabel("In Window 2", self.window_2)
class Main(QMainWindow, Ui):
def __init__(self):
super(Main, self).__init__()
self.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
M = Main()
M.show()
sys.exit(app.exec())

pyqt5 Is there a limit to loading widgets using clicked.connect?

I'm using the QPushButton to load the UI. First -> Jumin -> Department -> next -> next I want to create the UI in order. The problem is that I can not load the third Department into the QMainwindow window. I do not know why
When you create a widget in QVBoxLayout, it changes the size of the widget according to the wallpaper like wxpython layout (wx.all). Can not change the position (move) and size (resize) by automatic centering?
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
def FirstUI(self):
self.btn1 = QPushButton('test1', self)
self.btn1.move(50, 50)
self.btn1.clicked.connect(self.btn1_click)
def JuminUI(self):
self.ju1 = QLineEdit('13')
self.btn2 = QPushButton('^^^^^^^^^^')
self.ju_text = QLabel('asd')
self.jumim_layout = QVBoxLayout()
self.jumim_layout.addWidget(self.ju_text)
self.jumim_layout.addWidget(self.ju1)
self.jumim_layout.addWidget(self.btn2)
self.centralWidget().setLayout(self.jumim_layout)
self.btn2.clicked.connect(self.btn2_click)
def DepartmentUI(self):
self.depart_layout = QVBoxLayout()
self.depart_layout.addWidget(QPushButton('sdfsdf'))
self.centralWidget().setLayout(self.depart_layout)
def btn1_click(self):
self.btn1.deleteLater()
self.JuminUI()
def btn2_click(self):
self.ju1.deleteLater()
self.btn2.deleteLater()
self.ju_text.deleteLater()
self.DepartmentUI()
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
creating and removing widgets is almost always a bad idea, and your code falls into those bad ideas, it's always best to hide the widgets and for that you should use the QStackedWidget, what QStackedWidget does is just make a widget visible on all widgets that you have been assigned by changing the currentIndex.
import sys
from functools import partial
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QtWidgets.QStackedWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
self.JuminUI()
self.DepartmentUI()
def FirstUI(self):
widget = QtWidgets.QWidget()
self.btn1 = QtWidgets.QPushButton('test1', widget)
self.btn1.move(50, 50)
self.center_widget.addWidget(widget)
self.btn1.clicked.connect(partial(self.center_widget.setCurrentIndex, 1))
def JuminUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
self.ju1 = QtWidgets.QLineEdit('13')
self.btn2 = QtWidgets.QPushButton('^^^^^^^^^^')
self.ju_text = QtWidgets.QLabel('asd')
lay.addWidget(self.ju_text)
lay.addWidget(self.ju1)
lay.addWidget(self.btn2)
self.center_widget.addWidget(widget)
self.btn2.clicked.connect(partial(self.center_widget.setCurrentIndex, 2))
def DepartmentUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(QtWidgets.QPushButton('sdfsdf'))
self.center_widget.addWidget(widget)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
fream = MainWindow()
fream.show()
sys.exit(app.exec_())

How to focus view on QLabel

I want to find QLabel with specific text and focus view on it. Finding widget with desired text is easy, but I couldn't figure out how to focus view on it.
The code so far looks like this:
import sys
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super().__init__()
widget = QtGui.QWidget()
self.layout = QtGui.QGridLayout()
for i in range(10):
label = QtGui.QLabel("aaaa" + str(i))
self.layout.addWidget(label, i, 0)
widget.setLayout(self.layout)
self.toolbar = self.addToolBar("aa")
findAction = QtGui.QAction('Find', self)
findAction.triggered.connect(self.find)
self.toolbar.addAction(findAction)
self.scroll = QtGui.QScrollArea()
self.scroll.setWidget(widget)
self.scroll.setWidgetResizable(True)
self.setMaximumSize(200, 200)
self.setCentralWidget(self.scroll)
def find(self):
widgets = (self.layout.itemAt(i).widget() for i in range(self.layout.count()))
for w in widgets:
if isinstance(w, QtGui.QLabel):
if w.text() == "aaaa9":
w.setFocus()
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec_()
if __name__ == "__main__":
main()
Any ideas how to get focus working?
You can use ensureWidgetVisible()
self.scroll.ensureWidgetVisible(w)

PySide QLabel doesn't fill the available height

In the GUI, the styling applied to the QLabel is applied only upto the height of the text in it. How do I increase it to fill the available region?
You can try something like this:
from PySide import QtGui, QtCore
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
super().__init__()
layout = QtGui.QHBoxLayout()
self.setLayout(layout)
label = QtGui.QLabel('5')
label.setAutoFillBackground(True)
p = label.palette()
p.setColor(label.backgroundRole(), QtCore.Qt.red)
label.setPalette(p)
layout.addWidget(label)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories

Resources