Make checkbox by QAbstractTableModel - python

I want to make the first column with checkbox, and get the check status of that, how can I do that? I override the flag(), but it seems do not work, I am sure where is the problem?
And I got this links, but it does work for my code either. And I don't want to use delegate, because it is too complicated.
data = [['00','01','02'],
['10','11','12'],
['20','21','22']]
class MainWindow(QWidget):
def __init__(self, parent=None, *args):
super(MainWindow, self).__init__(parent)
clipTableWidget = QTableWidget()
self.model = TModel(data, self)
clipTableView = QTableView()
clipTableView.setModel(self.model)
layout = QVBoxLayout()
layout.addWidget(clipTableView)
self.setLayout(layout)
class TModel(QAbstractTableModel):
def __init__(self, datain, parent=None):
super(TModel, self).__init__(parent)
self.arraydata = datain
def rowCount(self, parent=QModelIndex()):
return len(self.arraydata)
def columnCount(self, parent=QModelIndex()):
return len(self.arraydata[0])
def data(self, index, role):
if not index.isValid():
return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
return QVariant(self.arraydata[index.row()][index.column()])
def flags(self, index):
if not index.isValid():
return QVariant()
elif index.column() == 1:
return Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsUserCheckable
return QVariant()

Ok here is the solution to your problem. The reason Why your flags weren't working is because those flags dont work on text , they are meant for QItems .
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class MainWindow(QWidget):
def __init__(self, parent=None, *args):
super(MainWindow, self).__init__(parent)
data = [['00','01','02'],
['10','11','12'],
['20','21','22']]
clipTableWidget = QTableWidget()
self.model = QStandardItemModel(self)
clipTableView = QTableView()
count1=0
for row in data:
count2 = 0
for column in row:
if count2 == 0:
item = QStandardItem(column)
item.setCheckable(True)
item.setCheckState(False)
item.setFlags(Qt.ItemIsUserCheckable| Qt.ItemIsEnabled)
self.model.setItem(count1,count2,item)
count2+=1
else:
item = QStandardItem(column)
self.model.setItem(count1,count2,item)
count2+=1
count1+=1
clipTableView.setModel(self.model)
layout = QVBoxLayout()
layout.addWidget(clipTableView)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Related

How to use QSortFilterProxyModel to do data aggregation

I used QSortFilterProxyModel to filter the model data and display it in a tableView.
Then I want to aggregate the filtered data, so I created a new tableView2 to display the filtered data aggregation.
I created a new QSortFilterProxyModel to aggregate the data, but I don’t know how to get the source data, and then increase the data
But with my current knowledge of QSortFilterProxyModel, I seem to be unable to start.
import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
class FilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, headers):
super(FilterProxyModel, self).__init__(parent=None)
self._headers = headers
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
if self.columnCount() < section+1:
return QtCore.QVariant()
else:
return self._headers[section]
return super().headerData(section, orientation, role)
def data(self, index, role=None):
if role == Qt.DisplayRole:
if index.row() < 1:
return 1
else:
return QtCore.QVariant()
def rowCount(self, parent=None, *args, **kwargs):
return 1
def columnCount(self, parent=None, *args, **kwargs):
return len(self._headers)
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, datas):
super(TableModel, self).__init__()
self._datas = datas
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._datas[index.row()][index.column()]
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._datas)
def columnCount(self, parent=QtCore.QModelIndex()):
if self._datas:
return len(self._datas[0])
else:
return 0
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.datas = [('a1', 'b1', 'c'), ('a2', 'b1', 'c'), ('a3', 'b1', 'c'), ('a4', 'b1', 'c')]*25
self.model = TableModel(self.datas)
self.proxy_model = QtCore.QSortFilterProxyModel()
self.proxy_model.setSourceModel(self.model)
self.proxy_model.setFilterRegExp(QtCore.QRegExp('a1|a3'))
self.proxy_model.setFilterKeyColumn(0)
self.proxy_model2 = FilterProxyModel(['a1', 'a3'])
self.proxy_model2.setSourceModel(self.model)
self.proxy_model2.setFilterRegExp(QtCore.QRegExp('a1|a3'))
self.proxy_model2.setFilterKeyColumn(0)
self.treeview = QtWidgets.QTableView()
self.treeview.setModel(self.proxy_model)
self.treeview2 = QtWidgets.QTableView()
self.treeview2.setModel(self.proxy_model2)
layout = QtWidgets.QVBoxLayout(self)
hlayout = QtWidgets.QHBoxLayout()
hlayout.addWidget(self.treeview)
hlayout.addWidget(self.treeview2)
layout.addLayout(hlayout)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main = MainWidget()
main.show()
sys.exit(app.exec_())
This is the effect I want to achieve according to the specified head key (a1, a3) to perform data aggregation:
A proxymodel is used to map items but in this case the goal is to count, so using a proxymodel seems unnecessary to me. Instead it is easier to use match() to count and connect the signals that indicate some change in the model to recalculate it.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, datas):
super(TableModel, self).__init__()
self._datas = datas
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
return self._datas[index.row()][index.column()]
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._datas)
def columnCount(self, parent=QtCore.QModelIndex()):
if self._datas:
return len(self._datas[0])
else:
return 0
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.datas = [
("a1", "b1", "c"),
("a2", "b1", "c"),
("a3", "b1", "c"),
("a4", "b1", "c"),
] * 25
self.model = TableModel(self.datas)
self.proxy_model = QtCore.QSortFilterProxyModel()
self.proxy_model.setSourceModel(self.model)
self.proxy_model.setFilterRegExp(QtCore.QRegExp("a1|a3"))
self.proxy_model.setFilterKeyColumn(0)
self.model2 = QtGui.QStandardItemModel()
self.model2.setHorizontalHeaderLabels(["a1", "a3"])
self.view1 = QtWidgets.QTableView()
self.view1.setModel(self.proxy_model)
self.view2 = QtWidgets.QTableView()
self.view2.setModel(self.model2)
layout = QtWidgets.QVBoxLayout(self)
hlayout = QtWidgets.QHBoxLayout()
hlayout.addWidget(self.view1)
hlayout.addWidget(self.view2)
layout.addLayout(hlayout)
self.model.dataChanged.connect(self.update_model2)
self.model.rowsInserted.connect(self.update_model2)
self.model.rowsRemoved.connect(self.update_model2)
self.model.columnsInserted.connect(self.update_model2)
self.model.columnsRemoved.connect(self.update_model2)
self.model.modelReset.connect(self.update_model2)
self.update_model2()
def update_model2(self):
self.model2.setRowCount(0)
column = 0
for i in range(self.model2.columnCount()):
header_item = self.model2.horizontalHeaderItem(i)
if header_item is None:
return
text = header_item.text()
indexes = self.model.match(
self.model.index(0, column),
QtCore.Qt.DisplayRole,
text,
hits=-1,
flags=QtCore.Qt.MatchExactly,
)
item = QtGui.QStandardItem()
item.setData(len(indexes), QtCore.Qt.DisplayRole)
self.model2.setItem(0, i, item)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main = MainWidget()
main.show()
sys.exit(app.exec_())

How can dynamically add headers to table in this pyqt5 app?

This code I have taken from https://bravo.hatenablog.jp/entry/2016/01/18/093059 .
In this I want to add header "Quantity" at run time if needed.
Before creating table it should ask for how many headers? If user supply 4 then create table with "Quantity" headers also. Otherwise create default headers (Three).
I have tried it but not found any solution.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Model(QAbstractItemModel):
headers = "Toppings", "Udon/Soba", "Hot/Cold"
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.items = []
def index(self, row, column, parent=QModelIndex()):
return self.createIndex(row, column, None)
def parent(self, child):
return QModelIndex()
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return len(self.headers)
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
try:
return self.items[index.row()][index.column()]
except:
return
return
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role != Qt.DisplayRole:
return
if orientation == Qt.Horizontal:
return self.headers[section]
def addRow(self, topping, menkind, hotcold):
self.beginInsertRows(QModelIndex(), len(self.items), 1)
self.items.append([topping, menkind, hotcold])
self.endInsertRows()
def removeRows(self, rowIndexes):
for row in sorted(rowIndexes, reverse=True):
self.beginRemoveRows(QModelIndex(), row, row + 1)
del self.items[row]
self.endRemoveRows()
def flags(self, index):
return super(Model, self).flags(index) | Qt.ItemIsEditable
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
self.items[index.row()][index.column()] = value
return True
return False
class View(QTreeView):
def __init__(self, parent=None):
super(View, self).__init__(parent)
self.setItemsExpandable(False)
self.setIndentation(0)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
def drawBranches(self, painter, rect, index):
return
class InputWidget(QWidget):
def __init__(self, parent=None):
super(InputWidget, self).__init__(parent)
layout = QVBoxLayout()
toppings = ("Kitsune", "Tanuki", "Tempura", "Tsukimi", "Meat", "Curry")
self.toppingInput = QComboBox()
for topping in toppings:
self.toppingInput.addItem(topping)
layout.addWidget(self.toppingInput)
self.bgrp = QGroupBox()
udon = QRadioButton('Udon')
udon.setChecked(True)
soba = QRadioButton('Soba')
btnlayout = QHBoxLayout()
btnlayout.addWidget(udon)
btnlayout.addWidget(soba)
self.bgrp.setLayout(btnlayout)
layout.addWidget(self.bgrp)
self.udonsoba = udon, soba
self.bgrp_temp = QGroupBox()
hot = QRadioButton('Warm')
hot.setChecked(True)
cold = QRadioButton('Cold')
btnlayout_temp = QHBoxLayout()
btnlayout_temp.addWidget(hot)
btnlayout_temp.addWidget(cold)
self.bgrp_temp.setLayout(btnlayout_temp)
layout.addWidget(self.bgrp_temp)
self.hotcold = hot, cold
self.addButton = QPushButton('OK')
layout.addWidget(self.addButton)
layout.addStretch()
self.setLayout(layout)
def values(self):
topping = self.toppingInput.currentText()
udonsoba = '?'
for btn in self.udonsoba:
if btn.isChecked():
udonsoba = btn.text()
break
hotcold = '?'
for btn in self.hotcold:
if btn.isChecked():
hotcold = btn.text()
break
return topping, udonsoba, hotcold
class Delegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(Delegate, self).__init__(parent)
def createEditor(self, parent, option, index):
return QLineEdit(parent)
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.DisplayRole)
editor.setText(value)
def setModelData(self, editor, model, index):
model.setData(index, editor.text())
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.view = View(self)
self.model = Model(self)
self.view.setModel(self.model)
self.view.setItemDelegate(Delegate())
self.setCentralWidget(self.view)
self.inputWidget = InputWidget()
self.inputWidget.addButton.clicked.connect(self.addItem)
self.addDock = QDockWidget('Input', self)
self.addDock.setWidget(self.inputWidget)
self.addDock.setAllowedAreas(Qt.AllDockWidgetAreas)
self.addDockWidget(Qt.RightDockWidgetArea, self.addDock)
self.addDock.hide()
toolBar = QToolBar()
self.addToolBar(toolBar)
delButton = QPushButton('Delete')
delButton.clicked.connect(self.removeItems)
toolBar.addWidget(delButton)
self.addButton = QPushButton('Add')
self.addButton.clicked.connect(self.addDock.show)
toolBar.addWidget(self.addButton)
def addItem(self):
self.model.addRow(*self.inputWidget.values())
def selectedRows(self):
rows = []
for index in self.view.selectedIndexes():
if index.column() == 0:
rows.append(index.row())
return rows
def removeItems(self):
self.model.removeRows(self.selectedRows())
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
w.raise_()
app.exec_()
if __name__ == '__main__':
main()

How to extend Qt Widgets that do not have model support by default

CustomMenu class inherits from QMenu. Its custom setData() method accepts an argument and sets it to a class variable model. I did this because QToolButton, QToolMenu and QAction do not support model/view framework. From what I know aside from all QList-QTable-QTree Views and Trees only the QComboBox does support model.
So the question: would it be possible to extend the functionality of other non-model widgets so they could be used as driven-by-model widgets too?
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os
class CustomMenu(QMenu):
def __init__(self):
super(CustomMenu, self).__init__()
self.model=None
def setModel(self, model):
self.model=model
self.pupulate()
def pupulate(self):
for item in self.model.items:
self.addAction(item)
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Row0_Column0','Row1_Column0','Row2_Column0']
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def rowCount(self, QModelIndex):
return len(self.items)
def columnCount(self, QModelIndex):
return 1
def data(self, index, role):
if not index.isValid(): return QVariant()
if role == Qt.DisplayRole:
return QVariant(self.items[index.row()])
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
if role == Qt.EditRole:
self.items[index.row()]=value
return True
return False
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
layout=QVBoxLayout(self)
self.setLayout(layout)
tablemodel=Model(self)
tableView=QTableView()
tableView.horizontalHeader().setStretchLastSection(True)
tableView.setModel(tablemodel)
layout.addWidget(tableView)
combo=QComboBox()
combo.setModel(tablemodel)
layout.addWidget(combo)
toolButton=QToolButton(self)
toolButton.setText('Tool Button')
toolButton.setPopupMode(QToolButton.InstantPopup)
toolButton.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
menu=CustomMenu()
menu.setModel(tablemodel)
toolButton.setMenu(menu)
layout.addWidget(toolButton)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
EDITED LATER: An attempt to implement a QDataWidgetMapper():
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os
class CustomMenuButton(QWidget):
def __init__(self, parent):
super(CustomMenuButton, self).__init__(parent)
self.dataMapper=QDataWidgetMapper()
layout=QVBoxLayout()
self.setLayout(layout)
toolButton=QToolButton(self)
toolButton.setText('Tool Button')
toolButton.setPopupMode(QToolButton.InstantPopup)
self.menu=QMenu(toolButton)
toolButton.setMenu(self.menu)
def setModel(self, model):
self.dataMapper.setModel(model)
for row in range(model.rowCount()):
action=self.menu.addAction('Item')
self.dataMapper.addMapping(action, row)
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Row0_Column0','Row1_Column0','Row2_Column0']
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid(): return QVariant()
if role == Qt.DisplayRole:
return QVariant(self.items[index.row()])
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
if role == Qt.EditRole:
self.items[index.row()]=value
self.dataChanged.emit(index, index)
return True
return False
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
layout=QVBoxLayout(self)
self.setLayout(layout)
tablemodel=Model(self)
tableView=QTableView()
tableView.horizontalHeader().setStretchLastSection(True)
tableView.setModel(tablemodel)
layout.addWidget(tableView)
combo=QComboBox()
combo.setModel(tablemodel)
layout.addWidget(combo)
menuButton=CustomMenuButton(self)
layout.addWidget(menuButton)
menuButton.setModel(tablemodel)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
EDIT 2: Defined a class CustomAction(QAction) with dataChanged = pyqtSignal(QModelIndex,QModelIndex)...
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os
class CustomAction(QAction):
dataChanged = pyqtSignal(QModelIndex,QModelIndex)
def __init__(self, name, parent):
super(CustomAction, self).__init__(name, parent)
class CustomMenuButton(QWidget):
def __init__(self, parent):
super(CustomMenuButton, self).__init__(parent)
self.dataMapper=QDataWidgetMapper()
layout=QVBoxLayout()
self.setLayout(layout)
toolButton=QToolButton(self)
toolButton.setText('Tool Button')
toolButton.setPopupMode(QToolButton.InstantPopup)
self.menu=QMenu(toolButton)
toolButton.setMenu(self.menu)
def setModel(self, model):
self.dataMapper.setModel(model)
for row in range(model.rowCount()):
index=model.index(row,0)
itemName=model.data(index, Qt.DisplayRole).toPyObject()
actn=CustomAction(itemName, self.menu)
self.menu.addAction(actn)
self.dataMapper.addMapping(actn, row)
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Row0_Column0','Row1_Column0','Row2_Column0']
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid(): return QVariant()
if role == Qt.DisplayRole:
return QVariant(self.items[index.row()])
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
if role == Qt.EditRole:
self.items[index.row()]=value
self.dataChanged.emit(index, index)
return True
return False
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
layout=QVBoxLayout(self)
self.setLayout(layout)
tablemodel=Model(self)
tableView=QTableView()
tableView.horizontalHeader().setStretchLastSection(True)
tableView.setModel(tablemodel)
layout.addWidget(tableView)
combo=QComboBox()
combo.setModel(tablemodel)
layout.addWidget(combo)
menuButton=CustomMenuButton(self)
layout.addWidget(menuButton)
menuButton.setModel(tablemodel)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
This is what the QDataWidgetMapper class was designed for. The following blog has a great tutorial covering exactly how to implement it: http://www.yasinuludag.com/blog/?p=98
EDIT: I should add that QDataWidgetMapper works with QWidget and its subclasses, so QAction would not apply. For classes that are not QWidget or its subclasses, you might have to implement your own custom model-view binding.

How to select QTableView row with one click

When one of the QTableView's QModelIndex is clicked I want to select an entire row of the same-row-indexes.
To accomplish this I connect QTableView's clicked signal to a custom viewClicked() method which receives the clicked QModelIndex automatically:
self.tableview=QTableView()
self.tableview.clicked.connect(self.viewClicked)
Inside of viewClicked(self, clickedIndex) I query clickedIndex's row number, its model and the total number of columns):
row=clickedIndex.row()
model=clickedIndex.model()
columnsTotal=model.columnCount(None)
Finally to select an every index in a row:
for i in range(columnsTotal): self.tableview.selectRow(row)
The problem is it is noticeably slow for Qt to process such an action.
I wonder if there is a faster way to select an entire row of indexes when one of the tableview items is clicked:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Row0_Column0','Row0_Column1','Row0_Column2']
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
def rowCount(self, parent):
return 1
def columnCount(self, parent):
return len(self.items)
def data(self, index, role):
if not index.isValid(): return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
column=index.column()
if column<len(self.items):
return QVariant(self.items[column])
else:
return QVariant()
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
tablemodel=Model(self)
self.tableview=QTableView()
self.tableview.setModel(tablemodel)
self.tableview.clicked.connect(self.viewClicked)
layout = QHBoxLayout(self)
layout.addWidget(self.tableview)
self.setLayout(layout)
def viewClicked(self, clickedIndex):
row=clickedIndex.row()
model=clickedIndex.model()
columnsTotal=model.columnCount(None)
for i in range(columnsTotal):
self.tableview.selectRow(row)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
EDITED LATER: REVISED WORKING CODE Thanks to Nejat! :
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Row0_Column0','Row0_Column1','Row0_Column2']
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
def rowCount(self, parent):
return 1
def columnCount(self, parent):
return len(self.items)
def data(self, index, role):
if not index.isValid(): return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
column=index.column()
if column<len(self.items):
return QVariant(self.items[column])
else:
return QVariant()
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
tablemodel=Model(self)
self.tableview=QTableView()
self.tableview.setModel(tablemodel)
self.tableview.clicked.connect(self.viewClicked)
self.tableview.setSelectionBehavior(QTableView.SelectRows)
layout = QHBoxLayout(self)
layout.addWidget(self.tableview)
self.setLayout(layout)
def viewClicked(self, clickedIndex):
row=clickedIndex.row()
model=clickedIndex.model()
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
You can use setSelectionBehavior function of QTableView to set the selection behavior to QTableView.SelectRows :
self.tableview.setSelectionBehavior(QTableView.SelectRows);
Now when you click on an item, the entire row is selected.

Passing changing data from tab to tab

I am trying to build a program with two tabs. In Tab1 I select point coordinates (x,y) from an image into values self.a. Besides the image I also have some other UI in Tab1 (i.e. a table). Now, I want to pass the values self.a to Tab2 (without inheriting all the other stuff). Keep in mind that self.a can be constantly updated when a new point is clicked.
from PySide import QtGui, QtCore
import pandas as pd
import pyqtgraph as pg
import numpy as np
QVariant = lambda value=None: value
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
v_global_layout = QtGui.QVBoxLayout()
v_global_layout.addWidget(TabDialog())
v_global_layout.setAlignment(QtCore.Qt.AlignTop)
self.setLayout(v_global_layout)
class TabDialog(QtGui.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
tab_widget = QtGui.QTabWidget()
tab_widget.addTab(Tab1(), "1")
tab_widget.addTab(Tab2(), "2")
main_layout = QtGui.QVBoxLayout()
main_layout.addWidget(tab_widget)
self.setLayout(main_layout)
class Tab1(QtGui.QTabWidget):
def __init__(self):
super().__init__()
layout = QtGui.QHBoxLayout()
self.fig = pg.PlotWidget(name='Example: Selecting scatter points')
self.plot_area = self.fig.plotItem
self.a = pg.ScatterPlotItem(pxMode=False)
spots = []
for i in range(10):
for j in range(10):
spots.append({'pos': (1*i, 1*j), 'size': 1, 'pen': {'color': 'w', 'width': 2},
'brush': pg.intColor(i*10+j, 100)})
self.a.addPoints(spots)
self.plot_area.addItem(self.a)
self.a.dataModel = DataFrameModel()
self.a.dataTable = QtGui.QTableView()
self.a.dataTable.setModel(self.a.dataModel)
layout.addWidget(self.a.dataTable)
layout.addWidget(self.fig)
self.setLayout(layout)
self.a.array = np.zeros((0, 2))
def clicked(self, points):
for p in points:
p.setPen('b', width=2)
position = p.viewPos()
self.array = np.append(self.array, np.array([[position.x(), position.y()]]), axis=0)
c = range(len(self.array))
c = list(map(str, c))
self.dataModel.signalUpdate(self.array, columns=c)
self.dataModel.printValues() # also: print(self.array)
self.a.sigClicked.connect(clicked)
class Tab2(QtGui.QTabWidget):
def __init__(self):
super().__init__()
layout = QtGui.QHBoxLayout()
##### Here I want to use Tab1.a and not inherit all the other stuff(layout) #####
#print("values = ", Tab1.a.array) # a should change when a new point is selected in Tab1
#####################################
self.setLayout(layout)
class DataFrameModel(QtCore.QAbstractTableModel):
""" data model for a DataFrame class """
def __init__(self):
super(DataFrameModel, self).__init__()
self.df = pd.DataFrame()
def signalUpdate(self, dataIn, columns):
self.df = pd.DataFrame(dataIn, columns)
self.layoutChanged.emit()
def printValues(self):
print("DataFrame values:\n", self.df.values)
def values(self):
return self.df.values
#------------- table display functions -----------------
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QVariant()
if orientation == QtCore.Qt.Horizontal:
try:
return self.df.columns.tolist()[section]
except (IndexError, ):
return QVariant()
elif orientation == QtCore.Qt.Vertical:
try:
# return self.df.index.tolist()
return self.df.index.tolist()[section]
except (IndexError, ):
return QVariant()
def data(self, index, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QVariant()
if not index.isValid():
return QVariant()
return QVariant(str(self.df.ix[index.row(), index.column()]))
def rowCount(self, index=QtCore.QModelIndex()):
return self.df.shape[0]
def columnCount(self, index=QtCore.QModelIndex()):
return self.df.shape[1]
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
main_window = Widget()
main_window.setGeometry(100, 100, 640, 480)
main_window.show()
sys.exit(app.exec_())
In this case, you can just use signals to get the job done.
Here you are trying to access Tab1.a like a static property, when it is not one. Ideally, we should try and decouple the different widgets. We should try and keep the dependency between them to a minimum and treat each of them as ignorant and unaware of each other. The TabDialog can be the one that knows about each of these widgets and the connections between them (In this case, Tab1 and Tab2). And hence, the TabDialog can take the responsibility of communication between these widgets.
To do this, we have the two tabs as properties of the TabDialog class like so:
# Have the tabs as this dialog's class properties
self.tab1 = Tab1(image)
self.tab2 = Tab2()
tab_widget.addTab(self.tab1, "1")
tab_widget.addTab(self.tab2, "2")
In the class Tab2, let us assume that the value you want to map with Tab1.a is points_from_tab1_a:
class Tab2(QtGui.QTabWidget):
def __init__(self):
super().__init__()
layout = QtGui.QHBoxLayout()
self.points_from_tab1_a = []
self.setLayout(layout)
Now, in TabDialog, we connect the sigClicked signal of tab1.a to a method that updates tab2.points_from_tab1_a:
self.tab1.a.sigClicked.connect(self.pointChanged)
def pointChanged(self, points):
tab2.points_from_tab1_a = tab1.a
And that should do the trick. So, your full code snippet, after these changes, would look like:
from PySide import QtGui, QtCore
import pandas as pd
import pyqtgraph as pg
import numpy as np
QVariant = lambda value=None: value
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
v_global_layout = QtGui.QVBoxLayout()
v_global_layout.addWidget(TabDialog())
v_global_layout.setAlignment(QtCore.Qt.AlignTop)
self.setLayout(v_global_layout)
class TabDialog(QtGui.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
tab_widget = QtGui.QTabWidget()
# Have the tabs as this dialog's class properties
self.tab1 = Tab1(image)
self.tab2 = Tab2()
tab_widget.addTab(self.tab1, "1")
tab_widget.addTab(self.tab2, "2")
self.tab1.a.sigClicked.connect(self.pointChanged)
main_layout = QtGui.QVBoxLayout()
main_layout.addWidget(tab_widget)
self.setLayout(main_layout)
def pointChanged(self, points):
tab2.points_from_tab1_a = tab1.a
class Tab1(QtGui.QTabWidget):
def __init__(self):
super().__init__()
layout = QtGui.QHBoxLayout()
self.fig = pg.PlotWidget(name='Example: Selecting scatter points')
self.plot_area = self.fig.plotItem
self.a = pg.ScatterPlotItem(pxMode=False)
spots = []
for i in range(10):
for j in range(10):
spots.append({'pos': (1*i, 1*j), 'size': 1, 'pen': {'color': 'w', 'width': 2},
'brush': pg.intColor(i*10+j, 100)})
self.a.addPoints(spots)
self.plot_area.addItem(self.a)
self.a.dataModel = DataFrameModel()
self.a.dataTable = QtGui.QTableView()
self.a.dataTable.setModel(self.a.dataModel)
layout.addWidget(self.a.dataTable)
layout.addWidget(self.fig)
self.setLayout(layout)
self.a.array = np.zeros((0, 2))
def clicked(self, points):
for p in points:
p.setPen('b', width=2)
position = p.viewPos()
self.array = np.append(self.array, np.array([[position.x(), position.y()]]), axis=0)
c = range(len(self.array))
c = list(map(str, c))
self.dataModel.signalUpdate(self.array, columns=c)
self.dataModel.printValues() # also: print(self.array)
self.a.sigClicked.connect(clicked)
class Tab2(QtGui.QTabWidget):
def __init__(self):
super().__init__()
layout = QtGui.QHBoxLayout()
self.points_from_tab1_a = []
##### Here I want to use Tab1.a and not inherit all the other stuff(layout) #####
#print("values = ", Tab1.a.array) # a should change when a new point is selected in Tab1
#####################################
self.setLayout(layout)
class DataFrameModel(QtCore.QAbstractTableModel):
""" data model for a DataFrame class """
def __init__(self):
super(DataFrameModel, self).__init__()
self.df = pd.DataFrame()
def signalUpdate(self, dataIn, columns):
self.df = pd.DataFrame(dataIn, columns)
self.layoutChanged.emit()
def printValues(self):
print("DataFrame values:\n", self.df.values)
def values(self):
return self.df.values
#------------- table display functions -----------------
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QVariant()
if orientation == QtCore.Qt.Horizontal:
try:
return self.df.columns.tolist()[section]
except (IndexError, ):
return QVariant()
elif orientation == QtCore.Qt.Vertical:
try:
# return self.df.index.tolist()
return self.df.index.tolist()[section]
except (IndexError, ):
return QVariant()
def data(self, index, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QVariant()
if not index.isValid():
return QVariant()
return QVariant(str(self.df.ix[index.row(), index.column()]))
def rowCount(self, index=QtCore.QModelIndex()):
return self.df.shape[0]
def columnCount(self, index=QtCore.QModelIndex()):
return self.df.shape[1]
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
main_window = Widget()
main_window.setGeometry(100, 100, 640, 480)
main_window.show()
sys.exit(app.exec_())
Feel free to change it to suit your needs, using the signals and slots concept. Hope this was useful.

Categories

Resources