I started with the answer from a previous post but it doesn't seem to be working. All the rows render the same color.
I create a main window class
import sys
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.settingsTree = SettingsTree()
self.setCentralWidget(self.settingsTree)
self.locationDialog = None
# self.autoRefreshAct.setChecked(True)
# self.fallbacksAct.setChecked(True)
self.setWindowTitle("Test")
self.resize(500, 600)
self.setTreeDataObject(ItemManifest())
self.settingsTree.setItemDelegate(SelectionColorDelegate(self.settingsTree))
def setTreeDataObject(self, treeData):
self.settingsTree.setTreeDataObject(treeData)
# self.refreshAct.setEnabled(True)
# self.autoRefreshAct.setEnabled(True)
The ItemManifest is used to hold data specific to list items (not really sure this is the Qt way of doing things)
class ItemManifest(QtCore.QObject):
def __init__(self, parent=None):
super(ItemManifest, self).__init__(parent)
self.myList = [{'name': 'a', 'vid':'1', 'pid': '1'},
{'name': 'b', 'vid':'2', 'pid': '1'},
{'name': 'c', 'vid':'3', 'pid': '1'}]
This is my actual tree widget with three columns:
class SettingsTree(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(SettingsTree, self).__init__(parent)
self.setHeaderLabels(("Name", "vid", "pid"))
self.header().setResizeMode(0, QtGui.QHeaderView.Stretch)
self.header().setResizeMode(2, QtGui.QHeaderView.Stretch)
self.refreshTimer = QtCore.QTimer()
self.refreshTimer.setInterval(2000)
self.autoRefresh = False
self.groupIcon = QtGui.QIcon()
self.groupIcon.addPixmap(self.style().standardPixmap(QtGui.QStyle.SP_DirClosedIcon),
QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.groupIcon.addPixmap(self.style().standardPixmap(QtGui.QStyle.SP_DirOpenIcon),
QtGui.QIcon.Normal, QtGui.QIcon.On)
self.keyIcon = QtGui.QIcon()
self.keyIcon.addPixmap(self.style().standardPixmap(QtGui.QStyle.SP_FileIcon))
self.refreshTimer.timeout.connect(self.refresh)
def setTreeDataObject(self, treeData):
self.treeData = treeData
self.clear()
if self.treeData is not None:
self.treeData.setParent(self)
self.refresh()
if self.autoRefresh:
self.refreshTimer.start()
else:
self.refreshTimer.stop()
def sizeHint(self):
return QtCore.QSize(800, 600)
def setAutoRefresh(self, autoRefresh):
self.autoRefresh = autoRefresh
if self.treeData is not None:
if self.autoRefresh:
self.refresh()
self.refreshTimer.start()
else:
self.refreshTimer.stop()
def refresh(self):
if self.treeData is None:
return
# The signal might not be connected.
# try:
# self.itemChanged.disconnect(self.updateSetting)
# except:
# pass
self.updateChildItems(None)
# self.itemChanged.connect(self.updateSetting)
def event(self, event):
if event.type() == QtCore.QEvent.WindowActivate:
if self.isActiveWindow() and self.autoRefresh:
self.refresh()
return super(SettingsTree, self).event(event)
'''on change to settings update tree'''
def updateChildItems(self, parent):
dividerIndex = 0
for printer in self.treeData.myList:
childIndex = self.findChild(parent, printer['name'], 0)
if childIndex == -1 or childIndex >= dividerIndex:
if childIndex != -1:
child = self.childAt(parent, childIndex)
for i in range(child.childCount()):
self.deleteItem(child, i)
self.moveItemForward(parent, childIndex, dividerIndex)
else:
child = self.createItem(printer['name'], parent, dividerIndex)
child.setIcon(0, self.keyIcon)
dividerIndex += 1
else:
child = self.childAt(parent, childIndex)
child.setText(1, printer['vid'])
child.setText(2, printer['pid'])
def createItem(self, text, parent, index):
after = None
if index != 0:
after = self.childAt(parent, index - 1)
if parent is not None:
item = QtGui.QTreeWidgetItem(parent, after)
else:
item = QtGui.QTreeWidgetItem(self, after)
item.setText(0, text)
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
return item
def deleteItem(self, parent, index):
if parent is not None:
item = parent.takeChild(index)
else:
item = self.takeTopLevelItem(index)
del item
def childAt(self, parent, index):
if parent is not None:
return parent.child(index)
else:
return self.topLevelItem(index)
def childCount(self, parent):
if parent is not None:
return parent.childCount()
else:
return self.topLevelItemCount()
def findChild(self, parent, text, startIndex):
for i in range(self.childCount(parent)):
if self.childAt(parent, i).text(0) == text:
return i
return -1
def moveItemForward(self, parent, oldIndex, newIndex):
for int in range(oldIndex - newIndex):
self.deleteItem(parent, newIndex)
Create a color delegate for setting the background color of a row with text 'a' to green
class SelectionColorDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent):
super(SelectionColorDelegate, self).__init__(parent)
def initStyleOption(self, option, index):
# let the base class initStyleOption fill option with the default values
super(SelectionColorDelegate,self).initStyleOption(option, index)
# override what you need to change in option
if index.data() == 'a':
backColor = QtGui.QColor("green")
option.palette.setColor(QtGui.QPalette.Base, backColor)
option.backgroundBrush = backColor
make it work:
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
Neither of my attempts at setting background color appear to be working.
After learning further on Qt I refactored the code to use the model/view relationship. The model is inherited from QtCore.QAbstractItemModel and implements the data method as follows:
class MyTreeModel(QtCore.QAbstractItemModel):
def data(self, index, role):
if not index.isValid():
return None
item = index.internalPointer()
if role == QtCore.Qt.BackgroundRole:
if item.status.status == 'Not installed':
return QtGui.QBrush(QtCore.Qt.lightGray)
if item.status.head_test_failed:
return QtGui.QBrush(QtCore.Qt.red)
if item.status.status == 'failure':
return QtGui.QBrush(QtCore.Qt.red)
if item.status.status == 'running':
return QtGui.QBrush(QtCore.Qt.green)
return QtGui.QBrush(QtCore.Qt.transparent)
if role != QtCore.Qt.DisplayRole:
return None
return item.data(index.column())
This way I was able to use to standard TreeView (or any other view for that matter). The role == QtCore.Qt.BackgroundRole tells the model that this is a formatting request.
Related
I need auto completion in a table. So far, I could make it work that I get the same list for the entire table.
However, I need a dynamic list for each cell. How can I get update the list when I move to a new position in the cell?
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class mainWindow(QMainWindow):
def __init__(self, parent = None):
super(mainWindow, self).__init__(parent)
self.initUI()
def initUI(self):
self.center_window = centerWindow(parent=self)
self.setCentralWidget(self.center_window)
class centerWindow(QWidget):
def __init__(self, parent=None):
super(centerWindow, self).__init__(parent)
table = QTableWidget()
table.setItemDelegate(TableItemCompleter())
table.setRowCount(5)
table.setColumnCount(1)
vbox = QVBoxLayout(self)
vbox.addWidget(table)
self.setLayout(vbox)
class TableItemCompleter(QStyledItemDelegate):
def __init__(self, parent = None):
super(TableItemCompleter, self).__init__(parent)
def createEditor(self, parent, styleOption, index):
editor = QLineEdit(parent)
completion_ls = ['aaa', 'bbb', 'ccc']
autoComplete = QCompleter(completion_ls)
editor.setCompleter(autoComplete)
return editor
if __name__ == '__main__':
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
else:
print('QApplication instance already exists: %s' % str(app))
ex = mainWindow()
ex.show()
sys.exit(app.exec_())
To make it more clear. Instead of having the completion_ls list in the TableItemCompleter, I'd like to add something like this:
table.setItem(row, column) #add my new auto completion list
QCompleter can be established a model that uses as a source for the autocomplete, we could pass the QModelIndex model that provides the method createEditor(self, parent, option, index) through index.model() but the problem is that you can only take a column and is not what is desired.
Then we must do a conversion of the tablemodel to a listmodel, and then we must filter the repeated elements so that the completer shows unique elements, one way to do them is through proxies, classes that inherit from QAbstractProxyModel, the scheme is as follows:
TableModel ----> ReadTable2ListProxyModel ----> DuplicateFilterProxyModel
En la siguiente sección muestros las clases:
class ReadTable2ListProxyModel(QIdentityProxyModel):
def columnCount(self, parent=QModelIndex()):
return 1
def rowCount(self, parent=QModelIndex()):
return self.sourceModel().rowCount() * self.sourceModel().columnCount()
def mapFromSource(self, sourceIndex):
if sourceIndex.isValid() and sourceIndex.column() == 0\
and sourceIndex.row() < self.rowCount():
r = sourceIndex.row()
c = sourceIndex.column()
row = sourceIndex.model().columnCount() * c + r
return self.index(row, 0)
return QModelIndex()
def mapToSource(self, proxyIndex):
r = proxyIndex.row() / self.sourceModel().columnCount()
c = proxyIndex.row() % self.sourceModel().columnCount()
return self.sourceModel().index(r, c)
def index(self, row, column, parent=QModelIndex()):
return self.createIndex(row, column)
class DuplicateFilterProxyModel(QSortFilterProxyModel):
def setSourceModel(self, model):
model.dataChanged.connect(lambda: self.invalidate())
QSortFilterProxyModel.setSourceModel(self, model)
def filterAcceptsRow(self, row, parent):
value = self.sourceModel().index(row, self.filterKeyColumn())\
.data(self.filterRole())
if value is None:
return False
if row == 0:
return True
for i in reversed(range(0, row)):
val = self.sourceModel().index(i, self.filterKeyColumn())\
.data(self.filterRole())
if val == value:
return False
return True
Then the conversion is established in the delegate:
class TableItemCompleter(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
completer = QCompleter(parent)
proxy1 = ReadTable2ListProxyModel(parent)
proxy2 = DuplicateFilterProxyModel(parent)
proxy1.setSourceModel(index.model())
proxy2.setSourceModel(proxy1)
completer.setModel(proxy2)
editor.setCompleter(completer)
return editor
In the following link you will find an example, and in the following image the operation is illustrated, in the first widget the table is observed, in the second the conversion to list and in the third the list eliminating duplicate elements.
If you want each item to have a list what can be done is to store the list in each item through a role that is not in use as Qt.UserRole through the setData() method, and in the delegate through the method data() of the QModelIndex:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import random
class TableItemCompleter(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
completion_ls = index.data(Qt.UserRole) # get list
completer = QCompleter(completion_ls, parent)
editor.setCompleter(completer)
return editor
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
lay = QHBoxLayout(self)
tv = QTableWidget(3, 4, self)
lay.addWidget(tv)
l = ["AA", "AB", "AC", "AD", "BA", "BB", "BC"]
for i in range(tv.rowCount()):
for j in range(tv.columnCount()):
it = QTableWidgetItem(f"{i},{j}")
tv.setItem(i, j, it)
it.setData(Qt.UserRole, random.sample(l, 3)) # set list
tv.setItemDelegate(TableItemCompleter(tv))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
How can I change the text displayed in the second column of the rows selected in the treeview when the user hits 'Edit' in the UI? I'm using python and pyside but I'm not clear on how to do this.
What I want to happen is: When user clicks Edit in the UI i would like it to change the text of the selected Treeview Rows second columns. You can just change the text to say 'Hello' or something simple.
import sys
from PySide import QtGui, QtCore
class SortModel(QtGui.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(SortModel, self).__init__(*args, **kwargs)
def lessThan(self, left, right):
leftData = self.sourceModel().data(left)
rightData = self.sourceModel().data(right)
if leftData:
leftData = leftData.lower()
if rightData:
rightData = rightData.lower()
print('L:', leftData, 'R:', rightData)
return leftData < rightData
class Browser(QtGui.QDialog):
def __init__(self, parent=None):
super(Browser, self).__init__(parent)
self.initUI()
def initUI(self):
self.resize(200, 300)
self.setWindowTitle('Assets')
self.setModal(True)
self.results = ""
self.uiItems = QtGui.QTreeView()
self.uiItems.setAlternatingRowColors(True)
self.uiItems.setSortingEnabled(True)
self.uiItems.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.uiItems.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.uiItems.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
self.uiItems.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.uiItems.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self._model = self.create_model(self)
self._spmodel = SortModel(self)
self._spmodel.setSourceModel(self._model)
self._spmodel.setDynamicSortFilter(False)
self.uiItems.setModel(self._spmodel)
self.uiEdit = QtGui.QPushButton('Edit')
grid = QtGui.QGridLayout()
grid.setContentsMargins(0, 0, 0, 0)
grid.addWidget(self.uiItems, 0, 0)
grid.addWidget(self.uiEdit, 1, 0)
self.setLayout(grid)
self.uiItems.doubleClicked.connect(self.doubleClickedItem)
self.show()
def doubleClickedItem(self, idx):
name = idx.data(role=QtCore.Qt.DisplayRole)
model = idx.model()
model.setData(idx, 'great', role=QtCore.Qt.DisplayRole)
def create_model(self, parent):
items = [
'Cookie dough',
'Hummus',
'Spaghetti',
'Dal makhani',
'Chocolate whipped cream'
]
model = QtGui.QStandardItemModel()
model.setHorizontalHeaderLabels(['Name', 'Great'])
for item in items:
root = []
parentNode = QtGui.QStandardItem(item)
root.append(parentNode)
# add child row with 2 columns
for i in range(3):
row = []
col1 = QtGui.QStandardItem()
col1.setData('COLUMN 1', role=QtCore.Qt.DisplayRole)
row.append(col1)
col2 = QtGui.QStandardItem()
col2.setData('COLUMN 2', role=QtCore.Qt.DisplayRole)
row.append(col2)
parentNode.appendRow(row)
model.appendRow(root)
return model
def showEvent(self, event):
geom = self.frameGeometry()
geom.moveCenter(QtGui.QCursor.pos())
self.setGeometry(geom)
super(Browser, self).showEvent(event)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
# self.hide()
self.close()
event.accept()
else:
super(Browser, self).keyPressEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
ex = Browser()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Connect the button’s clicked signal, retrieve the selected items via model.selectedIndexes, iterate over them and only handle those with .column()== 1 as in your doubleClickedItem function.
You can also change the SelectionBehavior to only select single items instead of complete rows.
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.
I am new to pyside and probably do not understand quite well the concepts of the GUI libraries. I have a specific question to the following code (python3.3), in which I create a view of a table, and a button. Once the button is pressed, data is added to the date of the widget, and the table should be redrawn. But the table does not update itself with the added contents.
How can I fix the code so the table updates/redraws itself after I press the 'Do_something' button?
For additional suggestions on my code I would be very thankful!
from PySide.QtCore import *
from PySide.QtGui import *
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# this list represents the database
self.data_list = [
('John', 'Connor', 'terminated'),
('Buzz', 'Lightyea', 'animated')
]
self.header = ['name', 'lastname', 'extra']
# set basic window stuff
self.setGeometry(300, 200, 970, 450)
self.setWindowTitle("Main Stock Overview")
# add the model to the view
self.table_model = MyTableModel(self, self.data_list, self.header)
self.table_view = QTableView()
self.table_view.setModel(self.table_model)
# add the table to the layout
layout = QVBoxLayout(self)
layout.addWidget(self.table_view)
btn1 = QPushButton("Do_something", self)
btn1.clicked.connect(self.do_something)
# add some button to the layout
action_layout = QHBoxLayout(self)
action_layout.addStretch(1)
action_layout.addWidget(btn1)
layout.addLayout(action_layout)
self.setLayout(layout)
def do_something(self):
# update the 'database'
self.data_list.append(('Harry','Potter','wizated'))
print("data has been updated: ", self.data_list)
# required to be redrawn here
index1 = self.table_model.createIndex(0,0)
index2 = self.table_model.createIndex(self.table_model.rowCount(self)+1, self.table_model.columnCount(self))
self.table_model.dataChanged.emit(index1, index2)
class MyTableModel(QAbstractTableModel):
def __init__(self, parent, mylist, header, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.mylist = mylist
self.header = header
def rowCount(self, parent):
return len(self.mylist)
def columnCount(self, parent):
return len(self.mylist[0])
def data(self, index, role):
if not index.isValid():
return None
elif role != Qt.DisplayRole:
return None
return self.mylist[index.row()][index.column()]
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.header[col]
return None
app = QApplication([])
win = MyWindow()
win.show()
app.exec_()
After reading the docs a bit more it turns out that after adding new data row you need to emit the rowsInserted signal. So, the following is sufficient:
def do_something(self):
self.data_list.append(('Harry','Potter','wizated'))
newRow = len(self.data_list) - 1
self.table_model.rowsInserted.emit(QModelIndex(), newRow, newRow)
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()