I've created a wizard with Pyside.
On one page, I create a new thread, which starts an installer.
When the installer is ready, the Wizard should go forward automatically, without clicking the next button.
I've read the pyside documentation, and now my understanding is, that QWizard has a next function. But how can I use this function?
My test is working fine:
from PySide.QtGui import *
from PySide.QtCore import *
...
...
class Install(QWizardPage):
def __init__(self, parent=None):
super(Install, self).__init__(parent)
def initializePage(self):
self.setTitle("Install")
label = QLabel("Install")
label.setWordWrap(True)
layout = QVBoxLayout()
self.progressBar = QProgressBar(self)
self.progressBar.setRange(0,1)
self.progressBar.setRange(0,0)
layout.addWidget(self.progressBar)
layout.addWidget(label)
self.setLayout(layout)
self.myTask = TaskThread()
self.myTask.start()
self.myTask.taskFinished.connect(self.Finished)
def Finished(self):
print("finish")
def isComplete(self):
return False
class TaskThread(QThread):
taskFinished = Signal()
def run(self):
a = 0
while a != 10000:
print("test")
a += 1
self.taskFinished.emit()
And when I try to use the next function I try:
self.CallNext = QWizard().next
self.myTask.taskFinished.connect(self.CallNext)
And also:
self.myTask.taskFinished.connect(QWizard().next)
But this is not working
This connection should be done in the context where the QWizard and QWizardPage exist, but before that we must move the creation of the QThread to the constructor, for example in the following example I do in the main:
class Install(QWizardPage):
def __init__(self, parent=None):
super(Install, self).__init__(parent)
self.myTask = TaskThread()
def initializePage(self):
[...]
self.setLayout(layout)
self.myTask.start()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
wizard = QWizard()
install = Install()
install.setTitle("installer")
install.myTask.taskFinished.connect(wizard.next)
wizard.addPage(install)
page = QWizardPage()
page.setTitle("next Page")
wizard.addPage(page)
wizard.show()
sys.exit(wizard.exec_())
Related
I am a beginner with PyQt5 and I am having trouble to use QtCore.signal
I'd like to send a signal when I press my buttons and switch the current widget displayed.
I don't have any errors when I run my code but when I press the buttons nothing happen and I guess it is because I am doing something wrong with the QtCore.Signal
Here is my code :
from PySide2 import QtCore, QtWidgets
from ui_Page_accueil import Ui_MainWindow
from ui_NouvelleVerif import Ui_Dialog as Ui_NouvelleVerif
from ui_NouvelleVerifEssieux import Ui_Dialog as Ui_NouvelleVerifEssieux
import sys
class MainWindowUi(Ui_MainWindow):
to_NouvelleVerif = QtCore.Signal()
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
#self.pushButton.clicked.connect(self.pushbutton_handler1)
self.pushButton_2.clicked.connect(self.pushbutton_handler2)
#def pushbutton_handler1(self):
# self.to_MainWindow.emit()
def pushbutton_handler2(self):
self.to_NouvelleVerif.emit()
class NouvelleVerifUi(QtWidgets.QWidget, Ui_NouvelleVerif):
to_MainWindow = QtCore.Signal()
to_NouvelleVerifEssieux = QtCore.Signal()
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.pushButton.clicked.connect(self.pushbutton_handler1)
#self.pushButton_2.clicked.connect(self.pushbutton_handler2)
self.pushButton_3.clicked.connect(self.pushbutton_handler3)
def pushbutton_handler1(self):
self.to_MainWindow.emit()
#def pushbutton_handler2(self):
# self.switch_window.emit()
def pushbutton_handler3(self):
self.to_NouvelleVerifEssieux.emit()
class NouvelleVerifEssieuxUi(QtWidgets.QWidget, Ui_NouvelleVerifEssieux):
to_NouvelleVerif = QtCore.Signal()
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.pushButton.clicked.connect(self.pushbutton_handler1)
def pushbutton_handler1(self):
self.to_NouvelleVerif.emit()
class Controller :
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QStackedWidget()
MainWindow = MainWindowUi()
NouvelleVerif = NouvelleVerifUi()
NouvelleVerifEssieux = NouvelleVerifEssieuxUi()
def __init__(self):
self.widget.addWidget(self.MainWindow) # create an instance of the first page class and add it to stackedwidget
self.widget.addWidget(self.NouvelleVerif) # adding second page
self.widget.addWidget(self.NouvelleVerifEssieux)
self.widget.setCurrentWidget(self.MainWindow) # setting the page that you want to load when application starts up. you can also use setCurrentIndex(int)
def show_MainWindow(self):
self.NouvelleVerif = NouvelleVerifUi()
self.NouvelleVerif.to_MainWindow.connect(self.show_MainWindow)
self.widget.setCurrentWidget(self.MainWindow)
def show_NouvelleVerif(self):
self.MainWindow = MainWindowUi()
self.NouvelleVerifEssieux = NouvelleVerifEssieuxUi()
self.MainWindow.to_NouvelleVerif.connect(self.show_NouvelleVerif)
self.NouvelleVerifEssieux.to_NouvelleVerif.connect(self.show_NouvelleVerif)
self.widget.setCurrentWidget(self.NouvelleVerif)
def show_NouvelleVerifEssieux(self):
self.NouvelleVerif = NouvelleVerifUi()
self.NouvelleVerif.to_NouvelleVerifEssieux.connect(self.show_NouvelleVerifEssieux)
self.widget.setCurrentWidget(self.NouvelleVerifEssieux)
def main():
controller = Controller()
controller.widget.show()
sys.exit(controller.app.exec_())
if __name__ == '__main__':
main()
#musicamante I don't know why I though the QtCore.Signal could be call even if he was in a function which has not been called. I did what you said and I realise I made another mistake with the widgets.
In the controller class they are create and add to the QStackedWidget in the __init__, but I was creating new ones and trying to set them as CurrentWidget without adding them to the QStackedWidget.
#alexpdev I wanted to navigate through my three differents with pushButton UI this way :
Start with MainWindowUI
MainWindowUI pushButton_2.clicked --> set the current widget display to NouvelleVerifUI
NouvelleVerifUI pushButton_1.clicked --> set the current widget display back to MainWindowUI
NouvelleVerifUI pushButton_3.clicked --> set the current widget display to NouvelleVerifEssieuxUI
NouvelleVerifEssieuxUI pushButton_1.clicked --> set the current widget display back to NouvelleVerifUI
Now everything work I did what you said #musicamante and I also use the UI created at first.
class Controller :
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QStackedWidget()
MainWindow = MainWindowUi()
NouvelleVerif = NouvelleVerifUi()
NouvelleVerifEssieux = NouvelleVerifEssieuxUi()
def __init__(self):
self.widget.addWidget(self.MainWindow) # create an instance of the first page class and add it to stackedwidget
self.widget.addWidget(self.NouvelleVerif) # adding second page
self.widget.addWidget(self.NouvelleVerifEssieux)
self.widget.setCurrentWidget(self.MainWindow) # setting the page that you want to load when application starts up. you can also use setCurrentIndex(int)
self.NouvelleVerif.to_MainWindow.connect(self.show_MainWindow)
self.MainWindow.to_NouvelleVerif.connect(self.show_NouvelleVerif)
self.NouvelleVerifEssieux.to_NouvelleVerif.connect(self.show_NouvelleVerif)
self.NouvelleVerif.to_NouvelleVerifEssieux.connect(self.show_NouvelleVerifEssieux)
def show_MainWindow(self):
self.widget.setCurrentWidget(self.MainWindow)
def show_NouvelleVerif(self):
self.widget.setCurrentWidget(self.NouvelleVerif)
def show_NouvelleVerifEssieux(self):
self.widget.setCurrentWidget(self.NouvelleVerifEssieux)
Thank you all for your time
To this question I am referring to the answer from #eyllanesc in PyQt5: How to scroll text in QTextEdit automatically (animational effect)?
There #eyllanesc shows how to make the text auto scrolls smoothly using verticalScrollBar(). It works great.
For this question, I have added some extra lines, to use QThread to get the text.
What I want to achieve here: to let the QThread class 'communicate' with the AnimationTextEdit class, so that the scrolling time can be determined by the text-length. So that the programm stops, when the scrolling process ends.
I must say it is very very tricky task for me. I would like to show the programm flow first, as I imagined.
UPDATE: My code is as follows. It works, but...
Problem with the code: when the text stops to scroll, the time.sleep() still works. The App wait there till time.sleep() stops.
What I want to get: When the text stops to scroll, the time.sleep() runs to its end value.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
import time
import sqlite3
class AnimationTextEdit(QTextEdit):
# signal_HowLongIsTheText = pyqtSignal(int) # signal to tell the QThread, how long the text is
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.moveToLine)
# def sent_Info_to_Thread(self):
# self.obj_Thread = Worker()
# self.signal_HowLongIsTheText.connect(self.obj_Thread.getText_HowLongIsIt)
# self.signal_HowLongIsTheText.emit(self.textLength)
# self.signal_HowLongIsTheText.disconnect(self.obj_Thread.getText_HowLongIsIt)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
self.animation.setStartValue(0)
self.textLength = self.verticalScrollBar().maximum()
# self.sent_Info_to_Thread()
self.animation.setEndValue(self.textLength)
self.animation.setDuration(self.animation.endValue()*4)
self.animation.start()
#pyqtSlot(QVariant)
def moveToLine(self, i):
self.verticalScrollBar().setValue(i)
class Worker(QObject):
finished = pyqtSignal()
textSignal = pyqtSignal(str)
# #pyqtSlot(int)
# def getText_HowLongIsIt(self, textLength):
# self.textLength = textLength
#pyqtSlot()
def getText(self):
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
self.textSignal.emit(longText)
time.sleep(10)
# time.sleep(int(self.textLength / 100))
# My question is about the above line: time.sleep(self.textLength)
# Instead of giving a fixed sleep time value here,
# I want let the Worker Class know,
# how long it will take to scroll all the text to the end.
self.finished.emit()
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.initUI()
self.startThread()
def initUI(self):
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.btn.clicked.connect(self.txt.startAnimation)
def startThread(self):
self.obj = Worker()
self.thread = QThread()
self.obj.textSignal.connect(self.textUpdate)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.getText)
self.thread.finished.connect(app.exit)
self.thread.start()
def textUpdate(self, longText):
self.txt.append(longText)
self.txt.moveToLine(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Thanks for the help and hint. What have I done wrong?
Although in the animation the duration is established it is necessary to understand that this is not exact, this could vary for several reasons, so calculating using the sleep to wait for it to end in a certain time and just closing the application may fail.
If your main objective is that when the animation is finished the program execution is finished then you must use the finished QVariantAnimation signal to finish the execution of the thread, this signal is emited when it finishes executing.
class AnimationTextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.moveToLine)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
self.animation.setStartValue(0)
self.textLength = self.verticalScrollBar().maximum()
self.animation.setEndValue(self.textLength)
self.animation.setDuration(self.animation.endValue()*4)
self.animation.start()
#pyqtSlot(QVariant)
def moveToLine(self, i):
self.verticalScrollBar().setValue(i)
class Worker(QObject):
textSignal = pyqtSignal(str)
#pyqtSlot()
def getText(self):
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
self.textSignal.emit(longText)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.initUI()
self.startThread()
def initUI(self):
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.btn.clicked.connect(self.txt.startAnimation)
def startThread(self):
self.obj = Worker()
self.thread = QThread()
self.obj.textSignal.connect(self.textUpdate)
self.obj.moveToThread(self.thread)
self.txt.animation.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.getText)
self.thread.finished.connect(app.exit)
self.thread.start()
def textUpdate(self, longText):
self.txt.append(longText)
self.txt.moveToLine(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Why does my application crash when i run the function setup_controls() twice.
Am I missing a 'parent/self' somewhere that is critical in the design?
import sys
from PySide import QtGui, QtCore
class QCategoryButton(QtGui.QPushButton):
def __init__(self, Text, treeitem, parent=None):
super(QCategoryButton, self).__init__(Text, parent)
self.treeitem = treeitem
def mousePressEvent(self, event):
mouse_button = event.button()
if mouse_button == QtCore.Qt.LeftButton:
self.treeitem.setExpanded(not self.treeitem.isExpanded())
class Example(QtGui.QWidget):
def __init__(self,):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# formatting
self.resize(300, 300)
self.setWindowTitle("Example")
# widgets
self.ui_treeWidget = QtGui.QTreeWidget()
self.ui_treeWidget.setRootIsDecorated(False)
self.ui_treeWidget.setHeaderHidden(True)
self.ui_treeWidget.setIndentation(0)
self.setup_controls()
# self.setup_controls()
# layout
self.mainLayout = QtGui.QGridLayout(self)
self.mainLayout.addWidget(self.ui_treeWidget)
self.show()
def setup_controls(self):
# Add Category
pCategory = QtGui.QTreeWidgetItem()
self.ui_treeWidget.addTopLevelItem(pCategory)
self.ui_toggler = QCategoryButton('Settings', pCategory)
self.ui_treeWidget.setItemWidget(pCategory, 0, self.ui_toggler)
pFrame = QtGui.QFrame(self.ui_treeWidget)
pLayout = QtGui.QVBoxLayout(pFrame)
self.ui_ctrl = QtGui.QPushButton('Great')
self.ui_ctrlb = QtGui.QPushButton('Cool')
pLayout.addWidget(self.ui_ctrl)
pLayout.addWidget(self.ui_ctrlb)
pContainer = QtGui.QTreeWidgetItem()
pContainer.setDisabled(False)
pCategory.addChild(pContainer)
self.ui_treeWidget.setItemWidget(pContainer, 0, pFrame)
# Main
# ------------------------------------------------------------------------------
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
The setItemWidget method takes ownership of the widget that is passed to it. If you don't keep a reference it, it could get garbage-collected by Python. But of course Qt knows nothing about Python, so when it subsequently tries to access the widget that is no longer there ... boom!
This is the problematic line:
self.ui_toggler = QCategoryButton('Settings', pCategory)
On the second call, the previous widget stored in self.ui_toggler will get deleted, because there is no other reference held for it (on the Python side). So instead you should do this:
ui_toggler = QCategoryButton('Settings', pCategory, self)
self.ui_treeWidget.setItemWidget(pCategory, 0, ui_toggler)
I am new to pyqt.I am trying to invoke a child GUI when a button is clicked in the parent GUI. In this process, parent GUI has to wait for the child GUI to be closed by the user after selecting some inputs. But this is not happening, Parent GUI does execute the next lines after which the child GUI has been invoked. Below is the code where I am passing an argument to child GUI from parent GUI. The child GUI will return value based on OK/Cancel button click
Code:
import sys
from PyQt4 import QtGui,QtCore,Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Child(QtGui.QWidget):
def __init__(self,switches=None):
super(Child,self).__init__()
self.swwidget = QtGui.QWidget()
self.swlayout = QtGui.QGridLayout()
switches = ['abc1','def1']
switches.sort()
self.switches = switches
def switchesUI(self):
self.swwidget.setWindowModality(QtCore.Qt.ApplicationModal)
self.swl = len(self.switches)
self.sw = {}
self.addsw = []
print ("I am in switchesUI")
#Add the switches to layout dynamically
for i in range(self.swl):
self.sw[i] = QtGui.QCheckBox(self.switches[i])
self.swlayout.addWidget(self.sw[i],i,0)
self.swbuttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel);
self.swbuttonbox.setOrientation(QtCore.Qt.Horizontal)
self.swlayout.addWidget(self.swbuttonbox)
self.swwidget.setWindowTitle('Switches')
self.swwidget.setLayout(self.swlayout)
self.swwidget.show()
self.connect(self.swbuttonbox,QtCore.SIGNAL("accepted()"),self.swaccept)
self.connect(self.swbuttonbox,QtCore.SIGNAL("rejected()"),self.swreject)
def swaccept(self):
for i in range(self.swl):
if self.sw[i].isChecked():
self.addsw.append(self.switches[i])
self.swwidget.close()
return self.addsw
def swreject(self):
self.swwidget.close()
return None
class Parent(QtGui.QWidget):
def __init__(self):
super(Parent,self).__init__()
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.button)
self.assw = ['Test1','Test2']
self.CH = Child(self.assw)
self.connect(self.button,SIGNAL("clicked()"),self.popup)
print ("Child GUI closed")
def popup(self):
self.CH.switchesUI()
def main():
app = QtGui.QApplication(sys.argv)
form = Parent()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
After the button "Test" is clicked, a child GUI will pop-up. I don't want the statement "Child GUI Closed" to be printed till the child GUI is closed.
Can someone suggest me how to achieve this functionality ?
You have to handle closeEvent to perform operations when a window wants to close, also since your Child class inherits from QWidget which means it's a QWidget itself you dont need to create another one with self.swwidget
import sys
from PyQt4 import QtGui,QtCore,Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Child(QtGui.QWidget):
def __init__(self,switches=None):
super(Child,self).__init__()
# self.swwidget = QtGui.QWidget() # you don't need to do this you can add all the properties to self
self.swlayout = QtGui.QGridLayout()
switches = ['abc1','def1']
switches.sort()
self.switches = switches
def switchesUI(self):
self.setWindowModality(QtCore.Qt.ApplicationModal)
self.swl = len(self.switches)
self.sw = {}
self.addsw = []
print ("I am in switchesUI")
#Add the switches to layout dynamically
for i in range(self.swl):
self.sw[i] = QtGui.QCheckBox(self.switches[i])
self.swlayout.addWidget(self.sw[i],i,0)
self.swbuttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel);
self.swbuttonbox.setOrientation(QtCore.Qt.Horizontal)
self.swlayout.addWidget(self.swbuttonbox)
self.setWindowTitle('Switches')
self.setLayout(self.swlayout)
self.show()
self.connect(self.swbuttonbox,QtCore.SIGNAL("accepted()"),self.swaccept)
self.connect(self.swbuttonbox,QtCore.SIGNAL("rejected()"),self.swreject)
def swaccept(self):
for i in range(self.swl):
if self.sw[i].isChecked():
self.addsw.append(self.switches[i])
self.close()
return self.addsw
def swreject(self):
self.close()
return None
def closeEvent(self, event):
print ("Child GUI closed")
class Parent(QtGui.QWidget):
def __init__(self):
super(Parent,self).__init__()
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.button)
self.assw = ['Test1','Test2']
self.CH = Child(self.assw)
self.connect(self.button,SIGNAL("clicked()"),self.popup)
def popup(self):
self.CH.switchesUI()
def main():
app = QtGui.QApplication(sys.argv)
form = Parent()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
My PyQt application starts with Login screen. If password OK, a module-screen (with icons) appears. When user click some button, a QMainWindow will appears. But I can't do this because of qmainwindow object has no attribute '_exec' error. This is my code:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
...
...
class Login(QDialog):
def __init__(self, parent=None):
super(Login, self).__init__(parent)
...
...
uyg=QApplication(sys.argv)
class icons(QDialog):
def __init__(self, parent=None):
super(icons, self).__init__(parent)
...
self.buton = QPushButton()
self.buton.pressed.connect(self.open)
...
def open(self):
dialogmain = Main()
dialogmain._exec() #or dialogmain.show() ???
self.accept()
self.close()
uyg.exec_()
if Login().exec_() == QDialog.Accepted:
dialog = icons()
dialog.exec_()
else:
uyg.quit()
What am I doing wrong? Thank you.
Lately i have done the similar work:I have a loging window and a main window ,and I used something like a FSM to switch between the loging and main window.
Let's say we have 3 state:loging,main,quit.
STATE_LOGING = 0
STATE_MAIN = 1
STATE_QUIT = 2
STATE_DESTROY = 3 #this is a flag
class CState():
sigSwitchState = pyqtSignal(int)
def __init__(self):
super(CState,self).__init__()
def start(self):
pass
def sendQuit(self,nextstate):
self.sigSwitch.emit(nextstate)
class CLoginState(CState):
def __init__(self):
super(CLoginState,self).__init__()
def start(self):
w = Loging()
w.show()
def whenPasswdOk(self):
self.sendQuit(STATE_MAIN)
class CMainState(CState):
def __init__(self):
super(CMainState,self).__init__()
def start(self):
w = MainWindow()
w.show()
def whenMainWindowQuit(self):
self.sendQuit(STATE_QUIT)
class CQuitState(CState):
def __init__(self):
super(CQuitState,self).__init__()
def start(self):
#do some clean stuff...
pass
def whenCleanDone(self):
self.sendQuit(STATE_DESTROY)
class CMainApp():
def __init__(self):
self.app = QApplication(sys.argv)
def __CreateState(state):
if state == STATE_LOGING:
s = CLoginState()
if state == STATE_MAIN:
s = CMainState()
#... same as other state
s.sigSwitchState.connect(self.procNextState)
def procNextState(self,state):
if state == STATE_DESTROY:
QApplication().exit()
s = self.__CreateState(state)
s.start()
def run(self):
self.procNextState(STATE_LOGING)
sys.exit(self.app.exec_())
if __name__ == "__main__":
app = CMainApp()
app.run()
Apart from the application object and QDrag, please pretend that exec() doesn't exist. It is an utterly confusing method that essentially never has to be used. Especially not by anyone new to Qt.
If you want to display any widget, simply show() it. If you want to be notified when a dialog was accepted, connect some code to its accepted() signal. That's all.