When I run the following script, named test2.py, if I click on Ok or Cancel button the window closes but I don't get the prompt. I think I forgot something stupid ...
This is the test2.py script:
from PyQt5 import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
import sys
import unittest
class MainAppWindow(QMainWindow):
def __init__(self):
super().__init__()
dialog = Qt.QDialog()
buttonbox = Qt.QDialogButtonBox(Qt.QDialogButtonBox.Ok | Qt.QDialogButtonBox.Cancel)
layout = Qt.QVBoxLayout()
param_box = Qt.QGroupBox('Iterate over parameters:')
pblayout = Qt.QVBoxLayout()
pblayout.setContentsMargins(0, 0, 0, 0)
scroll = Qt.QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameStyle(scroll.NoFrame)
scroll.setViewportMargins(0, 0, 0, 0)
pblayout.addWidget(scroll)
param_lay = Qt.QGridLayout()
wid = Qt.QWidget()
scroll.setWidget(wid)
wid.setLayout(param_lay)
param_box.setLayout(pblayout)
layout.addWidget(param_box)
layout.addWidget(buttonbox)
dialog.setLayout(layout)
buttonbox.accepted.connect(dialog.accept)
buttonbox.rejected.connect(dialog.reject)
param_lay.addWidget(Qt.QLabel('iter. / database:'), 0, 0, 1, 3)
param_lay.addWidget(Qt.QLabel('iter.:'), 0, 3, 1, 2)
param_lay.setColumnStretch(2, 1)
param_lay.setColumnStretch(4, 1)
param_lay.setRowStretch(0, 0)
res = dialog.exec_()
if res != dialog.Accepted:
print('dialog not accepted')
else:
print('dialog accepted')
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainAppWindow()
sys.exit(app.exec_())
When I run the script with python3 test2.py, I observe in stdout the dialog accepted or dialog not accepted depending on the button clicked, the window closes but I don't get the prompt. I have to stop manually (CtrLZ) to get the prompt.
The problem is that you have 2 eventloops: dialog.exec_() and app.exec_() so when the first eventloop finishes the other eventloop starts. On the other hand, it is useless to create a class that inherits from MainAppWindow since the window is never used or shown.
One possible solution is to start only one eventloop:
import sys
from PyQt5.QtWidgets import (
QApplication,
QDialog,
QDialogButtonBox,
QGridLayout,
QGroupBox,
QLabel,
QMainWindow,
QScrollArea,
QVBoxLayout,
QWidget,
)
def main():
dialog = QDialog()
buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
layout = QVBoxLayout(dialog)
param_box = QGroupBox("Iterate over parameters:")
pblayout = QVBoxLayout()
pblayout.setContentsMargins(0, 0, 0, 0)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameStyle(scroll.NoFrame)
scroll.setViewportMargins(0, 0, 0, 0)
pblayout.addWidget(scroll)
param_lay = QGridLayout()
wid = QWidget()
scroll.setWidget(wid)
wid.setLayout(param_lay)
param_box.setLayout(pblayout)
layout.addWidget(param_box)
layout.addWidget(buttonbox)
buttonbox.accepted.connect(dialog.accept)
buttonbox.rejected.connect(dialog.reject)
param_lay.addWidget(QLabel("iter. / database:"), 0, 0, 1, 3)
param_lay.addWidget(QLabel("iter.:"), 0, 3, 1, 2)
param_lay.setColumnStretch(2, 1)
param_lay.setColumnStretch(4, 1)
param_lay.setRowStretch(0, 0)
res = dialog.exec_()
if res != dialog.Accepted:
print("dialog not accepted")
else:
print("dialog accepted")
if __name__ == "__main__":
app = QApplication(sys.argv)
main()
Related
I'm trying to make a GUI for my program and faced a problem trying to access text from a clicked QPushButton.
The problem is, when I use for-loop to iterate through a list of buttons button.clicked.connect(function) processes every click as a click on the last item (at least, I think so).
Here's sample code
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QHBoxLayout
import sys
def main():
app = QApplication(sys.argv)
# main window
win = QMainWindow()
win.setGeometry(100, 100, 460, 80)
win.setWindowTitle("My great app")
# layout
box = QWidget(win)
box.setGeometry(0, 0, 460, 80)
layout = QHBoxLayout(win)
box.setLayout(layout)
btns = []
# creating 10 buttons
for i in range(10):
btn = QPushButton(str(i+1), box)
btns.append(btn)
layout.addWidget(btns[i], i)
# accessing buttons text
for btn in btns:
btn.clicked.connect(lambda: print(btn.text()))
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
If you run the program and click on any button it will print "10" every time.
You should read here https://doc.qt.io/qt-5/qmainwindow.html#details and here https://doc.qt.io/qt-5/qabstractbutton.html#clicked
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, \
QPushButton, QHBoxLayout
def main():
app = QApplication(sys.argv)
# main window
win = QMainWindow()
win.setGeometry(100, 100, 460, 80)
win.setWindowTitle("My great app")
# layout
box = QWidget() #(win)
box.setGeometry(0, 0, 460, 80)
layout = QHBoxLayout(box) #(win)
# box.setLayout(layout) # ---
win.setCentralWidget(box) # +++
btns = []
# creating 10 buttons
for i in range(10):
btn = QPushButton(str(i+1)) #, box)
btns.append(btn)
layout.addWidget(btns[i], i)
# accessing buttons text
# btn.clicked.connect(lambda: print(btn.text())) # ---
btn.clicked.connect(lambda ch, text=btn.text(): print(text)) # +++
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I'm making a GUI in PyQt5 where there are multiple tabs. In one of the tabs, users select a radio button with what type of data they are entering.
This information changes what buttons are available in other tabs and I'm having trouble getting this information passed from the original tab class. I'd like to see if a radio button is checked from another tab and if it is, enable a button.
I have a function to return a string depending on which radio button is selected, but when I call the getDataType() function from the other tab, I get the error "AttributeError: module 'DataTab' has no attribute 'getDataType.'"
I've tried moving the getDataType() function outside of the DataTab class, but since the radio buttons use "self," I'm not able to access them.
I've also tried making a new class and calling that new class from the function, but then I get stuck on how to get the information to my other tab.
So in this example, once a user selects a radio button in the Data Tab, I want the corresponding push button on the Analysis Tab enabled.
When I uncomment the call to getDataType(), I get the following error: "AttributeError: module 'DataTab' has no attribute 'getDataType'"
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem,
QLineEdit, QFileDialog, QRadioButton, QGroupBox, QPushButton,
QGridLayout, QButtonGroup, QApplication, QAbstractItemView,
QTabWidget)
from PyQt5.QtGui import QIcon
import sys, os
class TabPage(QTabWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('font: 15pt Tw Cen MT')
self.show()
# Creating the tabs here to have a reference
self.tabWidget = QTabWidget()
self.tabWidget.addTab(DataTab(), "Data Input")
self.tabWidget.addTab(AnalysisTab(), "Analysis")
self.layout = QVBoxLayout()
self.layout.addWidget(self.tabWidget)
self.setLayout(self.layout)
# The DataTab class holds all the GUI for the DataTab
class DataTab(QWidget):
def __init__(self):
super().__init__()
self.app = QApplication(sys.argv)
self.layout = QGridLayout()
self.intervalRadioButton = QRadioButton("Interval")
self.ordinalRadioButton = QRadioButton("Ordinal")
self.frequencyRadioButton = QRadioButton("Frequency")
self.submitButton = QPushButton("Submit Data")
self.layout.addWidget(self.intervalRadioButton, 7, 0, 1, 3)
self.layout.addWidget(self.ordinalRadioButton, 8, 0, 1, 3)
self.layout.addWidget(self.frequencyRadioButton, 9, 0, 1, 3)
self.setLayout(self.layout)
self.show()
def getDataType(self):
if self.intervalRadioButton.isChecked():
return "interval"
elif self.ordinalRadioButton.isChecked():
return "ordinal"
elif self.frequencyRadioButton.isChecked():
return "frequency"
class AnalysisTab(QWidget):
def __init__(self):
super().__init__()
self.app = QApplication(sys.argv)
self.createChooseIntervalGroup()
self.createChooseOrdinalGroup()
self.createChooseFrequencyGroup()
self.layout = QGridLayout()
self.layout.addWidget(self.ChooseIntervalGroup, 0, 1)
self.layout.addWidget(self.ChooseOrdinalGroup, 1, 1)
self.layout.addWidget(self.ChooseFrequencyGroup, 2, 1)
self.setLayout(self.layout)
self.show()
# The right side of AnalysisTab containing the buttons for
# analysis
def createChooseIntervalGroup(self):
self.ChooseIntervalGroup = QGroupBox("Tests for Interval Data")
self.analyzeIntervalButton = QPushButton("Analyze")
self.analyzeIntervalButton.setEnabled(False)
# if DataTab.getDataType() != "interval":
# self.analyzeIntervalButton.setEnabled(True)
self.layout = QGridLayout()
self.layout.addWidget(self.analyzeIntervalButton, 1, 1)
self.ChooseIntervalGroup.setLayout(self.layout)
# The right side of AnalysisTab containing the buttons for
# analysis
def createChooseOrdinalGroup(self):
self.ChooseOrdinalGroup = QGroupBox("Tests for Ordinal Data")
self.analyzeOrdinalButton = QPushButton("Analyze")
self.analyzeOrdinalButton.setEnabled(False)
# if DataTab.getDataType() != "ordinal":
# self.analyzeIntervalButton.setEnabled(True)
self.layout = QGridLayout()
self.layout.addWidget(self.analyzeOrdinalButton, 1, 1)
self.ChooseOrdinalGroup.setLayout(self.layout)
# The right side of AnalysisTab containing the buttons for
# analysis
def createChooseFrequencyGroup(self):
self.ChooseFrequencyGroup = QGroupBox("Tests for Frequency Data")
self.analyzeFrequencyButton = QPushButton("Analyze")
self.analyzeFrequencyButton.setEnabled(False)
# if DataTab.getDataType() != "frequency":
# self.analyzeIntervalButton.setEnabled(True)
self.layout = QGridLayout()
self.layout.addWidget(self.analyzeFrequencyButton, 1, 1)
self.ChooseFrequencyGroup.setLayout(self.layout)
def run():
app = QApplication(sys.argv)
tabPage = TabPage()
tabPage.show()
app.exec_()
run()
A possible solution for this case is to use the signals to notify the change of status of the QRadioButton to enable or disable the button.
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QWidget,
QVBoxLayout,
QRadioButton,
QGroupBox,
QPushButton,
QGridLayout,
QButtonGroup,
QApplication,
QTabWidget,
)
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setStyleSheet("font: 15pt Tw Cen MT")
self.data_tab = DataTab()
self.analysis_tab = AnalysisTab()
self.tabWidget = QTabWidget()
self.tabWidget.addTab(self.data_tab, "Data Input")
self.tabWidget.addTab(self.analysis_tab, "Analysis")
layout = QVBoxLayout(self)
layout.addWidget(self.tabWidget)
self.data_tab.intervalRadioButton.toggled.connect(
self.analysis_tab.analyzeIntervalButton.setEnabled
)
self.data_tab.ordinalRadioButton.toggled.connect(
self.analysis_tab.analyzeOrdinalButton.setEnabled
)
self.data_tab.frequencyRadioButton.toggled.connect(
self.analysis_tab.analyzeFrequencyButton.setEnabled
)
class DataTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QGridLayout(self)
self.intervalRadioButton = QRadioButton("Interval")
self.ordinalRadioButton = QRadioButton("Ordinal")
self.frequencyRadioButton = QRadioButton("Frequency")
self.submitButton = QPushButton("Submit Data")
layout.addWidget(self.intervalRadioButton, 7, 0, 1, 3)
layout.addWidget(self.ordinalRadioButton, 8, 0, 1, 3)
layout.addWidget(self.frequencyRadioButton, 9, 0, 1, 3)
class AnalysisTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.createChooseIntervalGroup()
self.createChooseOrdinalGroup()
self.createChooseFrequencyGroup()
layout = QGridLayout(self)
layout.addWidget(self.ChooseIntervalGroup, 0, 1)
layout.addWidget(self.ChooseOrdinalGroup, 1, 1)
layout.addWidget(self.ChooseFrequencyGroup, 2, 1)
def createChooseIntervalGroup(self):
self.ChooseIntervalGroup = QGroupBox("Tests for Interval Data")
self.analyzeIntervalButton = QPushButton("Analyze")
self.analyzeIntervalButton.setEnabled(False)
layout = QGridLayout(self.ChooseIntervalGroup)
layout.addWidget(self.analyzeIntervalButton, 1, 1)
def createChooseOrdinalGroup(self):
self.ChooseOrdinalGroup = QGroupBox("Tests for Ordinal Data")
self.analyzeOrdinalButton = QPushButton("Analyze")
self.analyzeOrdinalButton.setEnabled(False)
layout = QGridLayout(self.ChooseOrdinalGroup)
layout.addWidget(self.analyzeOrdinalButton, 1, 1)
def createChooseFrequencyGroup(self):
self.ChooseFrequencyGroup = QGroupBox("Tests for Frequency Data")
self.analyzeFrequencyButton = QPushButton("Analyze")
self.analyzeFrequencyButton.setEnabled(False)
layout = QGridLayout(self.ChooseFrequencyGroup)
layout.addWidget(self.analyzeFrequencyButton, 1, 1)
def run():
app = QApplication(sys.argv)
tabPage = Widget()
tabPage.show()
app.exec_()
run()
I'm trying to put a QProgressBar below a QPushButton and align them on the center of a QVBoxLayout, but for some reason the button stays left aligned when the progress bar is present and center aligned if it is not.
I tried setting the alignment of all parent widgets and layouts to Qt.AlignCenter, but the progress bar keeps making the button go to the left.
connect_box = QVBoxLayout()
connect_box.setAlignment(Qt.AlignCenter)
connect_button = QPushButton('Connect')
connect_button.setFixedSize(120, 30)
connect_progress = QProgressBar()
connect_progress.setRange(0, 10000)
connect_progress.setValue(0)
connect_box.addWidget(connect_button)
connect_box.addWidget(connect_progress)
connect_box.setContentsMargins(0, 20, 0, 0)
I expect the button to stay center aligned when the progress bar is added.
Try it:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class MyWidget(QWidget):
def __init__(self):
super().__init__()
connect_button = QPushButton('Connect')
connect_button.setFixedSize(120, 30)
connect_progress = QProgressBar()
connect_progress.setRange(0, 10000)
connect_progress.setValue(0)
connect_box = QVBoxLayout(self)
connect_box.setAlignment(Qt.AlignCenter)
connect_box.addWidget(connect_button, alignment=Qt.AlignCenter) # < ----
connect_box.addWidget(connect_progress)
connect_box.setContentsMargins(0, 20, 0, 0)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())
I am trying to create a program where the user has three options to answer a question. I used QGridLayout for this but only the last widget added to the QGridLayout is shown.
Expected:
################################
# Text #
################################
########## ########## ##########
#Button 1# #Button 2# #Button 3#
########## ########## ##########
Reality:
##########
#Button 3#
##########
Simplified Python 3 Code (No Text Widget):
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
QApp = QApplication(sys.argv)
QMWRoot = QMainWindow()
QGLRoot = QGridLayout(QMWRoot)
QMWRoot.setLayout(QGLRoot)
QPB0 = QPushButton('B0x0', QMWRoot)
QGLRoot.addWidget(QPB0, 0, 0)
QPB1 = QPushButton('B0x1', QMWRoot)
QGLRoot.addWidget(QPB1, 0, 1)
QPB2 = QPushButton('B1x0', QMWRoot)
QGLRoot.addWidget(QPB2, 1, 0)
QMWRoot.show()
sys.exit(QApp.exec_())
Original (Unfinished) Python 3 Code:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Root(QMainWindow):
def __init__(self, QApp):
super().__init__()
self.QApp = QApp
def setupUi(self):
self.setWindowTitle('')
self.QGLRoot = QGridLayout()
self.setLayout(self.QGLRoot)
self.QLBTool = QLabel()
self.QLBTool.setText('Tool')
self.QGLRoot.addWidget(self.QLBTool, 0, 0)
self.QPB0 = QPushButton(self)
self.QPB0.setText('0')
self.QGLRoot.addWidget(self.QPB0, 1, 0)
self.QPB1 = QPushButton(self)
self.QPB1.setText('1')
self.QGLRoot.addWidget(self.QPB1, 1, 1)
self.QPB2 = QPushButton(self)
self.QPB2.setText('2')
self.QGLRoot.addWidget(self.QPB2, 1, 2)
def startUi(self):
self.show()
def updateUi(self):
pass
QApp = QApplication(sys.argv)
App = Root(QApp)
App.setupUi()
App.startUi()
sys.exit(QApp.exec_())
QMainWindow you have a special structure:
So in your case you must establish a centralwidget and there place the layout.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Root(QMainWindow):
def __init__(self, QApp):
super().__init__()
self.QApp = QApp
def setupUi(self):
self.setWindowTitle('')
widget = QWidget()
self.setCentralWidget(widget)
self.QGLRoot = QGridLayout()
widget.setLayout(self.QGLRoot)
self.QLBTool = QLabel()
self.QLBTool.setAlignment(Qt.AlignHCenter)
self.QLBTool.setText('Tool')
self.QGLRoot.addWidget(self.QLBTool, 0, 0, 1, 3)
self.QPB0 = QPushButton()
self.QPB0.setText('0')
self.QGLRoot.addWidget(self.QPB0, 1, 0)
self.QPB1 = QPushButton()
self.QPB1.setText('1')
self.QGLRoot.addWidget(self.QPB1, 1, 1)
self.QPB2 = QPushButton()
self.QPB2.setText('2')
self.QGLRoot.addWidget(self.QPB2, 1, 2)
def startUi(self):
self.show()
QApp = QApplication(sys.argv)
App = Root(QApp)
App.setupUi()
App.startUi()
sys.exit(QApp.exec_())
Compact and generalized code:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, \
QGridLayout, QLabel, QPushButton
class Root(QMainWindow):
def __init__(self, QApp):
super().__init__()
self.setupUi()
def setupUi(self):
self.setWindowTitle('')
widget = QWidget()
self.setCentralWidget(widget)
QGLRoot = QGridLayout(widget)
options = ("0", "1", "2")
QLBTool = QLabel('Tool')
QLBTool.setAlignment(Qt.AlignHCenter)
QGLRoot.addWidget(QLBTool, 0, 0, 1, len(options))
for i, option in enumerate(options):
button = QPushButton(option)
QGLRoot.addWidget(button, 1, i)
QApp = QApplication(sys.argv)
App = Root(QApp)
App.show()
sys.exit(QApp.exec_())
I am trying to make work a progress bar with pyqt5 however when I open the Window of the progress bar I am getting a blank windows.
The code for the progress bar generated from the ui is:
Ui_dlg_progress generated from the ui:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_dlg_progress(object):
def setupUi(self, dlg_progress):
dlg_progress.setObjectName("dlg_progress")
dlg_progress.resize(376, 91)
dlg_progress.setSizeGripEnabled(True)
self.gridLayout = QtWidgets.QGridLayout(dlg_progress)
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(dlg_progress)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.label_num_folders = QtWidgets.QLabel(dlg_progress)
self.label_num_folders.setObjectName("label_num_folders")
self.gridLayout.addWidget(self.label_num_folders, 0, 1, 1, 1)
self.label_text = QtWidgets.QLabel(dlg_progress)
self.label_text.setObjectName("label_text")
self.gridLayout.addWidget(self.label_text, 0, 2, 1, 1)
self.label_total_folders = QtWidgets.QLabel(dlg_progress)
self.label_total_folders.setObjectName("label_total_folders")
self.gridLayout.addWidget(self.label_total_folders, 0, 3, 1, 1)
self.progressBar = QtWidgets.QProgressBar(dlg_progress)
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 4)
self.retranslateUi(dlg_progress)
QtCore.QMetaObject.connectSlotsByName(dlg_progress)
def retranslateUi(self, dlg_progress):
_translate = QtCore.QCoreApplication.translate
dlg_progress.setWindowTitle(_translate("dlg_progress", "Processing"))
self.label.setText(_translate("dlg_progress", "Processing:"))
self.label_num_folders.setText(_translate("dlg_progress", "0"))
self.label_text.setText(_translate("dlg_progress", "folders of a total of: "))
self.label_total_folders.setText(_translate("dlg_progress", "1"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
dlg_progress = QtWidgets.QDialog()
ui = Ui_dlg_progress()
ui.setupUi(dlg_progress)
dlg_progress.show()
ProgressBar.py - Calls Ui_dlg_progress
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QDialog
from Ui_progress_bar import Ui_dlg_progress
class dlg_progress(QDialog, Ui_dlg_progress):
"""
Class documentation goes here.
"""
def __init__(self, parent=None):
"""
Constructor
#param parent reference to the parent widget
#type QWidget
"""
super(dlg_progress, self).__init__(parent)
self.setupUi(self)
self.show()
And the main code where is called the progress bar:
def main():
subdirs = [f for f in source.iterdir() if f.is_dir()]
if len(subdirs) == 0:
return -1
prog = dlg_progress()
#Actual number of folders procecessed
proc_folders = 0
prog.progressBar.setValue(proc_folders)
prog.label_num_folders.setText(str(proc_folders))
prog.label_total_folders.setText(str(len(subdirs)))
prog.progressBar.maximum = len(subdirs)
for subdir in subdirs:
process of the folders
#Number of processed folders
proc_folders += 1
prog.setValue(proc_folders)
prog.label_num_folders.setText(str(proc_folders))
QApplication.processEvents()
return 0
It just looks like a typo in the last loop :
for subdir in subdirs:
#Number of processed folders
proc_folders += 1
prog.progressBar.setValue(proc_folders)
prog.label_num_folders.setText(str(proc_folders))
QApplication.processEvents()
Otherwise, you should test the source and the length of generated list, if it's empty, the loop won't start, and the progressbar will stay at 0.