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))
Related
I found some code on here that shows an example of how you can get the window to resize when the widget is hidden, and it works for me. Here is the code:
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super(MainWindow, self).__init__()
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def startup(self):
self.show()
sys.exit(self.app.exec_())
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
myApp = MainWindow()
myApp.startup()
I then tried to modify this to match my existing code by separating the mainWindow class and the widget class. Here is the code that does that.
from PySide import QtGui,QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.setCentralWidget(self.w)
self.show()
class testW(QtGui.QWidget):
def __init__(self,parent):
super(testW,self).__init__()
self.parent = parent
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.button.setChecked(True);
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myApp = MainWindow()
sys.exit(app.exec_())
#time.sleep(1)
Running the first code does what I want it to. After I hide the widget, the window resizes to the correct size. The second implementation of the code does not shrink and expand the window when I hide and show the widget. Is this because the MainWindow is in a separate class?
Use size policies for your widgets. For your example you can change UI creation code as follows:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.w.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding
)
self.setCentralWidget(self.w)
self.show()
Please note new setSizePolicy call which say Qt layout engine how to change the size of your widget according to its content.
Unfortunately QMainWindow does not respect sizeHint automatically, but it is calculated properly, so you can adjustSize manually:
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.parent.adjustSize)
You do not need to resize your widget itself, because it will be resized according to the policy. Even sizeHint will be calculated automatically so you need only to call adjustSize of QMainWindow.
PS: I used PySide2 instead of PySide so the imports are different a little bit:
from PySide2 import QtWidgets, QtCore
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_())
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
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__()
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_()