Add more than one Qmenu from other classes in MainWindow - python

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_())

Related

PyQt5 window not opening on show() when parent window is specified

I have defined a simple main window and second pop-up window. When I call the MainWindow.create_new_window() method, the SecondWindow does not show up as a new window but its QLabel is created within the MainWindow instance. Here's the code:
import sys
from PyQt5.QtWidgets import QApplication, QPushButton, QLabel, QWidget, QVBoxLayout
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.second_window = None
self.main_layout = QVBoxLayout(self)
self.new_window_button = QPushButton('New Window', self)
self.new_window_button.clicked.connect(self.create_new_window)
self.main_layout.addWidget(self.new_window_button)
def create_new_window(self):
if self.second_window is None:
self.second_window = SecondWindow(self)
self.second_window.show()
class SecondWindow(QWidget):
def __init__(self, *args, **kwargs):
super(SecondWindow, self).__init__(*args, **kwargs)
self.main_layout = QVBoxLayout(self)
self.hello_label = QLabel('Hello I am the second window.', self)
self.main_layout.addWidget(self.hello_label)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwin = MainWindow()
mainwin.show()
sys.exit(app.exec_())
When I create the second window without specifying the MainWindow instance as the parent (self.second_window = SecondWindow()), it opens as expected. Can anyone tell me what's going on here?
By default, a QWidget that has a parent implies that the widget will be placed inside the parent, so you observe that behavior.
If you want it to be a window then you must activate the flag Qt::Window
# ...
from PyQt5.QtCore import Qt
# ...
class SecondWindow(QWidget):
def __init__(self, *args, **kwargs):
super(SecondWindow, self).__init__(*args, **kwargs)
self.setWindowFlags(self.windowFlags() | Qt.Window) # <---
# ...
Other options is to use a QDialog that is a type of widget that by default already has that flag activated and whose objective is to ask the user for information.
From documentation:
If parent is 0, the new widget becomes a window. If parent is another widget, this widget becomes a child window inside parent. The new widget is deleted when its parent is deleted.
When I run your code, I get that new widget inside of the main one - as is described in documentation.
So basically, you should set parent to a QWidget only if you indend to use it as a window's widget (insert it in a layout, or use it as central widget etc.); if you want to use it as-is you don't need a parent.
If you need your widget to have a parent and be a separate window, better idea may be to use QDialog instead of QWidget. Just make your SecondWindow class subclass QDialog instead and you're good to go.
Example code (I've changed both your Windows to QDialog):
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QApplication, QDialog
class MainWindow(QDialog):
def __init__(self):
super(MainWindow, self).__init__()
self.second_window = None
self.main_layout = QVBoxLayout(self)
self.new_window_button = QPushButton('New Window', self)
self.new_window_button.clicked.connect(self.create_new_window)
self.main_layout.addWidget(self.new_window_button)
def create_new_window(self):
if self.second_window is None:
self.second_window = SecondWindow(self)
# set second window as modal, because MainWindow is QDialog/QWidget.
self.setModal(True)
self.second_window.show()
class SecondWindow(QDialog):
def __init__(self, *args, **kwargs):
super(SecondWindow, self).__init__(*args, **kwargs)
self.main_layout = QVBoxLayout(self)
self.hello_label = QLabel('Hello I am the second window.', self)
self.main_layout.addWidget(self.hello_label)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwin = MainWindow()
mainwin.show()
sys.exit(app.exec_())
In your imported packages import that staff:
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel

How can I get rid of the previous layout and set new Grid Layout in QMainWindow?

I am a newbie with PyQt. I am trying to organize my buttons on a grid layout, but I guess the window has a default layout already. How can I get rid of it and replace it with the new Grid Layout? I have contained the code block relevant with hashes ###, Here is my program:
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QWidget
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setMinimumSize (800,600) # set minimum size for window
self.setWindowTitle("CoolPlay Kabul") # set window title
self.setWindowIcon(QtGui.QIcon("images/CoolPlay.png"))# set icon for Window
myMenu = self.menuBar()
File_Menu = myMenu.addMenu("&File")
Items_Menu = myMenu.addMenu("&Items")
Playlist_Menu = myMenu.addMenu("&Playlist")
Option_Menu = myMenu.addMenu("&Option")
Exit_Menu = myMenu.addMenu("&Exit")
File_Menu.addAction("New Time")
File_Menu.addAction("Delete Time")
File_Menu.addSeparator()
File_Menu.addAction("Exit")
Items_Menu.addAction("New Item")
Items_Menu.addAction("Delete Item")
Items_Menu.addSeparator()
Items_Menu.addAction("Toggle Segue")
Playlist_Menu.addAction("Clear Playlist")
Playlist_Menu.addAction("Save playlist")
Playlist_Menu.addAction("Load Playlist")
Playlist_Menu.addSeparator()
Playlist_Menu.addAction("Clear 'Played' Indication")
Option_Menu.addAction("Application Setup")
Exit_Menu.addAction("Help")
Exit_Menu.addAction("About")
######################################################
Overall_Layout = QtGui.QGridLayout(self)
self.setLayout(Overall_Layout)
Play_Button = QtGui.QPushButton(QtGui.QIcon("images/PLAY.bmp"), "PLAY",self)
Overall_Layout.addWidget(Play_Button,1,2)
Overall_Layout.addWidget(Play_Button,2,2)
########################################################
self.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
CoolPlay = MainWindow()
CoolPlay.show()
sys.exit(app.exec_())
QMainWindow is a special widget since it already has a preset layout as shown below:
So in this case you should not set a layout to the QMainWindow but to the central widget, but first establish a centralwidget, using the indicated thing we get the following:
######################################################
central_widget = QtGui.QWidget()
self.setCentralWidget(central_widget)
Overall_Layout = QtGui.QGridLayout(central_widget)
Play_Button = QtGui.QPushButton(QtGui.QIcon("images/PLAY.bmp"), "PLAY")
Overall_Layout.addWidget(Play_Button,1,2)
Overall_Layout.addWidget(Play_Button,2,2)
########################################################
On the other hand if you inherit from QMainWindow you must call the QMainWindow constructor, but in code you call QWidget, so you must modify it to:
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Or
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()

How to add actions menu in a toolbar?

I want to add a menu from an item in a toolbar.
For example, from the following code:
import sys
from PyQt5.QtWidgets import QAction, QMainWindow, QApplication
class Menu(QMainWindow):
def __init__(self):
super().__init__()
colors = QAction('Colors', self)
exitAct = QAction('Exit', self)
self.statusBar()
toolbar = self.addToolBar('Exit')
toolbar.addAction(colors)
toolbar.addAction(exitAct)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
menu = Menu()
sys.exit(app.exec_())
I get:
I want to press on 'Colors' and get a list of options (like Qmenu, but for the toolbar).
How can I achieve this?
If you wish to add a QMenu to a QToolBar item you must add a widget that supports it, for example a QPushButton:
import sys
from PyQt5 import QtWidgets
class Menu(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
colorButton = QtWidgets.QPushButton("Colors")
exitAct = QtWidgets.QAction('Exit', self)
toolbar = self.addToolBar("Exit")
toolbar.addWidget(colorButton)
toolbar.addAction(exitAct)
menu = QtWidgets.QMenu()
menu.addAction("red")
menu.addAction("green")
menu.addAction("blue")
colorButton.setMenu(menu)
menu.triggered.connect(lambda action: print(action.text()))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
menu = Menu()
menu.show()
sys.exit(app.exec_())

PyQt5 - Passed reference is None

so I'm pretty new to Python. And for the life of me can't figure out why the following code isn't working (I'm using PyQt5). I'm basically trying to have 2 widgets inside a stackedwidget so I can switch between them. And the button to switch from window 0 to window 1 would be in window 0 obviously. So I would need to be able to somehow reference the stackedwidget. But when I try to pass the stackedwidget as a reference, it complains that the variable is None, even when that shouldn't be the case.
from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget, QGridLayout, QPushButton, QHBoxLayout, QVBoxLayout, QStackedWidget
from PyQt5.QtCore import QCoreApplication
class DeviceSelectionWindow(QWidget):
def __init__(self, mainWindow):
super().__init__()
self.initUI()
def initUI(self):
testB = QPushButton("test",self)
#------------------------------------------------------------
class ModeSelectionWindow(QWidget):
def __init__(self, mainWindow):
super().__init__()
self.mainWindow = mainWindow
self.initUI()
def initUI(self):
recordButton = QPushButton("Record tutorial")
watchButton = QPushButton("Watch tutorial")
recordButton.setFixedSize(200,100)
recordButton.setStyleSheet("font-size:30px")
watchButton.setFixedSize(200,100)
watchButton.setStyleSheet("font-size:30px")
recordButton.clicked.connect(self.mainWindow.setCurrentIndex(1))
#Add horizontal strech layout box (centered)
hbox = QHBoxLayout()
hbox.addWidget(recordButton)
hbox.addWidget(watchButton)
#Add vertical strech layout box (centered)
vbox = QVBoxLayout()
vbox.addLayout(hbox)
self.setLayout(vbox)
#------------------------------------------------------------
class MainWindow(QStackedWidget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.initUI()
def initUI(self):
self.resize(1200,600)
self.centerWindow()
self.setWindowTitle("MultiPov Tutorial")
modeSelectionWindow = ModeSelectionWindow(self)
deviceSelectionWindow = DeviceSelectionWindow(self)
self.addWidget(modeSelectionWindow)
self.addWidget(deviceSelectionWindow)
self.show()
def centerWindow(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
#------------------------------------------------------------
if __name__=='__main__':
app = QApplication(sys.argv)
mainWindow = MainWindow()
sys.exit(app.exec_())
The connect method takes a function name, you're doing a function call inside it which return None.
recordButton.clicked.connect(self.mainWindow.setCurrentIndex(1))
One way to get around this is to write a method that does the work (change the index)
def setWindow1(self):
self.mainWindow.setCurrentIndex(1)
Then connect that method to the clicked signal
recordButton.clicked.connect(self.setWindow1)
Or, use a lambda function and get it done in one line
recordButton.clicked.connect(lambda: self.mainWindow.setCurrentIndex(1))

Python PyQt4: Single child window

I have a simple PyQt4 example.
When run, it displays a QMainWindow with a button.
If you click the button, then a second QMainWindow is created.
If you click it again, you get 2 second windows.
What is an elegant and simple way to prevent more than 1 second window in this example?
import sys
from PyQt4.QtGui import *
class win2(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self,parent)
layout = QVBoxLayout()
label = QLabel(self)
label.setText('This is win2')
layout.addWidget(label)
self.adjustSize()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
layout = QVBoxLayout()
button1 = QPushButton("win2", self)
layout.addWidget(button1)
button1.clicked.connect(self.showwin2)
def showwin2(self):
w2 = win2(self)
w2.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
Your Function creates a new instance of the class win2 each time the button is pressed. To Supress this behavior only call the show and raise_ functions instead of creating a new instance.
I would create the class as follows, and only use the button to 'show' the window. Tested and works as intended. Also consider using self when assigning your variables so they can be accessed throughout the class instance.
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
layout = QVBoxLayout()
button1 = QPushButton("win2", self)
layout.addWidget(button1)
button1.clicked.connect(self.showwin2)
self.w2 = win2(self)
def showwin2(self):
self.w2.show()
self.w2.raise_()

Categories