At the moment I'm writing a calendar program with QT. My main window holds a QCalendarWidget and now I want to listen to double click events of the cells. My problem is that I do not know how I can get a cell (which ia a child of the QCalendarWidget) so I can add an event listener to it. With:
calendarWidget.findChildren(QtCore.QObject)
I can get all children of the Widget but I do not know how to identify a cell. Do you have any ideas how I can do this?
The calendar widget contains a QTableView, so you can get a reference to that and query its contents.
The demo below installs an event-filter on the table to get double-clicks, because the table's doubleClicked signal is disabled by the calendar (presumably to prevent editing of the cells).
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.calendar = QtGui.QCalendarWidget(self)
self.table = self.calendar.findChild(QtGui.QTableView)
self.table.viewport().installEventFilter(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.calendar)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseButtonDblClick and
source is self.table.viewport()):
index = self.table.indexAt(event.pos())
print('row: %s, column: %s, text: %s' % (
index.row(), index.column(), index.data()))
return super(Window, self).eventFilter(source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(750, 250, 300, 300)
window.show()
sys.exit(app.exec_())
Related
I am trying to build a hover Dialog but i am stuck at the interaction between QComboBox selection and QDialog's leaveEvent. it looks like when i try to click on combobox to select something, it triggers a leaveEvent which then hides my QDialog. Why is this happening? What can i try to ensure that the Dialog is only hidden when I move my mouse outside of the Dialog?
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
class hoverDialog(QDialog):
def __init__(self, parent=None):
super().__init__()
self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.v = QVBoxLayout()
self.combobox = QComboBox()
self.combobox.addItems(['Work-around','Permanent'])
self.textedit = QPlainTextEdit()
self.v.addWidget(self.combobox)
self.v.addWidget(self.textedit)
self.setLayout(self.v)
#self.setMouseTracking(True)
def leaveEvent(self, event):
self.hide()
return super().leaveEvent(event)
class Table(QWidget):
def __init__(self, parent=None):
super().__init__()
self.label4 = QLabel()
self.label4.setText("hover popup")
self.label4.installEventFilter(self)
self.checkbox1 = QCheckBox()
self.pushButton3 = QPushButton()
self.pushButton3.setText('Generate')
self.pushButton3.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
self.pushButton3.clicked.connect(self.buttonPressed)
self.hbox5 = QHBoxLayout()
self.hbox5.addWidget(self.checkbox1)
self.hbox5.addWidget(self.label4)
self.hbox5.addWidget(self.pushButton3)
self.vbox1 = QVBoxLayout()
self.vbox1.addLayout(self.hbox5)
self.setLayout(self.vbox1)
self.autoResolve = hoverDialog(self)
def eventFilter(self, obj, event):
if obj == self.label4 and event.type() == QtCore.QEvent.Enter and self.autoResolve.isHidden():
self.onHovered()
return super().eventFilter(obj, event)
def onHovered(self):
pos = QtGui.QCursor.pos()
self.autoResolve.move(pos)
self.autoResolve.show()
def buttonPressed(self):
if self.checkbox.isChecked():
print('do something..')
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Table()
form.show()
app.exec_()
Looking at the sources, I believe that the origin of the problem is in Qt's behavior whenever a new popup widget is shown.
I cannot guarantee this at 100%, but in any case the simplest solution is to hide the widget only if the combo popup is not shown:
def leaveEvent(self, event):
if not self.combobox.view().isVisible():
self.hide()
Note that your approach is not really perfect: since the dialog could be shown with a geometry that is outside the current mouse position, it will not be able to hide itself until the mouse actually enters it. You should probably filter FocusOut and WindowDeactivate events also.
How to fire mouse click event on clicking in year option for QCalendarWidget.
onclick of year(2012),
i want to print some text using pyqt5
Can anyone help. Thanks in advance/
The first thing is to obtain the QSpinBox that shows the year using findChildren, then it is to detect the mouse event but as this solution points out it is not possible so a workaround is to detect the focus event:
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.calendar_widget = QtWidgets.QCalendarWidget()
self.setCentralWidget(self.calendar_widget)
self.year_spinbox = self.calendar_widget.findChild(
QtWidgets.QSpinBox, "qt_calendar_yearedit"
)
self.year_spinbox.installEventFilter(self)
def eventFilter(self, obj, event):
if obj is self.year_spinbox and event.type() == QtCore.QEvent.FocusIn:
print(self.year_spinbox.value())
return super().eventFilter(obj, event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I am working on a custom drag-drop implementation in a QTableView. When I drag a cell and drop it on another cell I want to manually change some data in the model based on what was dragged and where it was dropped. How can I do this? I've been reading through all Qt documentation but I am utterly lost, and in particular with drag-drop it seems that the C++ to PyQt conversion is a little less intuitive.
Basically what I need is when I drop I want to know what cells were initially dragged, and where they were dropped. Where my confusion lies seems to be with QMimeData. From what I can tell when the drag starts, the drag event receives the right MIME data but I don't know how to get at it in PyQt (been able to do this sort of thing with text and urls in the past but I'm lost when it comes to an item view). I also need to know where I'm dropping to. I guess I could do an "item at cursor pos" but I assume this data already exists in the drop event and I just need to figure out how to query it.
Here's a simple example:
import sys
from PyQt4 import QtGui, QtCore
class TableView(QtGui.QTableView):
def __init__(self, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
self.setDragEnabled(True)
self.setDropIndicatorShow(True)
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.setDragDropOverwriteMode(False)
self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
# I want to do cool things with the dragged cells, and I need to know where they dropped!
print(event.mimeData().formats()) # this tells me that I shuld get some sort of "qabstractitemmodeldatalist". Sounds promising...
print(event.mimeData().data("application/x-qabstractitemmodeldatalist")) # this gives me an interesting looking QByteArray but I have no idea what to do with it...
event.accept()
class Dialog(QtGui.QDialog):
def __init__(self):
super(Dialog, self).__init__()
model = QtGui.QStandardItemModel(self)
model.insertRow(0, QtGui.QStandardItem("C"))
model.insertRow(0, QtGui.QStandardItem("B"))
model.insertRow(0, QtGui.QStandardItem("A"))
table = TableView(self)
table.setModel(model)
app = QtGui.QApplication(sys.argv)
ex = Dialog()
ex.show()
sys.exit(app.exec_())
You can not know where it was dragged since the mimeData does not have that information but you can get the data dragged, for that we created a temporary model where we will establish the mimeData emulating the same behavior of the drag. To obtain where it was dropped, the position that comes as part of the event must be used together with indexAt(), thus obtaining the QModelIndex:
import sys
from PyQt4 import QtGui, QtCore
class TableView(QtGui.QTableView):
def __init__(self, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.setDragDropOverwriteMode(False)
self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
if self.viewport().rect().contains(event.pos()):
fake_model = QtGui.QStandardItemModel()
fake_model.dropMimeData(
event.mimeData(), event.dropAction(), 0, 0, QtCore.QModelIndex()
)
print("from:")
for r in range(fake_model.rowCount()):
for c in range(fake_model.columnCount()):
ix = fake_model.index(r, c)
print(ix.data())
to_index = self.indexAt(event.pos())
if to_index.isValid():
print("to:", to_index.data())
super(TableView, self).dropEvent(event)
class Dialog(QtGui.QDialog):
def __init__(self):
super(Dialog, self).__init__()
model = QtGui.QStandardItemModel(self)
for letter in "ABC":
model.appendRow(QtGui.QStandardItem(letter))
table = TableView()
table.setModel(model)
lay = QtGui.QVBoxLayout(self)
lay.addWidget(table)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = Dialog()
ex.show()
sys.exit(app.exec_())
lets consider the following screenshot:
You can see that the top toolbar displays 2 rows; however to do so , in need to click on the >> at the top right (circled in red) and keep hovering hover the toolbar area, which can get a bit annoying.
Is there a way to keep the 2 rows of the toolbar always displaying?
The solution is:
Expand the QToolBar using the layout that in the implementation of the private API has a slot called setExpanded() that allows to expand the QToolBar.
Hide the button, and for this case it only worked to set the size to QSize(0, 0).
Deactivate the event Leave of the QToolBar so that it does not collapse.
from PyQt5 import QtCore, QtGui, QtWidgets
class ToolBar(QtWidgets.QToolBar):
def __init__(self, parent=None):
super().__init__(parent)
lay = self.findChild(QtWidgets.QLayout)
if lay is not None:
lay.setExpanded(True)
QtCore.QTimer.singleShot(0, self.on_timeout)
#QtCore.pyqtSlot()
def on_timeout(self):
button = self.findChild(QtWidgets.QToolButton, "qt_toolbar_ext_button")
if button is not None:
button.setFixedSize(0, 0)
def event(self, e):
if e.type() == QtCore.QEvent.Leave:
return True
return super().event(e)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
toolbar = ToolBar()
for i in range(20):
toolbar.addAction("action{}".format(i))
w.addToolBar(QtCore.Qt.TopToolBarArea, toolbar)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
I have QTableView that gets information from a QSqlQueryModel and displays it in real time. The thing is, QTableView allows the user to copy and paste the info from one of the fields.
projectModel = QtSql.QSqlQueryModel()
projectModel.setQuery("select * from queue",self.db)
self.total_rows = projectModel.rowCount()
projectModel.setHeaderData(0, QtCore.Qt.Horizontal, 'ID cola')
projectModel.setHeaderData(1, QtCore.Qt.Horizontal, 'Código')
self.projectView = QtGui.QTableView()
self.projectView.setModel(projectModel)
self.projectView.resizeColumnsToContents()
self.projectView.horizontalHeader().setStretchLastSection(True)
How do I deny copying the content of QTableView and pasting it outside in a text editor, for example?
You can make the whole table read-only like this:
self.projectView.setEditTriggers(QAbstractItemView.NoEditTriggers)
EDIT:
If you also want to prevent copying of cells, you will need to kill the relevant keyboard shortcuts. Below is some example code that does that:
from PySide import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self, rows, columns):
super(Window, self).__init__()
self.table = QtGui.QTableView(self)
model = QtGui.QStandardItemModel(rows, columns, self.table)
for row in range(rows):
for column in range(columns):
item = QtGui.QStandardItem('(%d, %d)' % (row, column))
model.setItem(row, column, item)
self.table.setModel(model)
self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table)
self.table.installEventFilter(self)
def eventFilter(self, source, event):
if (source is self.table and
event.type() == QtCore.QEvent.KeyPress and
event == QtGui.QKeySequence.Copy):
return True
return super(Window, self).eventFilter(source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window(5, 5)
window.setGeometry(600, 300, 600, 250)
window.show()
sys.exit(app.exec_())