How to make "finish" button in QStackedWidget - python-3.x

I trying to create "Finish" button in QStackedWidget.
In a "checkButtons" function i checking current page index and set click events and text. I tried to check it by class name, but it doesn't work too.
Here is a code:
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QDialog, QComboBox, QStackedWidget, QWidget,
QPushButton, QLabel, QVBoxLayout, QHBoxLayout, QStyle)
class Main(QDialog):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# Main window setup
self.setWindowTitle("Stacked widget example")
self.setWindowIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
self.setMinimumSize(400, 400)
self.setMaximumSize(640, 480)
self.rootVBox = QVBoxLayout()
self.rootHBox = QHBoxLayout()
self.rootHBox.addStretch()
self.rootVBox.addStretch()
self.pages = [FirstPage, SecondPage]
self.stacked = QStackedWidget(self)
for i in self.pages: self.stacked.addWidget(i(self))
self.pageState = True
self.buttonNext = QPushButton("Next")
self.buttonNext.clicked.connect(self.buttonNextConnect)
self.buttonBack = QPushButton("Back")
self.buttonBack.clicked.connect(self.buttonBackConnect)
self.rootHBox.addWidget(self.buttonBack)
self.rootHBox.addWidget(self.buttonNext)
self.rootVBox.addLayout(self.rootHBox)
self.setLayout(self.rootVBox)
def checkButtons(self):
print(self.stacked.currentIndex())
# I tried to check self.stacked.currentIndex() but it didn't work too
# if self.stacked.currentWidget().__class__ == self.pages[-1]:
if self.stacked.currentIndex() == len(self.pages) - 1:
self.buttonNext.setText("Finish")
self.buttonNext.clicked.connect(self.close)
elif self.stacked.currentIndex() < len(self.pages) - 1:
self.buttonNext.setText("Next")
self.buttonNext.clicked.connect(self.buttonNextConnect)
def buttonNextConnect(self):
self.stacked.setCurrentIndex(self.stacked.currentIndex() + 1)
self.checkButtons()
def buttonBackConnect(self):
self.stacked.setCurrentIndex(self.stacked.currentIndex() - 1)
self.checkButtons()
def finish(self):
self.close()
class FirstPage(QWidget):
def __init__(self, parent=None):
super(FirstPage, self).__init__(parent)
label = QLabel("First page")
rootVBox = QVBoxLayout()
rootHBox = QHBoxLayout()
rootHBox.addWidget(label)
rootVBox.addLayout(rootHBox)
self.setLayout(rootVBox)
class SecondPage(QWidget):
def __init__(self, parent=None):
super(SecondPage, self).__init__(parent)
label = QLabel("Second page")
rootVBox = QVBoxLayout()
rootHBox = QHBoxLayout()
rootHBox.addWidget(label)
rootVBox.addLayout(rootHBox)
self.setLayout(rootVBox)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
If you try to press "next", "back" and then "next" again a program will be close. So, how can i fix it? Should i just make control buttons for each widget?

You must use the currentChanged signal of the QStackedWidget to know what page you are on and thus change the text, but in the buttonNextConnect slot you should check if you are already on the last page before switching to a new page, if you are then call to finish and if you do not change to another page
class Main(QDialog):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# Main window setup
self.setWindowTitle("Stacked widget example")
self.setWindowIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
self.setMinimumSize(400, 400)
self.setMaximumSize(640, 480)
rootVBox = QVBoxLayout(self)
rootHBox = QHBoxLayout()
rootHBox.addStretch()
rootVBox.addStretch()
self.pages = [FirstPage, SecondPage]
self.stacked = QStackedWidget(self)
for i in self.pages: self.stacked.addWidget(i(self))
self.buttonNext = QPushButton("Next")
self.buttonNext.clicked.connect(self.buttonNextConnect)
self.buttonBack = QPushButton("Back")
self.buttonBack.clicked.connect(self.buttonBackConnect)
rootHBox.addWidget(self.buttonBack)
rootHBox.addWidget(self.buttonNext)
rootVBox.addLayout(rootHBox)
self.stacked.currentChanged.connect(self.on_currentChanged)
def buttonNextConnect(self):
if self.stacked.currentIndex() == self.stacked.count() -1:
self.finish()
if self.stacked.currentIndex() < self.stacked.count() -1:
self.stacked.setCurrentIndex(self.stacked.currentIndex() + 1)
def buttonBackConnect(self):
if self.stacked.currentIndex() > 0:
self.stacked.setCurrentIndex(self.stacked.currentIndex() - 1)
def on_currentChanged(self, index):
if index == self.stacked.count() -1:
self.buttonNext.setText("Finish")
else:
self.buttonNext.setText("Next")
def finish(self):
self.close()
Another option is to use QWizard and QWizardPage:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Main(QtWidgets.QWizard):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
buttons = [
QtWidgets.QWizard.Stretch,
QtWidgets.QWizard.BackButton,
QtWidgets.QWizard.NextButton,
QtWidgets.QWizard.FinishButton
]
self.setButtonLayout(buttons)
self.addPage(FirstPage())
self.addPage(SecondPage())
class FirstPage(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(FirstPage, self).__init__(parent)
self.setTitle("First page")
class SecondPage(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(SecondPage, self).__init__(parent)
self.setTitle("Second page")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())

Related

How to select row on cursor move?

I want to imitate list walking with arrow keys, I mean to select row when cursor goes up or down with arrow keys, like it happens when I click right-mouse-button on cell in this code, but keyPressEvent does not fires when I use arrows keys.
import sys
from PySide6 import QtCore
from PySide6.QtGui import QKeyEvent
from PySide6.QtWidgets import QMainWindow, QApplication, QTableWidgetItem, QTableWidget, QVBoxLayout, QWidget
from loguru import logger
class TableWindow(QMainWindow):
def __init__(self, data, parent=None):
super(TableWindow, self).__init__(parent)
self.table_widget = QTableWidget()
self.table_widget.cellClicked.connect(self.clicked)
self.populate_cells(data)
layout = QVBoxLayout()
layout.addWidget(self.table_widget)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def keyPressEvent(self, event) -> None:
logger.info(f"{event.key()=}")
def clicked(self, row, col):
logger.info(f"{row}, {col}")
self.table_widget.selectRow(row)
def populate_cells(self, data):
self.table_widget.setRowCount(len(data))
self.table_widget.setColumnCount(len(data[0]))
for row in range(len(data)):
for col in range(len(data[0])):
value = data[row][col]
self.table_widget.setItem(row, col, QTableWidgetItem(value))
if __name__ == "__main__":
data_sample = [
["customer1", "address1"],
["customer2", "address2"]
]
app = QApplication(sys.argv)
window = TableWindow(data_sample)
window.show()
sys.exit(app.exec())
I think the issue is that you are listening for keyPressEvents on the main window when you want to be listening for them on the QTableWidget.
What you can do is subclass QTableWidget and reimplement the keyPressEvent method.
For Example:
class TableWidget(QTableWidget):
def keyPressEvent(self, event):
logger.info(f"{event.key()=}")
super().keyPressEvent(event)
class TableWindow(QMainWindow):
def __init__(self, data, parent=None):
super(TableWindow, self).__init__(parent)
self.table_widget = TableWidget()
self.table_widget.cellClicked.connect(self.clicked)
self.populate_cells(data)
layout = QVBoxLayout()
layout.addWidget(self.table_widget)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def clicked(self, row, col):
logger.info(f"{row}, {col}")
self.table_widget.selectRow(row)
def populate_cells(self, data):
self.table_widget.setRowCount(len(data))
self.table_widget.setColumnCount(len(data[0]))
for row in range(len(data)):
for col in range(len(data[0])):
value = data[row][col]
self.table_widget.setItem(row, col, QTableWidgetItem(value))
if __name__ == "__main__":
data_sample = [
["customer1", "address1"],
["customer2", "address2"]
]
app = QApplication(sys.argv)
window = TableWindow(data_sample)
window.show()
sys.exit(app.exec())

pyqt signal from a window not changing the main window widgets

I can pass values between two windows using signal,
and I want the plain text change, while I press a button from another window.
but the insertPlainText and even text.show() are not working
I've tried sending the signal to the init part of the Mainwindow,
tried update, repaint, but none of them works.
appreciate for any of your help, thanks!
the search_send method in class SearchWindow to MainWindow method test_
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.tag_box.hide()
def test_(self, i): # -------problem here------------------
print(i) // <- value here were right
self.ui.tag_box.insertPlainText(i) # -------Plain Text does not change-------
self.ui.tag_box.show()# -------Plain Text does not show either--------------
class SearchWindow(QtWidgets.QMainWindow):
signal=pyqtSignal(str)
def __init__(self, endpoint=None, user=None, password=None, points_link=None):
super(SearchWindow, self).__init__()
self.ui = Ui_Search()
self.ui.setupUi(self)
self.ui.pushButton_2.clicked.connect(self.search_send)
def search_send(self): # -------problem here------------------
tag_list = [tag1,tag2, tag3]
otherClass = MainWindow()
self.signal.connect(otherClass.test_)
for k in tag_list:
self.signal.emit(k)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec_())
As we're unclear what Ui_Dialog contains, I can't tell what's wrong exactly, but can show you how Signal should be used.
This is example of Signal that's transferring text to Slot. Name may differ in PyQt5 - change accordingly.
from PySide2.QtWidgets import QWidget, QApplication, QPlainTextEdit, QTextEdit, QVBoxLayout
from PySide2.QtCore import Signal, Slot
import sys
class MainWindow(QWidget):
sig = Signal(str)
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("Main")
self.layout = QVBoxLayout()
self.text = QTextEdit()
self.layout.addWidget(self.text)
self.setLayout(self.layout)
self.text.textChanged.connect(self.onChange)
def onChange(self):
self.sig.emit(self.text.toPlainText())
class SubWindow(QWidget):
def __init__(self, connect_target: MainWindow):
super(SubWindow, self).__init__()
self.setWindowTitle("Sub")
self.layout = QVBoxLayout()
self.text = QPlainTextEdit()
self.layout.addWidget(self.text)
self.setLayout(self.layout)
connect_target.sig.connect(self.onSignal)
#Slot(str)
def onSignal(self, text):
self.text.insertPlainText(text + '\r\n')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
window_sub = SubWindow(window)
window_sub.show()
sys.exit(app.exec_())
Anything inside Main will be inserted to QPlainTextEdit as you wanted.

Back to Previous Window

i'm new in Python and PyQt4.
I want to ask "How to back to previous Window ?"
i have 2 file in here, file 'login' and 'signup'
here file login.py
import sys
from PyQt4.QtGui import QWidget, QPushButton, QLineEdit, QLabel, \
QApplication, QGridLayout
from signup import SignUp
class Login(QWidget):
def __init__(self):
super(Login, self).__init__()
self.setWindowTitle("Login")
self.login_window()
def login_window(self):
self.login_layout = QGridLayout()
self.login_button = QPushButton("Login")
self.signup_button = QPushButton("Sign Up")
self.login_layout.addWidget(self.login_button, 2, 0)
self.login_layout.addWidget(self.signup_button, 2, 1)
self.signup_button.clicked.connect(self.signup_show)
self.setLayout(self.login_layout)
self.show()
def signup_show(self):
self.signupshow = SignUp()
self.hide()
self.signupshow.show()
def check_signup(self):
SignUp.check_signup()
self.show()
def main():
app = QApplication(sys.argv)
login = Login()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
and here is signup.py
import sys
from PyQt4.QtGui import QWidget, QPushButton, QLineEdit, QLabel, \
QApplication, QGridLayout
class SignUp(QWidget):
def __init__(self):
super(SignUp, self).__init__()
self.setWindowTitle("Sign Up")
self.signup_window()
def signup_window(self):
self.signup_layout = QGridLayout()
self.signup_button = QPushButton("Sign Up")
self.signup_layout.addWidget(self.signup_button, 2, 0, 1, 0)
self.signup_button.clicked.connect(self.check_signup)
self.setLayout(self.signup_layout)
self.show()
def check_signup(self):
self.close()
def main():
app = QApplication(sys.argv)
signup = SignUp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
my problem is, when i push button signup from signup.py, it's close but window Login is not show.
i think i don't have any trigger in signup.py for check_signup in login.py
in this code, i delete some Line Edit and Label. I think it's not a problem.
i hope someone will help, Thank you before :)
and if you answer this questtion, i hope you will explain a little bit of the logic, thank you again :)
I'm not really sure what you are trying to achieve (your description is kind of confusing), but let's start here. I put all your code in one file, since you need to import each other (which leads to cyclic import). Then, I just added these two lines
self.login = Login()
self.login.show()
into check_signup method of SignUp class, which pops-up Login window. If this is not your desired result, please let us know and provide us a better description. The code follows:
import sys
from PyQt4.QtGui import QWidget, QPushButton, QLineEdit, QLabel, \
QApplication, QGridLayout
class Login(QWidget):
def __init__(self):
super(Login, self).__init__()
self.setWindowTitle("Login")
self.login_window()
def login_window(self):
self.login_layout = QGridLayout()
self.login_button = QPushButton("Login")
self.signup_button = QPushButton("Sign Up")
self.login_layout.addWidget(self.login_button, 2, 0)
self.login_layout.addWidget(self.signup_button, 2, 1)
self.signup_button.clicked.connect(self.signup_show)
self.setLayout(self.login_layout)
self.show()
def signup_show(self):
self.signupshow = SignUp()
self.hide()
self.signupshow.show()
def check_signup(self):
SignUp.check_signup()
self.show()
class SignUp(QWidget):
def __init__(self):
super(SignUp, self).__init__()
self.setWindowTitle("Sign Up")
self.signup_window()
def signup_window(self):
self.signup_layout = QGridLayout()
self.signup_button = QPushButton("Sign Up")
self.signup_layout.addWidget(self.signup_button, 2, 0, 1, 0)
self.signup_button.clicked.connect(self.check_signup)
self.setLayout(self.signup_layout)
self.show()
def check_signup(self):
self.login = Login()
self.login.show()
self.close()
def main():
app = QApplication(sys.argv)
signup = SignUp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You should rather use signal-slot connections:
import sys
from PyQt4 import QtGui, QtCore
class WidgetA(QtGui.QWidget):
open_b = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(WidgetA, self).__init__(parent)
self.resize(100, 100)
self.l = QtGui.QVBoxLayout()
self.close_btn = QtGui.QPushButton('Close')
self.b_btn = QtGui.QPushButton('Open B')
self.b_btn.clicked.connect(self.b_btn_clicked)
self.l.addWidget(self.close_btn)
self.l.addWidget(self.b_btn)
self.setLayout(self.l)
def b_btn_clicked(self):
self.open_b.emit()
self.hide()
class WidgetB(QtGui.QWidget):
open_a = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(WidgetB, self).__init__(parent)
self.resize(100, 100)
self.l = QtGui.QVBoxLayout()
self.close_btn = QtGui.QPushButton('Close')
self.close_btn.clicked.connect(self.hide)
self.a_btn = QtGui.QPushButton('Open A')
self.a_btn.clicked.connect(self.a_btn_clicked)
self.l.addWidget(self.close_btn)
self.l.addWidget(self.a_btn)
self.setLayout(self.l)
def a_btn_clicked(self):
self.open_a.emit()
self.hide()
def main():
app = QtGui.QApplication(sys.argv)
a = WidgetA()
b = WidgetB()
a.open_b.connect(lambda: b.show())
b.open_a.connect(lambda: a.show())
a.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

pyqt4 Main window crashes after second window closes

I've got this simple program. Its has a button and when you press it a second window will come up.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
import urllib.request
class second_window(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self)
super(second_window, self).__init__(parent)
layout = QVBoxLayout()
button = QPushButton("close")
button.clicked.connect(self.button_clicked)
layout.addWidget(button)
self.setLayout(layout)
def button_clicked(self):
self.close()
class Main_window(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
button = QPushButton("Second Window")
self.sec_window = second_window(self)
layout.addWidget(button)
button.clicked.connect(self.button_clicked)
self.setLayout(layout)
def button_clicked(self):
self.sec_window.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
dl = window()
dl.show()
app.exec()
But sometimes if you close the Main_window just after you close the second_window it crashes and I get a message saying "Python.exe has stopped working".
can anyone help?
First Solution: Change self.sec_window = second_window(self) to self.sec_window = second_window() since it will not be necessary to clean because there is no relationship between second_window and Main_window. As the following code shows
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class second_window(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self)
super(second_window, self).__init__(parent)
layout = QVBoxLayout()
button = QPushButton("close")
button.clicked.connect(self.button_clicked)
layout.addWidget(button)
self.setLayout(layout)
def button_clicked(self):
self.close()
class Main_window(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
button = QPushButton("Second Window")
self.sec_window = second_window()
layout.addWidget(button)
button.clicked.connect(self.button_clicked)
self.setLayout(layout)
def button_clicked(self):
self.sec_window.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
dl = Main_window()
dl.show()
app.exec()
Second Solution: Add self.sec_window.deleteLater() on closeEvent(self, event) to be able to delete second_window
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class second_window(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self)
super(second_window, self).__init__(parent)
layout = QVBoxLayout()
button = QPushButton("close")
button.clicked.connect(self.button_clicked)
layout.addWidget(button)
self.setLayout(layout)
def button_clicked(self):
self.close()
class Main_window(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
button = QPushButton("Second Window")
self.sec_window = second_window(self)
layout.addWidget(button)
button.clicked.connect(self.button_clicked)
self.setLayout(layout)
def button_clicked(self):
self.sec_window.exec_()
def closeEvent(self, event):
self.sec_window.deleteLater()
super().closeEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
dl = Main_window()
dl.show()
app.exec()

QWidget cannot display on QMainWindow instance PyQt5

I am learning PyQt5 now and tried to do something little on my own. I have made a very basic custom toolbox, which has just 6 QPushButtons buttons on it, which inherits from QWidget class.
My problem is that I can't display my toolbox on my QMainWidow instance. Let me show you what I did;
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ToolBox(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
btn = [QPushButton('B', self) for i in range(6)]
for Btn in btn:
Btn.resize(30, 30)
self.resize(60, 90)
k = 0
for i in range(6):
btn[i].move((i%2)*30, k*30)
k += 1 if i % 2 == 1 else 0
self.show()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(300, 200)
self.statusBar().showMessage('Ready!')
exitAction = QAction(QIcon('idea.png'), 'Exit', self)
exitAction.setStatusTip('Exit application')
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(qApp.quit)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('File')
fileMenu.addAction(exitAction)
t = ToolBox()
t.move(150, 150)
t.show() #With and without this line, it doesn't work.
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
m = MainWindow()
sys.exit(app.exec_())
You just need to position your widget somewhere in the QMainWindow canvas. All you have to do is position it in the MainWindow. Just for an example, I use setCentralWidget() to position your QWidget.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ToolBox(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
btn = [QPushButton('B', self) for i in range(6)]
for Btn in btn:
Btn.resize(30, 30)
self.resize(60, 90)
k = 0
for i in range(6):
btn[i].move((i%2)*30, k*30)
k += 1 if i % 2 == 1 else 0
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(300, 200)
self.statusBar().showMessage('Ready!')
exitAction = QAction(QIcon('idea.png'), 'Exit', self)
exitAction.setStatusTip('Exit application')
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(qApp.quit)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('File')
fileMenu.addAction(exitAction)
t = ToolBox()
self.setCentralWidget(t)
if __name__ == '__main__':
app = QApplication(sys.argv)
m = MainWindow()
m.show()
sys.exit(app.exec_())

Resources