With the window declared with CustomWidget as super class: class App(CustomWidget) hitting Alt+A properly prints 'keyPressEvent: Alt + a' message.
But the KeyEvent functionality is broken when the CustomWidget is assigned to window with setCentralWidget() or is set with layer.addWidget(widget). What is missing in a code?
from PyQt4 import QtCore, QtGui
class CustomWidget(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent=parent)
def keyPressEvent(self, event):
if event.modifiers() == QtCore.Qt.AltModifier:
if event.key() == QtCore.Qt.Key_A:
print 'keyPressEvent: Alt + a'
# super(CustomWidget, self).keyPressEvent(event)
class App(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent=parent)
centralWidget = CustomWidget(self)
self.setCentralWidget(centralWidget)
mainLayout=QtGui.QVBoxLayout()
centralWidget.setLayout(mainLayout)
widget = CustomWidget(self)
mainLayout.addWidget(widget)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = App()
w.show()
sys.exit(app.exec_())
The widget must have focus to receive the event. Make sure you call setFocusPolicy() to have the CustomWidget accept and maintain focus after creating the window.
QWidget, keyPressEvent
QWidget, setFocusPolicy
Working Solution:
Important: At the end of GroupBox' keyPressEvent() method we have to pass the Event up to the super. Or the Event will not get propagated to the parent widget: super(QtGui.QGroupBox, self).keyPressEvent(event)
import sys
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent):
QtGui.QMainWindow.__init__(self, parent=parent)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def keyPressEvent(self, event):
if event.modifiers() == QtCore.Qt.ControlModifier:
if event.key() == QtCore.Qt.Key_T:
print 'MainWindow: Control + t'
if event.key() == QtCore.Qt.Key_M:
print 'MainWindow: Control + m'
class GroupBox(QtGui.QGroupBox):
def __init__(self, parent=None):
QtGui.QGroupBox.__init__(self, parent=parent)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def keyPressEvent(self, event):
if event.modifiers() == QtCore.Qt.ControlModifier:
if event.key() == QtCore.Qt.Key_T:
print 'GroupBox: Control + t'
if event.key() == QtCore.Qt.Key_S:
print 'GroupBox: Control + s'
super(QtGui.QGroupBox, self).keyPressEvent(event)
class App(MainWindow):
def __init__(self, parent=None):
MainWindow.__init__(self, parent=parent)
centralWidget = QtGui.QWidget(self)
self.setCentralWidget(centralWidget)
mainLayout=QtGui.QVBoxLayout()
centralWidget.setLayout(mainLayout)
groupBox = GroupBox(self)
mainLayout.addWidget(groupBox)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = App()
w.show()
sys.exit(app.exec_())
Related
I am using Python3.6 and PySide2
I have a treewidget-A populated with employee list.
on click on employee (treewidget-A item) a another treeWidget-B will populate.
Now if I click on empty area (deselect item) but treeWidget-B still showing last clicked employee item details.
This is to populate treewidget items
self.treeWidget_A.itemClicked.connect(self.populate_employee)
self.treeWidget_A.currentItemChanged.connect(self.populate_employee)
How to execute clear_employee_data() function on click on empty area of QtreeWidget ?
def clear_employee_data(self):
self.treeWidget_B.clear()
You have to detect the click and verify that there is not an item:
from PySide2 import QtCore, QtWidgets
class TreeWidget(QtWidgets.QTreeWidget):
emptyClicked = QtCore.Signal()
def __init__(self, parent=None):
super(TreeWidget, self).__init__(parent)
for i in range(2):
it = QtWidgets.QTreeWidgetItem(self, ["item-{}".format(i)])
for j in range(3):
child_it = QtWidgets.QTreeWidgetItem(it, ["item-{}-{}".format(i, j)])
self.expandAll()
def mousePressEvent(self, event):
super(TreeWidget, self).mousePressEvent(event)
if not self.indexAt(event.pos()).isValid():
self.emptyClicked.emit()
#QtCore.Slot()
def on_empty_clicked():
print("clicked")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = TreeWidget()
w.emptyClicked.connect(on_empty_clicked)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
With Qt Designer use eventfilter:
from PySide2 import QtCore, QtGui, QtWidgets
from design import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
emptyClicked = QtCore.Signal()
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.treeWidget_A.viewport().installEventFilter(self)
self.emptyClicked.connect(self.on_emptyClicked)
def eventFilter(self, obj, event):
if obj is self.treeWidget_A.viewport() and event.type() == QtCore.QEvent.MouseButtonPress:
if not self.treeWidget_A.indexAt(event.pos()).isValid():
self.emptyClicked.emit()
return super(MainWindow, self).eventFilter(obj, event)
#QtCore.Slot()
def on_emptyClicked(self):
print("empty")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I open the dialog from the main window, where by clamping the keys, I fill the line with their names. The problem is that I can not understand where you need to do a cycle of checking all the keys on their state. Maybe there is another way to get the keys pressed? Or where you need to listen to the clamping so that the dialog box does not hang and the string is updated.
MainWindow:
def showBindings(self, param):
from dialogs import KeyBindingsDialog
self.dialog = KeyBindingsDialog()
self.dialog.show()
Dialog:
class KeyBindingsDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(KeyBindingsDialog, self).__init__(parent)
self.ui = KeyBindings()
self.ui.setupUi(self)
Use QKeySequenceEdit:
from PyQt5 import QtCore, QtGui, QtWidgets
class KeySequenceEdit(QtWidgets.QKeySequenceEdit):
def keyPressEvent(self, event):
super(KeySequenceEdit, self).keyPressEvent(event)
seq_string = self.keySequence().toString(QtGui.QKeySequence.NativeText)
if seq_string:
last_seq = seq_string.split(",")[-1].strip()
le = self.findChild(QtWidgets.QLineEdit, "qt_keysequenceedit_lineedit")
self.setKeySequence(QtGui.QKeySequence(last_seq))
le.setText(last_seq)
self.editingFinished.emit()
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self._keysequenceedit = KeySequenceEdit(editingFinished=self.on_editingFinished)
button = QtWidgets.QPushButton("clear", clicked=self._keysequenceedit.clear)
hlay = QtWidgets.QHBoxLayout(self)
hlay.addWidget(self._keysequenceedit)
hlay.addWidget(button)
#QtCore.pyqtSlot()
def on_editingFinished(self):
sequence = self._keysequenceedit.keySequence()
seq_string = sequence.toString(QtGui.QKeySequence.NativeText)
print("sequence: ", seq_string)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Is there a way to determine the width of the arrow buttons in the qspinbox?
I'm trying to overwrite the context menu event, and i only want my custom event to take place if the user right-clicks over the arrow button, otherwise i want the normal context menu to appear.
Right now I'm just hardcoding a value of 20 which is not ideal.
import sys
import os
from PySide import QtGui, QtCore
class MySpinner(QtGui.QSpinBox):
def __init__(self, parent=None):
super(MySpinner, self).__init__(parent)
self.setAccelerated(False)
self.setRange(-1000,1000)
self.setSingleStep(1)
self.setValue(300)
def contextMenuEvent(self, event):
if event.pos().x() > self.rect().right()-20:
self.setValue(50)
self.selectAll()
else:
super(self.__class__, self).contextMenuEvent(event)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(300, 200)
grid = QtGui.QVBoxLayout()
grid.addWidget(MySpinner())
content = QtGui.QWidget()
content.setLayout(grid)
self.setCentralWidget(content)
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Instead of obtaining the width it is only necessary to obtain the SubControl to know if it was pressed in one of the arrows buttons:
def contextMenuEvent(self, event):
opt = QtGui.QStyleOptionSpinBox()
self.initStyleOption(opt)
opt.subControls = QtGui.QStyle.SC_All
hoverControl = self.style().hitTestComplexControl(QtGui.QStyle.CC_SpinBox, opt, event.pos(), self)
if hoverControl == QtGui.QStyle.SC_SpinBoxUp:
print("up")
elif hoverControl == QtGui.QStyle.SC_SpinBoxDown:
print("down")
else:
super(self.__class__, self).contextMenuEvent(event)
If you want to get the QRect of each subcontrol you should use
# up
rect_up = self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, QtGui.QStyle.SC_SpinBoxUp, self)
# down
rect_down = self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, QtGui.QStyle.SC_SpinBoxDown, self)
Another option:
def contextMenuEvent(self, event):
opt = QtGui.QStyleOptionSpinBox()
self.initStyleOption(opt)
r = QtCore.QRect()
for sc in (QtGui.QStyle.SC_SpinBoxUp, QtGui.QStyle.SC_SpinBoxDown):
r= r.united(self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, sc, self))
if r.contains(event.pos()):
print("arrow")
else:
super(self.__class__, self).contextMenuEvent(event)
I am trying to run enterFunc() when pressing enter (return) key. But not working. Here is the code and what is the true code?:
class myForm(QMainWindow):
...
def keyPressEvent(self,event):
if(event.key()==Qt.Key_Enter):
enterFunc()
...
myForm.myTreeWidget.keyPressEvent(self,event)
...
First xxxEvent are not signals and should not be invoked, if you want to listen to one of them you should use an event filter as shown below, on the other hand you should not use the Qt::Key_Enter key but the Qt::Key_Return key:
from PyQt5 import QtCore, QtWidgets, uic
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
uic.loadUi("ui_mainwindow.ui",self)
self.myTreeWidget.installEventFilter(self)
def eventFilter(self, obj, event):
if obj == self.myTreeWidget:
if event.type() == QtCore.QEvent.KeyPress:
if event.key() == QtCore.Qt.Key_Return:
print("enter pressed")
return super(MainWindow, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Or more easily use QShortcut:
from PyQt5 import QtCore, QtWidgets, uic
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
uic.loadUi("ui_mainwindow.ui",self)
shorcut = QtWidgets.QShortcut(QtCore.Qt.Key_Return,
self.myTreeWidget,
context=QtCore.Qt.WidgetShortcut,
activated=self.some_function)
def some_function(self):
print("some_function")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Hi my question is related to this: PyQT4 WheelEvent
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
layout.addWidget(Scroll(self))
class Scroll(QScrollArea):
def __init__(self, parent=None):
super(Scroll, self).__init__(parent)
self.parent = parent
def wheelEvent(self, event):
super(Scroll, self).wheelEvent(event)
print "wheelEvent", event.delta()
newHeight = self.parent.geometry().height() - event.delta()
width = self.parent.geometry().width()
self.parent.resize(width, newHeight)
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
My Scroll class is created in Qt Designer and i have no access to it
Is there any opportunity to have access to PyQT WheelEvent of Scroll class but in Main class?
Use an event-filter:
class Main(QWidget):
def __init__(self, parent=None):
...
self.scroll.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.Wheel and source is self.scroll:
print "wheelEvent", event.delta()
newHeight = self.geometry().height() - event.delta()
width = self.geometry().width()
self.resize(width, newHeight)
# return True to consume the event
return False
return super(Main, self).eventFilter(source, event)