How to execute a callback when a QDialog is shown in PyQt4? - qt

I'd like to be able to execute a callback when a QDialog is shown in PyQt4, preferably via the signal/slot mechanism. Looking at the PyQt documentation on QDialog, I can't find the correct signal to which to attach the slot that I want run.
What is a good way to do this?

If you want a signal to be emitted every time the dialog is shown, you could create a class like this:
class Dialog(QtGui.QDialog):
dialogShown = QtCore.pyqtSignal()
def showEvent(self, event):
super(Dialog, self).showEvent(event)
self.dialogShown.emit()
and then use it like this:
self.dialog = Dialog()
self.dialog.dialogShown.connect(self.handleDialogShown)

Would be fine in my opinion to override the show or showEvent method and put your callbacks there.
class MyDialog(QtGui.QDialog):
def show(self):
super(MyDialog, self).show()
callbacks()

Related

Implement a simple QProgressbar PyQT5

i want to implement a QProgressbar for a function in my GUI. I thought the process is simple but it seems not to be.
I thought i can implement the bar like this:
1- Button clicked
2- Progressbar and function start simultaneously
3- Function and Progressbar end simultaneously
What i have found when i searched is the update of this bar with a for-loop.I don't want to do so because my code is a simple function that must be run only one time.
Is it possible to do so ? Or am i misunderstanding something ? I found that the start and stop signals have to be managed with Threading. I wanted to ask here before i go and do further search.
Thanks.
I think the best practice is, using the main thread for QProgressbar or QProgressDialog and side thread for your function.
When main thread is used for your function, the UI won't be updated, even you use for-loop to setValue.
What I am doing is, create a QThread like with a signal giving out the progress:
class AThread(QThread):
progress = pyqtSignal(int)
def run(self):
for i in range(100):
self.progress.emit(i)
Then set the value of progressbar using the number in the signal

Don't auto focus QDialog fields

I am using QtDesigner (4.8.7) to build a QDialog which will serve as the main interaction point for a QGIS plugin I am creating. Most of the user input is entered through various QLineEdit fields, some of which have a placeholderText set.
Unfortunately, every time the QDialog is opened, a QLineEdit field is immediately selected (i.e. it receives focus), which causes the placeholderText to disappear in order to allow the user to enter text. Therefore, I'm wondering if it's possible to create a QDialog which does not automatically focus on any field.
This would allow the end user of my plugin to inspect the place holder texts before entering any value themselves.
I'm currently initializing the QDialog as follows:
import PyQt4.QtGui as QTG
import PyQt4.QtCore as QTC
from dialog_ui import Ui_dialog
class UI (object):
def __init__(self, iface):
# iface is just a way to interact with QGIS
self.iface = iface
self.container = QTG.QDialog()
self.ui = Ui_dialog()
self.setup()
def setup(self):
self.ui.setupUi(self.container)
# Custom ui setup code follows after this...
# Called by an external function
def show(self):
self.container.exec_()
Use the Tab Order Editing Mode in Qt Designer.
Start the tab-ordering on some widget that doesn't have placeholder text. That widget will get the initial focus.
Alternatively, just call setFocus() on an appropriate widget before showing the dialog.
I eventually went for a combination of ekhumoro's answer and the solution that I found here. The gist of the solution is to use clearFocus() on the QLineEdit to force it to lose focus.
I decided to make the QLineEdit lose focus when the user clicks on another location of the overarching QDialog. The code ends up looking like this:
import PyQt4.QtGui as QTG
import PyQt4.QtCore as QTC
from dialog_ui import Ui_dialog
class CustomDialog (QTG.QDialog):
def mousePressEvent(self, event):
focusWidget = QTG.QApplication.focusWidget()
if focusWidget:
focusWidget.clearFocus()
class UI (object):
def __init__(self, iface):
# iface is just a way to interact with QGIS
self.iface = iface
self.container = CustomDialog()
self.ui = Ui_dialog()
# Rest of the code remains unchanged

QWidget Geometry() not updating

I have QWidgets lined up in a QVBoxLayout inside a QScrollArea with setWidgetResizable(True)
When I resize one of the QWidgets, the QWidgets relocate themselves accordingly in the graphical user interface, but their geometry() property doesn't reflect that, geometry().x() and geometry().y() both remain the same before and after relocation.
The only method I have found so far to update the x() and y() coordinates is to hide() and show() the QScrollArea.
I have tried update(), updateGeometry() and repaint() without any success.
Here is a sample test code that I made to summarize the problem :
import sys
from PySide import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.showMaximized()
self.widget_window = QtGui.QWidget()
self.scrollarea = QtGui.QScrollArea()
self.v_layout = QtGui.QVBoxLayout()
self.test_1 = QtGui.QPushButton('Test 1')
self.test_2 = QtGui.QPushButton('Test 2')
self.v_layout.addWidget(self.test_1)
self.v_layout.addWidget(self.test_2)
self.widget_window.setLayout(self.v_layout)
self.scrollarea.setWidgetResizable(True)
self.scrollarea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scrollarea.setAlignment(QtCore.Qt.AlignCenter)
self.scrollarea.setWidget(self.widget_window)
self.setCentralWidget(self.scrollarea)
print(self.test_2.geometry().x())
print(self.test_2.geometry().y())
self.test_1.setFixedHeight(1000)
#uncommenting the following next two lines, solves the problem
#self.scrollarea.hide()
#self.scrollarea.show()
print(self.test_2.geometry().x())
print(self.test_2.geometry().y())
def main():
app = QtGui.QApplication(sys.argv)
main = MainWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
My questions are :
Is there a better solution ?
Isn't updateGeometry() supposed to actually update the geometry() ?
Why is this behavior happening ? (i.e why does it update graphically but not programmatically ?)
Can using hide() and show() successively cause some problems in other contexts (window flickering or something else...) ?
When you update the GUI, it's not instantaneous. It's performed when you give the control to the event loop. In this case, it's done after __init__ completes, which is obviously after all your prints. Therefore, you'll see the old position.
hide/show forces an update at that point. That's why you get correct values. However, this is not the best way, because you might observe flicker while hide/show does its job.
Better way would be telling the event loop to process events before continuing. This will ensure that the GUI is updated:
#...
print(self.test_2.geometry().x())
print(self.test_2.geometry().y())
self.test_1.setFixedHeight(1000)
QtGui.QApplication.processEvents()
print(self.test_2.geometry().x())
print(self.test_2.geometry().y())
#...
PS: I'm assuming, you have good reasons to do this. Most of the time, you won't care when the GUI is updated.

Detecting enter on a QLineEdit or QPushButton

I've built an app for a game, simple to start. It's a game in which the system randomly chooses a number and a gamer (player) tries to find out the number. Everything is almost done. The app consists of a QLineEdit, a label and three buttons. Once the app tells the player the range of the wanted number, he/she types a bet_number and clicks on the play button. And according to this number he/she gets a message about how close or far the wanted number is away from the bet_number.
But I find it a little disgusting to click a button. Instead I want to use Enter key to play. So to achieve this, it comes down to specifically two questions:
How could one change to using Enter to play (I mean I need know when QLineEdit detects enter key is pressed)? In this way I'll code properly to point the play method.
If the play button's got the focus, how do you use enter key on this button? (make Button accept Enter key)
For the QLineEdit connect to the returnPressed signal.
Alternatively, if you use the setAutoDefault method on your QPushButtons you emit the clicked signal when Enter is pressed on a focused QPushButton:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtGui, QtCore
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.pushButtonOK = QtGui.QPushButton(self)
self.pushButtonOK.setText("OK")
self.pushButtonOK.clicked.connect(self.on_pushButtonOK_clicked)
self.pushButtonOK.setAutoDefault(True)
self.lineEditNumber = QtGui.QLineEdit(self)
self.lineEditNumber.returnPressed.connect(self.pushButtonOK.click)
self.layoutHorizontal = QtGui.QHBoxLayout(self)
self.layoutHorizontal.addWidget(self.pushButtonOK)
self.layoutHorizontal.addWidget(self.lineEditNumber)
#QtCore.pyqtSlot()
def on_pushButtonOK_clicked(self):
inputNumber = self.lineEditNumber.text()
if inputNumber.isdigit():
info = "You selected `{0}`"
else:
info = "Please select a number, `{0}` isn't valid!"
print info.format(inputNumber)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())
QLineEdit will emit the signal returnPressed() whenever the user presses the enter key while in it: http://qt-project.org/doc/qt-4.8/qlineedit.html#signals. You can either connect this signal to your button's click() slot or directly call whatever your button's clicked() signal was connected to.
A slight C++ variation on other answers, it's not significantly different, but I thought i would include it anyway because how you lay things in QT code can be very different from codebase to codebase and I wanted to strip out the extraneous stuff to give the shortest and easiest to understand excerpt of code.
QLineEdit *TextSend = new QLineEdit("");
QPushButton *SendPB = new QPushButton("Send!");
connect(TextSend, &QLineEdit::returnPressed, this, &CLITab::SendCommand);
connect(SendPB, &QPushButton::released, this, &CLITab::SendCommand);
So what this is doing is we create a QLineEdit textbox and a QPushbutton.
We do cosmetic things like set the string label for them and add them to our layout.
Then we setup a callback handler, which will be triggered when the QLineEdit returns "returnPressed", which then calls automatically into a function which i wrote called "CLITab::SendCommand()", and then it's upto this function to extract the data out of QLineEdit and do whatever needs to be done. In practice the TextSend and SendPB pointers would live in the parent class, so that SendCommand() has visibility over these objects.
Just putting this here, along side an example pushbutton, because essentially they work in precisely the same way, all that's different is the signal name emitted.

nube: stdio to QTextedit

I've read lots of similar threads to this one but im not such a great programmer that i can make sense of it all. Im using qtcreator to make life simple and want to maka a program that can trigger another process, monitor its stdout and then kill it if necessary.
What I assume i want to do is create a QTextedit in the designer and plug a signal into it that updates the contained text whenever the stream updates, so far so good, but thats where i get fuzzy. My initial thought was to create a subclass of QObject that starts the process as a QProcess and whenever the stdout updates the QObject and appends new data to the QTextedit box.
So my program structure would run like this:
on button press create new QObject derived class.
The QObject derived class constructor starts a QProcess and connects the readyReadStdout() signal to the derived qObject class slot.
When the derived QObject is triggered it takes readyReadStdout() and appends any new data to the QTetEdit box.
on button press, call the derived QObject destructor and which kills the process.
Has anybody done something similar? Like i say ive read similar posts but sometimes it takes asking a question in your own words to be able to understand it
Thanks everyone (also my forst post, woo:)
Ok so heres my update:
I have added an instance of QProcess class (named proc) to my mainWindow class and also new instance of a QObject derived class (named procLog) to which I added a slot. I want this slot to take the readyReadStandardOutput() signal as a trigger to call readAllStandardOutput() and emit the new line to a new signal in procLog, I'm having trouble connecting the QProcess slot to the QObject derived class. heres what I'm trying:
connect(proc, SIGNAL(readyReadStandardOutput ()), procLog, SLOT(logReady()));
I get, error: QObject::connect: Cannot connect (null)::readyReadStandardOutput () to (null)::logReady()
Do you know why this is. Aslo is there a way to add code blocks to text in the comments?
thanks!

Resources