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_())
Related
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_())
I am trying to add drag and drop functionality to a small application. Getting data from a QlistWidget and Dropping the data on a QTableWidget. I should override the dropEvent of QTableWidget in order to add some other functions when dropping the data. But i have trouble, i think i can not get the text() of the object gotten from the ListWidget. here is the code:
class Table(QtWidgets.QTableWidget):
def __init__(self,r,c, parent=None):
super().__init__(r,c,parent)
self.init_ui()
def init_ui(self):
self.setAcceptDrops(True)
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
"""def dragMoveEvent(self, e):
e.setDropAction(QtCore.Qt.MoveAction)
e.accept()
def dragEnterEvent(self,e):
e.accept()"""
def dropEvent(self,e):
data = e.mimeData()
a=e.pos()
row = self.rowAt(a.y())
col = self.columnAt(a.x())
self.setItem(row,col,QtWidgets.QTableWidgetItem(data.text()))
print(row,col)
print(type(data.text()))
print(e.source())
x = data.text()
print(x)
e.accept()
`
The data that is transmitted from a QListWidget through the drag-and-drop is not given through text(), because an item has much more information identified by the roles, in addition you can drag several items. The data is transmitted using the MIME type application/x-qabstractitemmodeldatalist and the solution is to decode it as shown below:
from PyQt5 import QtCore, QtWidgets
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, r,c, parent=None):
super(TableWidget, self).__init__(r,c, parent)
self.setAcceptDrops(True)
self.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly)
def dropEvent(self, event):
md = event.mimeData()
fmt = "application/x-qabstractitemmodeldatalist"
if md.hasFormat(fmt):
encoded = md.data(fmt)
stream = QtCore.QDataStream(encoded, QtCore.QIODevice.ReadOnly)
table_items = []
while not stream.atEnd():
# row and column where it comes from
row = stream.readInt32()
column = stream.readInt32()
map_items = stream.readInt32()
it = QtWidgets.QTableWidgetItem()
for i in range(map_items):
role = stream.readInt32()
value = QtCore.QVariant()
stream >> value
it.setData(role, value)
table_items.append(it)
for it in table_items:
print(it, it.text())
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
list_widget = QtWidgets.QListWidget()
list_widget.setAcceptDrops(False)
list_widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
list_widget.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
for i in range(10):
it = QtWidgets.QListWidgetItem("item-{}".format(i))
list_widget.addItem(it)
table_widget = TableWidget(5, 10)
central_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(central_widget)
hlay.addWidget(list_widget)
hlay.addWidget(table_widget)
self.setCentralWidget(central_widget)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I have a tab widget that contains table widgets for each tab, and I also have a dock widget with options. One of the options is the column count, and I want to change it as soon as the column count spin box value changes. But when switching to a different tab, I'd like the spin box value (and all other options) to reset / switch to that specific tab's settings.
My question is how to best do this and still have the options as a dock widget. I could store all settings as variables for each tab widget and then change the value each time a new tab is opened, I guess, but maybe there is a better solution.
from PyQt5 import QtWidgets, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__()
self.__setup__()
def __setup__(self):
self.resize(400, 400)
tabWidget = TabWidget(self)
self.setCentralWidget(tabWidget)
options = Options(self)
optionsDock = QtWidgets.QDockWidget()
optionsDock.setWidget(options)
optionsDock.setWindowTitle("Options")
self.addDockWidget(QtCore.Qt.TopDockWidgetArea, optionsDock)
options.spinBox_columns.valueChanged.connect(lambda: tabWidget.tabWidget.currentWidget().
setColumnCount(options.spinBox_columns.value()))
class Options(QtWidgets.QWidget):
def __init__(self, parent):
super(Options, self).__init__(parent)
self.__setup__()
def __setup__(self):
self.spinBox_columns = QtWidgets.QSpinBox()
self.spinBox_columns.setValue(1)
self.spinBox_columns.setMinimum(1)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.spinBox_columns)
self.setLayout(layout)
class TabWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(TabWidget, self).__init__(parent)
self.__setup__()
def __setup__(self):
self.tabWidget = QtWidgets.QTabWidget()
for i in range(3):
widget = QtWidgets.QTableWidget()
widget.setColumnCount(1)
widget.setRowCount(3)
self.tabWidget.addTab(widget, "Column " + str(i))
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.tabWidget)
self.setLayout(layout)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
You have to connect the currentChanged signal provided by the index of the tab, then use the widget() method to obtain the index associated with that index, then access its QTabWidget and obtain the number of columns using it to place the value to the QSpinBox.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.__setup__()
def __setup__(self):
self.resize(400, 400)
tabWidget = TabWidget(self)
self.setCentralWidget(tabWidget)
options = Options(self)
optionsDock = QtWidgets.QDockWidget()
optionsDock.setWidget(options)
optionsDock.setWindowTitle("Options")
self.addDockWidget(QtCore.Qt.TopDockWidgetArea, optionsDock)
tabWidget.tabWidget.currentChanged.connect(lambda index: options.spinBox_columns.
setValue(tabWidget.tabWidget.widget(index).columnCount()))
options.spinBox_columns.valueChanged.connect(lambda value: tabWidget.tabWidget.currentWidget().
setColumnCount(value))
I am having problems with the Qt method to update a DataFrame if it has a specific element modified by the user in the GUI.
For example, when I run the following code, I get a 10 by 3 DataFrame with random values displayed. If I try to change any cell to value 400, I double click, type 400 and then press enter. When I print the DataFrame, the value is still the old value. I would like the DataFrame cell to update on user changing the value.
Many thanks!
import sys
import numpy as np
import pandas as pd
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon, QColor
from PyQt5.QtCore import pyqtSlot, Qt, QTimer
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(700, 100, 350, 380)
self.createTable()
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.button = QPushButton('Print DataFrame', self)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.print_my_df)
self.tableWidget.doubleClicked.connect(self.on_click_table)
self.show()
def createTable(self):
self.tableWidget = QTableWidget()
self.df_rows = 10
self.df_cols = 3
self.df = pd.DataFrame(np.random.randn(self.df_rows, self.df_cols))
self.tableWidget.setRowCount(self.df_rows)
self.tableWidget.setColumnCount(self.df_cols)
for i in range(self.df_rows):
for j in range(self.df_cols):
x = '{:.3f}'.format(self.df.iloc[i, j])
self.tableWidget.setItem(i, j, QTableWidgetItem(x))
#pyqtSlot()
def print_my_df(self):
print(self.df)
#pyqtSlot()
def on_click_table(self):
for currentQTableWidgetItem in self.tableWidget.selectedItems():
print((currentQTableWidgetItem.row(), currentQTableWidgetItem.column()))
self.print_my_df()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
QTableWidget does not know about the existence of the DataFrame so it is not updating it. We must update it for this we use the cellChanged signal that gives us the row and column, then we use the item() method that returns the QTableWidgetItem given the column and row, then we use the text() method of QTableWidgetItem.
The data that is placed in the items in the user's edition can be of any type for example a text and this would generate an error since the DataFrame only accepts numerical values for this we must provide an input that validates for this we place a QLineEdit with a QDoubleValidator.
class FloatDelegate(QItemDelegate):
def __init__(self, parent=None):
QItemDelegate.__init__(self, parent=parent)
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
editor.setValidator(QDoubleValidator())
return editor
class TableWidget(QTableWidget):
def __init__(self, df, parent=None):
QTableWidget.__init__(self, parent)
self.df = df
nRows = len(self.df.index)
nColumns = len(self.df.columns)
self.setRowCount(nRows)
self.setColumnCount(nColumns)
self.setItemDelegate(FloatDelegate())
for i in range(self.rowCount()):
for j in range(self.columnCount()):
x = '{:.3f}'.format(self.df.iloc[i, j])
self.setItem(i, j, QTableWidgetItem(x))
self.cellChanged.connect(self.onCellChanged)
#pyqtSlot(int, int)
def onCellChanged(self, row, column):
text = self.item(row, column).text()
number = float(text)
self.df.set_value(row, column, number)
Example:
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(700, 100, 350, 380)
df_rows = 10
df_cols = 3
df = pd.DataFrame(np.random.randn(df_rows, df_cols))
self.tableWidget = TableWidget(df, self)
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.button = QPushButton('Print DataFrame', self)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.print_my_df)
#pyqtSlot()
def print_my_df(self):
print(self.tableWidget.df)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
I have created a window containing a QTreeWidget called StudentTreeWidget, and when double clicking on one of the items in the Tree Widget it should open up another window called StudentEnterprisePassport:
class Student_search(Ui_Student_search):
def __init__(self, database, tableName, parent=None):
QtGui.QWidget.__init__(self)
self.dbu = DB_manager_students.DatabaseUtility(database, tableName)
self.connect(self, QtCore.SIGNAL('itemClicked(QTreeWidgetItem*, int)'), self.handleButton)
self.setupUi(self)
self.UpdateTree()
self.window4 = None
def handleButton(self):
if self.window4 is None:
database = 'EnterprisePassport'
tablename = 'students'
self.window4 = StudentEnterprisePassport(database, tablename)
self.window4.show()
class StudentEnterprisePassport(Ui_StudentEnterprisePassport):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.sessionTracker_btn.clicked.connect(self.handleButton)
When running the code no error is outputted, however, nothing happens when I double click an item in the Tree Widget. The QTreeWidget window is being imported from a Ui file created and converted from Qt Designer.
from PyQt4 import QtCore, QtGui
import DB_manager_students, sys
(....)
class Ui_Student_search(QtGui.QWidget):
def __init__(self, database, tableName):
QtGui.QWidget.__init__(self)
self.dbu = DB_manager_students.DatabaseUtility(database, tableName)
self.setupUi(self)
self.UpdateTree()
def setupUi(self, Student_search):
Student_search.setObjectName(_fromUtf8("Student_search"))
Student_search.resize(1299, 856)
self.gridLayout = QtGui.QGridLayout(Student_search)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.verticalLayout_2 = QtGui.QVBoxLayout()
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
self.StudentTreeWidget = QtGui.QTreeWidget(Student_search)
self.StudentTreeWidget.setObjectName(_fromUtf8("StudentTreeWidget"))
self.verticalLayout_2.addWidget(self.StudentTreeWidget)
self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1)
Code to input data into the TreeWidget:
def UpdateTree(self):
col = self.dbu.GetColumns()
table = self.dbu.GetTable()
for c in range(len(col)):
self.StudentTreeWidget.headerItem().setText(c, col[c][0])
self.StudentTreeWidget.clear()
for item in range(len(table)):
QtGui.QTreeWidgetItem(self.StudentTreeWidget)
for value in range(len(table[item])):
self.StudentTreeWidget.topLevelItem(item).setText(value, str(table[item][value]))
if __name__ == '__main__':
db = 'EnterprisePassport'
tableName = 'students'
app = QtGui.QApplication(sys.argv)
ex = Ui_Student_search(db, tableName)
ex.show()
x = app.exec_()
sys.exit(x)