I am trying to implement a QMainWindow with centralWidget and dockWidget. When the user resize the dock widget I want resizeEvent to be called for the dock widget and some values to be returned. I have implemented the resizeEvent for the complete QMainWindow and it is working fine. How can i call resizeEvent for the dockWidget which is a Qwidget without making another class which will inherit from Qwidget and implement the resizeEvent there and afterwards to create an object in QMainwindow. The first given example is working just fine.
class ui(QMainWindow):
def __init__(self):
super().__init__()
self.bottom_dock_widget = DockWidget('Results')
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottom_dock_widget)
self.resize(500, 500)
def resizeEvent(self, event):
print('mainWindow')
self.update()
class DockWidget(QDockWidget):
def __init__(self, name, image_view):
super().__init__()
self.setWindowTitle(name)
def resizeEvent(self, event):
print('in Dock')
self.update()
Is there a way to be able to implement the example like this:
class ui(QMainWindow):
def __init__(self):
super().__init__()
self.bottom_dock_widget = QDockWidget('Results')
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottom_dock_widget)
self.resize(500, 500)
but to be able to call resizeEvent only for the dock widget
def resizeEvent(self, event):
print('dock')
self.update()
like on c++ with the scope
def bottom_dock_widget :: resizeEvent(self):
If you want to hear the resize event of a widget it is not necessary to override the resizeEvent() method since it is enough to install an event filter analyzed the QEvent::Resize event
import sys
from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtWidgets import QApplication, QDockWidget, QMainWindow
class UI(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.bottom_dock_widget = QDockWidget("Results")
self.bottom_dock_widget.installEventFilter(self)
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottom_dock_widget)
self.resize(500, 500)
def eventFilter(self, obj, event):
if obj is self.bottom_dock_widget and event.type() == QEvent.Resize:
print("dock")
return super().eventFilter(obj, event)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = UI()
w.show()
sys.exit(app.exec_())
Related
This question already has an answer here:
How to Catch Hover and Mouse Leave Signal In PyQt5
(1 answer)
Closed 2 years ago.
I know someone had asked this almost exact same question previously (How to Catch Hover and Mouse Leave Signal In PyQt5)
But I didn't really understand how or where to implement it, I just need some clarification.
Basically, what the user did was he created a second class called button, which he used to create a button. For example, this was the code:
class Button(QPushButton):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
# other initializations...
def enterEvent(self, QEvent):
print("enter")
pass
def leaveEvent(self, QEvent):
print("leave")
pass
Now, in order to create a button with this class, all we need to do is call the class like this:
self.buttn = Button(self)
This creates a button using the previous class. Therefore, this is what the whole code will look like:
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import PyQt5.QtGui
from PyQt5.QtCore import QEvent
import sys
class Button(QPushButton):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
# other initializations...
def enterEvent(self, QEvent):
print("enter")
pass
def leaveEvent(self, QEvent):
print("leave")
pass
class Main(QWidget):
def __init__(self):
super(Main,self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Button")
self.setGeometry(400,400,300,260)
self.buttn = Button(self)
self.buttn.setText("Button") #text
self.buttn.clicked.connect(self.close)
self.buttn.move(100,100)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
Either I don't understand completely how Qt's event propagation works or something, but I cannot understand why exactly closeEvent is not being called both for QPushButton-derived class and for QWidget-derived one itself.
Shouldn't wid.closeEvent() trigger closeEvents of all children widgets?
#!/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from Qt.QtCore import *
from Qt.QtWidgets import *
from Qt.QtGui import *
class butt(QPushButton):
def __init__(self, parent, name='Button'):
super(self.__class__, self).__init__(parent)
self.name = name
def closeEvent(self, e):
print('butt closeevent')
e.accept()
class wid(QWidget):
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
self.initUI()
def initUI(self):
#self.setAttribute(Qt.WA_DeleteOnClose)
self.vl = QVBoxLayout(self)
self.button = butt(self)
self.button.setText('test1')
self.vl.addWidget(self.button)
self.button.clicked.connect(QCoreApplication.quit)
def closeEvent(self, e):
print('wid closeevent')
e.accept()
def show():
app = QApplication(sys.argv)
win = QMainWindow()
widget = wid(win)
win.setCentralWidget(widget)
win.show()
app.exec_()
if __name__ == "__main__":
show()
I'm expecting to see 2 lines
wid closeevent
butt closeevent
as an output, but I see nothing. Why closeEvent it not being called for them?
In the following examples when you press the button visually you will observe the same behavior: the window will close, but we see the difference, in the first one it is called closeEvent(), and in the second one it is not.
Example1:
#!/bin/env python
# -*- coding: utf-8 -*-
from Qt import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
def closeEvent(self, event):
print("button closeEvent")
event.accept()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
button = Button(text="Press me")
button.clicked.connect(button.close)
button.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Example2:
#!/bin/env python
# -*- coding: utf-8 -*-
from Qt import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
def closeEvent(self, event):
print("button closeEvent")
event.accept()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
button = Button(text="Press me")
button.clicked.connect(QtCore.QCoreApplication.quit)
button.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Why do not you call closeEvent when you call QCoreApplication::quit?
Because this method is used to exit the event loop of Qt, and if there is no event loop the events(QCloseEvent) do not work.
When a widget is closed, the children widget is not closed, that is, only if a widget is closed, only its own closeEvent will be called. So if you want the closeevent of the widget to be called, call your method close.
#!/bin/env python
# -*- coding: utf-8 -*-
from Qt import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
def __init__(self, name="Button", parent=None):
super(Button, self).__init__(parent)
self.m_name = name
def closeEvent(self, event):
print("button closeEvent")
event.accept()
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.initUI()
def initUI(self):
vl = QtWidgets.QVBoxLayout(self)
button = Button()
button.setText("test1")
vl.addWidget(button)
button.clicked.connect(self.close)
def closeEvent(self, event):
print("Widget closeevent")
event.accept()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
widget = Widget()
w.setCentralWidget(widget)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
In the previous example depending on how you interact with the widget you will get the following behavior:
If you press the button the widget will close so it will be called closeEvent, and will not call the closeEvent of the button because even your child is not closing it.
If you press the "X" button in the window it will not be called the closeEvent of the widget but the QMainWindow, the explanation is the same as the previous one.
Conclusions:
Each type of event has its own workflow, some events are only received by the widget and not by the children, while others send the information to the children.
The close method uses the event loop to notify that the widget is to be closed, but QCoreApplication::quit() terminates the event loop.
Update:
why wid.closeEvent is not called when user pressing "X" button on Qt window? Aren't main window supposed to call closeEvent on all it's children widgets and then destroy them properly?
No, one thing is the closing of a widget and another thing is the destruction of the widget, it can be destroyed without closing and closing the window does not involve destroying the object.
As already pointed out closing a window does not imply deleting it, there may be other windows open, but if the last QApplication window is closed by default the eventloop that implies the destruction of the widget will be terminated which does not necessarily imply calling the close method.
To be understood, let's use the following code:
from Qt import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
def closeEvent(self, event):
print("closeEvent Button")
super(Button, self).closeEvent(event)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
button_quit = Button(
text="quit",
clicked=QtCore.QCoreApplication.quit
)
button_close = Button(
text="close",
clicked=self.close
)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button_quit)
lay.addWidget(button_close)
def closeEvent(self, event):
print("closeEvent Widget")
super(Widget, self).closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
There are 2 buttons, in the case of the first button that calls QCoreApplication::quit() that will end the eventloop so all the widgets will be destroyed, and in that case no closeEvent will be called, in the case of the second button it will be call near the window so it will call its closeEvent but not the closeEvents of its children.
my actual problem is that I have saveUI() func in closeEvent, and it's not being called upon widgets hierarchical destruction on window close
If you want the closeEvent method to be called hierarchically then you must call the close method manually since Qt does not design it that way. In the next part there is an example:
from PyQt5 import QtCore, QtGui, QtWidgets
class PushButton(QtWidgets.QPushButton):
def closeEvent(self, event):
for children in self.findChildren(
QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
):
children.close()
print("closeEvent PushButton")
super(PushButton, self).closeEvent(event)
class LineEdit(QtWidgets.QLineEdit):
def closeEvent(self, event):
for children in self.findChildren(
QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
):
children.close()
print("closeEvent LineEdit")
super(LineEdit, self).closeEvent(event)
class ComboBox(QtWidgets.QComboBox):
def closeEvent(self, event):
for children in self.findChildren(
QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
):
children.close()
print("closeEvent ComboBox")
super(ComboBox, self).closeEvent(event)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
button_close = PushButton(text="close", clicked=self.close)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button_close)
lay.addWidget(LineEdit())
lay.addWidget(ComboBox())
def closeEvent(self, event):
for children in self.findChildren(
QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
):
children.close()
print("closeEvent Widget")
super(Widget, self).closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I am new to PyQt. I designed a form in QtDeveloper which have three controls. One push button, one combo box and one line edit. The name of the line edit widget in my ui form is myLineEdit. I want to know which Qwidget got focus (QLineEdit or QComboBox). I implement the code obtained from internet. When the code run, a separate line edit is created and it works fine. But I want to give the focusInEvent to myLineEdit widget created in the .ui form. My code is given. Please help.
class MyLineEdit(QtGui.QLineEdit):
def __init__(self, parent=None):
super(MyLineEdit, self).__init__(parent)
def focusInEvent(self, event):
print 'focus in event'
self.clear()
QLineEdit.focusInEvent(self, QFocusEvent(QEvent.FocusIn))
class MainWindow(QtGui.QMainWindow,Ui_MainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.myLineEdit = MyLineEdit(self)
You must implement the eventFilter method and enable this property to the widgets that are needed with:
{your widget}.installEventFilter(self)
The eventFilter method has as information the object and type of event.
Example
import sys
from PyQt5 import uic
from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QApplication, QWidget
uiFile = "widget.ui" # Enter file here.
Ui_Widget, _ = uic.loadUiType(uiFile)
class Widget(QWidget, Ui_Widget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent=parent)
self.setupUi(self)
self.lineEdit.installEventFilter(self)
self.pushButton.installEventFilter(self)
self.comboBox.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QEvent.FocusIn:
if obj == self.lineEdit:
print("lineedit")
elif obj == self.pushButton:
print("pushbutton")
elif obj == self.comboBox:
print("combobox")
return super(Widget, self).eventFilter(obj, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Ouput:
lineedit
pushbutton
combobox
pushbutton
lineedit
Hi, I'm trying to make simple GUI application using PyQt5, Python 3.4 and Windows 7.
Below code works properly.
# coding: utf-8
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
class MainWnd(QMainWindow):
def __init__(self):
super().__init__()
self.popup_dlg = None
self.init_ui()
def init_ui(self):
self.setGeometry(100, 100, 300, 200)
self.show()
self.popup_dlg = ChildWnd()
class ChildWnd(QDialog):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.resize(200, 100)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWnd()
sys.exit(app.exec_())
Two windows are created. One is main window and the other is child window(popup window). But what I want is to make child window's default location is centered of main window.
So I've modified the code like this.
# coding: utf-8
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
class MainWnd(QMainWindow):
def __init__(self):
super().__init__()
self.popup_dlg = None
self.init_ui()
def init_ui(self):
self.setGeometry(100, 100, 300, 200)
self.show()
self.popup_dlg = ChildWnd(self) # make instance with parent window argument.
class ChildWnd(QDialog):
def __init__(self, parent_wnd):
super().__init__()
self.setParent(parent_wnd) # set child window's parent
self.init_ui()
def init_ui(self):
self.resize(200, 100)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWnd()
sys.exit(app.exec_())
But this code makes problem. Child window doesn't show up. Only main window(=parent window) shows. In Qt's QDialog's manual, I found this.
but if it has a parent, its default location is centered on top of the
parent's top-level widget (if it is not top-level itself).
This is why I added the setParent().
What should I do?
Please help me!!
As specified in the documentation calling setParent will just change the ownership of the QDialog widget. If you want the QDialog widget to be centered within it's parent, you need to pass the parent widget instance to the super constructor of your QDialog:
class ChildWnd(QDialog):
def __init__(self, parent_wnd):
super().__init__(parent_wnd)
self.init_ui()
def init_ui(self):
self.resize(200, 100)
self.show()
I am new to Qt/PySide. I want QLineEdit to select all text in it when it gets focus. After getting focus and selecting all text, it should select all text only after focus is lost and gained again. It should not select all text when I change cursor position after QLineEdit gains focus. How do I do that?
Update: My current code improved as suggested by Ashwani Kumar. I still can't get it to work though:
import sys
from PySide.QtGui import QLineEdit, QApplication, QVBoxLayout, QWidget
class MyLineEdit(QLineEdit):
def __init__(self, parent=None):
super(MyLineEdit, self).__init__(parent)
def focusInEvent(self, e):
self.selectAll()
app = QApplication(sys.argv)
top = QWidget()
layout = QVBoxLayout()
layout.addWidget(MyLineEdit())
layout.addWidget(MyLineEdit())
top.setLayout(layout)
top.show()
app.exec_()
With focusInEvent, when you click the widget, it gets executed, but since you click, it removes the selected text.
To overcome this, we must use the mousePressEvent, this can be done two ways:
import sys
from PySide.QtGui import QLineEdit, QApplication, QVBoxLayout, QWidget
class MyLineEdit(QLineEdit):
def __init__(self, parent=None):
super(MyLineEdit, self).__init__(parent)
def mousePressEvent(self, e):
self.selectAll()
app = QApplication(sys.argv)
top = QWidget()
layout = QVBoxLayout()
layout.addWidget(MyLineEdit())
layout.addWidget(MyLineEdit())
top.setLayout(layout)
top.show()
app.exec_()
Or you can do it by simply overriding the base QLineEdit class:
txt_demo = QtGui.QLineEdit()
txt_demo.mousePressEvent = lambda _ : txt_demo.selectAll()
However, since we are modifying the mousePressEvent, whenever you try to click text, it will always select all first.
For future visitors, I am posting code that is working for me. As I am a newbie I am not sure if the code contains any malpractices. If it does feel free to comment and I'll update my code/answer. Code:
import sys
from PySide.QtGui import QLineEdit, QApplication, QVBoxLayout, QWidget
class LineEdit(QLineEdit):
def __init__(self, parent=None):
super(LineEdit, self).__init__(parent)
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
super(LineEdit, self).mousePressEvent(e) #required to deselect on 2e click
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
super(LineEdit, self).focusOutEvent(e) #required to remove cursor on focusOut
self.deselect()
self.readyToEdit = True
app = QApplication(sys.argv)
top = QWidget()
layout = QVBoxLayout()
layout.addWidget(LineEdit())
layout.addWidget(LineEdit())
top.setLayout(layout)
top.show()
app.exec_()
You have to subclass the QLineEdit and then use the new class instead of QLineEdit.
e.g: -
class MyLineEdit(QtGui.QLineEdit):
def __init__(self, parent=None)
super(MyLineEdit, self).__init__(parent)
def focusInEvent(self, e):
self.selectAll()
lineedit = MyLineEdit()
QTimer solution as seen on QtCentre:
import types
from PyQt4 import QtCore
def bind(func, to):
"Bind function to instance, unbind if needed"
return types.MethodType(func.__func__ if hasattr(func, "__self__") else func, to)
...
self.txtSrc.focusInEvent = bind(lambda w, e: QtCore.QTimer.singleShot(0, w.selectAll), self.txtSrc)
Also the provided solution doesn't require to subclass QLineEdit.
These answers don't really provide the sort of standard ergonomics you'd probably want (and users might expect). For example, if you single-click once on a QLE which is not currently all-selected, and then single-click again, typically you'd want the first click to select-all, and the second click to allow you to place the cursor in the specific spot you have chosen.
This can be achieved simply by doing this:
def mousePressEvent(self, event):
already_select_all = self.text() == self.selectedText()
super().mousePressEvent(event)
if not already_select_all:
self.selectAll()
The question in fact asks about gaining focus, not specifically by mouse-clicking, and indeed, if you are a keyboardist or generally musophobic you'll probably also want the whole text to be selected any time the QLE gains focus, e.g. by tabbing or by use of a QLabel "buddy" mnemonic. This seems to do the job:
class MyLineEdit(QtWidgets.QLineEdit):
def __init__(self, *args):
super().__init__(*args)
self.focus_in_reason = None
def focusInEvent(self, event):
super().focusInEvent(event)
self.selectAll()
self.focus_in_reason = event.reason()
def mousePressEvent(self, event):
super().mousePressEvent(event)
if self.focus_in_reason == QtCore.Qt.MouseFocusReason:
self.selectAll()
self.focus_in_reason = None