In PySide, is there a way to selected a few hundred treeview items without having to manually go row by row and select them? The problem with this method is the UI updates each time a new row is selected causing the application to freeze up while it finishes the method. Is there a way I can just pass the selection-model a list of all the rows I want selected?
My treeview has hundres of rows and four columns, but the treeview is set to select entire rows, not cells.
model = self.uiFilesList.model()
rows = self.selectedFileItems.selectedRows()
self.uiFilesList.selectionModel().clear()
I would expect this to work, but it doesn't.
selection = self.uiFilesList.selectionModel().selection()
self.uiFilesList.selectionModel().clear()
mode = QtGui.QItemSelectionModel.Select | QtGui.QItemSelectionModel.Rows
self.uiFilesList.selectionModel().select(selection, mode)
Here is my sample project where the selection is not updating after i update the data in the mode. You'll see in my sample below, when you right-click and change the age or the jersey number the list must be repopulated in order to update the displayed data. However i attempt to store the selection before updating the list. Then i try to restore it after the list is repopulated but it doesn't appear to work.
import sys, os, random
from PySide import QtGui, QtCore
class Person(object):
def __init__(self, name, age, hair, jersey):
self.name = name
self.age = age
self.hair = hair
self.jersey = jersey
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(500, 320)
self.people = [
Person('Kevin', 10, 'Brown', 20),
Person('Marsha', 32, 'Blonde', 00),
Person('Leslie', 27, 'Black', 15),
Person('Tim', 53, 'Red', 37),
Person('Marie', 65, 'Brown', 101),
Person('Bella', 8, 'Blonde', 1)
]
self.treeview = QtGui.QTreeView()
self.treeview.setAlternatingRowColors(True)
self.treeview.setModel(QtGui.QStandardItemModel())
self.treeview.setSortingEnabled(True)
self.treeview.setRootIsDecorated(False)
self.treeview.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.treeview.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.treeview.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.treeview.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.treeview.customContextMenuRequested.connect(self.open_menu)
self.selectedItems = self.treeview.selectionModel()
self.setCentralWidget(self.treeview)
self.populate_list()
# actions
self.actRandomizeAges = QtGui.QAction('Randomize Ages', self)
self.actRandomizeJerseys = QtGui.QAction('Randomize Jersey Numbers', self)
# menu
self.cmenu = QtGui.QMenu()
self.cmenu.addAction(self.actRandomizeAges)
self.cmenu.addAction(self.actRandomizeJerseys)
# connections
self.actRandomizeAges.triggered.connect(self.randomize_ages)
self.actRandomizeJerseys.triggered.connect(self.randomize_jerseys)
def open_menu(self, position):
self.cmenu.exec_(self.treeview.viewport().mapToGlobal(position))
def randomize_ages(self):
rows = self.selectedItems.selectedRows()
for i, item in enumerate(rows):
obj = item.data(role=QtCore.Qt.UserRole)
obj.age = random.randint(0, 70)
self.populate_list()
def randomize_jerseys(self):
rows = self.selectedItems.selectedRows()
for i, item in enumerate(rows):
obj = item.data(role=QtCore.Qt.UserRole)
obj.jersey = random.randint(1, 100)
self.populate_list()
def populate_list(self):
selection = self.treeview.selectionModel().selection()
flags = QtGui.QItemSelectionModel.Select
self.treeview.selectionModel().clear()
model = self.treeview.model()
model.clear()
model.setHorizontalHeaderLabels(['Name','Age','Hair', 'Jersey'])
for p in self.people:
# column 1
col1 = QtGui.QStandardItem()
col1.setData(p.name, role=QtCore.Qt.DisplayRole)
col1.setData(p, role=QtCore.Qt.UserRole)
# column 2
col2 = QtGui.QStandardItem()
col2.setData(p.age, role=QtCore.Qt.DisplayRole)
if p.age > 30:
col2.setData(QtGui.QBrush(QtGui.QColor(255,0,0,255)), role=QtCore.Qt.ForegroundRole)
# column 3
col3 = QtGui.QStandardItem()
col3.setData(p.hair, role=QtCore.Qt.DisplayRole)
# column 4
col4 = QtGui.QStandardItem()
col4.setData(p.jersey, role=QtCore.Qt.DisplayRole)
if p.jersey > 30:
col4.setData(QtGui.QBrush(QtGui.QColor(0,0,255,255)), role=QtCore.Qt.ForegroundRole)
model.appendRow([col1, col2, col3, col4])
self.treeview.selectionModel().select(selection, flags)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
Your example doesn't work because it uses the wrong selection-flags. If you use QItemSelectionModel.Select alone, it will work correctly (and will select whole rows).
To set the selection from a list of indexes, you can create a series of QItemSelection objects which cover contiguous ranges, and merge them all into one selection.
Here is a demo script which shows how to do that:
import sys
from PySide import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Select')
self.button.clicked.connect(self.handleButton)
self.tree = QtGui.QTreeView()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.tree)
layout.addWidget(self.button)
columns = 'One Two Three Four'.split()
mod = QtGui.QStandardItemModel(self)
mod.setHorizontalHeaderLabels(columns)
for row in range(1000):
mod.appendRow((
QtGui.QStandardItem('A%s' % row),
QtGui.QStandardItem('B%s' % row),
QtGui.QStandardItem('C%s' % row),
QtGui.QStandardItem('D%s' % row),
))
self.tree.setModel(mod)
self.tree.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection)
def handleButton(self):
mod = self.tree.model()
columns = mod.columnCount() - 1
flags = QtGui.QItemSelectionModel.Select
selection = QtGui.QItemSelection()
for start, end in ((2, 15), (25, 260), (500, 996)):
start, end = mod.index(start, 0), mod.index(end, columns)
if selection.indexes():
selection.merge(QtGui.QItemSelection(start, end), flags)
else:
selection.select(start, end)
self.tree.selectionModel().clear()
self.tree.selectionModel().select(selection, flags)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(700, 50, 500, 600)
window.show()
sys.exit(app.exec_())
UPDATE:
The example in your question doesn't work because your populate_list method clears the model, which will invalidate all the indexes in the selection. So you need a method to save the current selection as a list of row-numbers (rather than model-indexes). This can then be fed to the method I gave above to re-create the selection.
If you update your example as follows, it should work as expected:
def save_selection(self):
selection = self.treeview.selectionModel().selectedRows()
blocks = []
for count, index in enumerate(sorted(selection)):
row = index.row()
if count > 0 and row == block[1] + 1:
block[1] = row
else:
block = [row, row]
blocks.append(block)
return blocks
def create_selection(self, blocks):
mod = self.treeview.model()
columns = mod.columnCount() - 1
flags = QtGui.QItemSelectionModel.Select
selection = QtGui.QItemSelection()
for start, end in blocks:
start, end = mod.index(start, 0), mod.index(end, columns)
if selection.indexes():
selection.merge(QtGui.QItemSelection(start, end), flags)
else:
selection.select(start, end)
self.treeview.selectionModel().clear()
self.treeview.selectionModel().select(selection, flags)
def populate_list(self):
selection = self.save_selection()
... # re-populate model
self.create_selection(selection)
Related
I want to make that when i click on particular cell on the QTableWidget it will block the corresponding rows and I want to return the value of each row selected into the QLineEdit.
I couldnt seem to find the solution my code only return when i click it will block the rows but not getting the value.
def click_inventorytable(self):
self.tableInventory.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
index = (self.tableInventory.selectionModel().currentIndex())
value = index.row()
list = [value]
if(len(list)==6):
self.lineproductnameinv.setText((list[1]))
self.linedescinv.setText((list[2]))
self.combocateinv.setText((list[3]))
self.linepriceinv.setText((list[4]))
self.linecurrentstock.setText((list[5]))
self.addstock.setText('')
Since the OP does not provide an MRE then I will create a simple demo of how you can implement the functionality of mapping the elements of the selected row of a table in various widgets.
The appropriate widgets must be chosen so that the user does not enter incorrect values, for example in the case of stock if a QLineEdit is used the user could enter a word which does not make sense since a number is expected so it is better to use a QSpinBox.
Also when the data is saved in the table it is not good to convert it to a string since it loses the way to differentiate them, it is better to save the value through setData() associated with the Qt::DisplayRole role.
Finally, the key to the solution is to use a QDataWidgetMapper that allows mapping parts of a model in widgets, so each time a row is selected the currentIndex of the mapper is updated and it sends the information to the editors.
from functools import cached_property
import random
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
LABELS = (
"Product ID",
"Product Name",
"Description",
"Category",
"Price",
"Stock",
)
CATEGORY_OPTIONS = (
"OPTION1",
"OPTION2",
"OPTION3",
"OPTION4",
)
def __init__(self, parent=None):
super().__init__(parent)
self.mapper.setModel(self.tableWidget.model())
self.tableWidget.selectionModel().currentChanged.connect(
self.mapper.setCurrentModelIndex
)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QHBoxLayout(central_widget)
flay = QtWidgets.QFormLayout()
lay.addLayout(flay)
lay.addWidget(self.tableWidget, stretch=1)
editors = (
self.name_edit,
self.description_edit,
self.category_combo,
self.price_edit,
self.stock_edit,
)
for i, (label, widget) in enumerate(zip(self.LABELS[1:], editors)):
flay.addRow(label, widget)
self.mapper.addMapping(widget, i)
self.fillTable()
self.resize(960, 480)
#cached_property
def tableWidget(self):
table = QtWidgets.QTableWidget(
0,
len(self.LABELS),
selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
selectionMode=QtWidgets.QAbstractItemView.SingleSelection,
)
table.setHorizontalHeaderLabels(self.LABELS)
return table
#cached_property
def name_edit(self):
return QtWidgets.QLineEdit()
#cached_property
def description_edit(self):
return QtWidgets.QLineEdit()
#cached_property
def category_combo(self):
combo = QtWidgets.QComboBox()
combo.addItems(["--Null--"] + list(self.CATEGORY_OPTIONS))
combo.setCurrentIndex(0)
return combo
#cached_property
def price_edit(self):
return QtWidgets.QDoubleSpinBox(maximum=2147483647)
#cached_property
def stock_edit(self):
return QtWidgets.QSpinBox(maximum=2147483647)
#cached_property
def mapper(self):
return QtWidgets.QDataWidgetMapper()
def fillTable(self):
self.tableWidget.setRowCount(0)
for i in range(30):
self.tableWidget.insertRow(self.tableWidget.rowCount())
values = (
i,
f"name-{i}",
f"Description-{i}",
random.choice(self.CATEGORY_OPTIONS),
random.uniform(100, 2000),
random.randint(0, 100),
)
for j, value in enumerate(values):
it = QtWidgets.QTableWidgetItem()
it.setData(QtCore.Qt.DisplayRole, value)
it.setFlags(it.flags() & ~QtCore.Qt.ItemIsEditable)
self.tableWidget.setItem(i, j, it)
def main():
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
I have two tables. I want to get the selected value in table1 and put it in table2.
For example, if you select 1, table2
I want the whole value of row 1 to be entered and the next row 5 to be added to the row 5.
In conclusion, I would like to make table1 show the selected row value in table2.
I do not know exactly how to load the selected table1 value, but I think it would be better to append one value to QStandardItemModel in def table1_DoubleClicked (self): using self.serch.table.setModel in table2. How can I do it?
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.resize(500, 500)
self.Table1()
self.Table2()
self.Layout()
def Table1(self):
self.select_guorpbox = QGroupBox()
self.select_guorpbox.setTitle("Article 1")
self.columncount = 10
self.rowcount = 10
self.select_table_model = QStandardItemModel(self.rowcount,self.columncount)
for i in range(self.rowcount):
for j in range(self.columncount):
table = QStandardItem('test [{},{}]'.format(i,j))
self.select_table_model.setItem(i,j,table)
table.setTextAlignment(Qt.AlignCenter)
self.TextFilter = QSortFilterProxyModel()
self.TextFilter.setSourceModel(self.select_table_model)
self.TextFilter.setFilterKeyColumn(2)
self.SerchLineEdit = QLineEdit()
self.SerchLineEdit.textChanged.connect(self.TextFilter.setFilterRegExp)
self.select_table = QTableView()
self.select_table.setModel(self.TextFilter)
self.select_table.setColumnWidth(1, 150)
self.select_table.setColumnWidth(2, 300)
self.select_table.setEditTriggers(QTableView.NoEditTriggers)
self.select_table.setSelectionBehavior(QTableView.SelectRows)
self.select_table.setContextMenuPolicy(Qt.CustomContextMenu)
self.select_table.doubleClicked.connect(self.table1_DoubleClicked)
self.select_table.customContextMenuRequested.connect(self.table1_CustomContextMenu)
# column auto sort
# self.select_table.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
# self.select_table.resizeColumnsToContents()
v = QVBoxLayout()
v.addWidget(self.select_table)
self.select_guorpbox.setLayout(v)
def Table2(self):
self.serch_groupbox = QGroupBox()
self.serch_groupbox.setTitle("Article 2")
lable = QLabel("~")
lable.setFixedWidth(10)
lable.setAlignment(Qt.AlignCenter)
insertbutton = QPushButton("insert")
self.startdate = QDateEdit()
self.startdate.setDate(QDate.currentDate())
self.startdate.setFixedWidth(150)
self.startdate.setCalendarPopup(True)
self.enddate = QDateEdit()
self.enddate.setDate(QDate.currentDate())
self.enddate.setFixedWidth(150)
self.enddate.setCalendarPopup(True)
self.article_serch_button = QPushButton("ARTICL SERTCH")
self.article_serch_button.setFixedWidth(250)
self.serch_table = QTableView()
h1 = QHBoxLayout()
h1.addWidget(insertbutton)
h1.addWidget(self.startdate)
h1.addWidget(lable)
h1.addWidget(self.enddate)
h1.addWidget(self.article_serch_button)
h2 = QHBoxLayout()
h2.addWidget(self.serch_table)
v = QVBoxLayout()
v.addLayout(h1)
v.addLayout(h2)
self.serch_groupbox.setLayout(v)
def table1_DoubleClicked(self):
self.k =QItemSelectionModel().Select
def table1_CustomContextMenu(self, position):
menu = QMenu()
menu.addAction("Add")
menu.exec_(self.select_table.mapToGlobal(position))
print("?")
def Layout(self):
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.SerchLineEdit)
self.vbox.addWidget(self.select_guorpbox)
self.vbox.addWidget(self.serch_groupbox)
self.setLayout(self.vbox)
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
You could try the following, it copies the selected row from one table to the other:
def table1_DoubleClicked(self, index):
rows = []
row = []
for column_index in range(self.columncount):
cell_idx = self.select_table.model().index(index.row(), column_index)
row.append(self.select_table.model().data(cell_idx))
rows.append(row)
search_table_model = QStandardItemModel(len(rows), self.columncount)
for i in range(len(rows)):
for j in range(self.columncount):
search_table_model.setItem(i, j, QStandardItem(rows[i][j]))
self.serch_table.setModel(search_table_model)
this is the code I use to fill a table drawn in QT Designer.
Designed to be universal for any table, it works fine, but...
When I try to show a datasat containing 18 columns and ~12000 rows, it just freezes for 30 seconds or more.
So, what I am doing wrong and is there way to speed up, keeping the code still suitable for any table?
That's my code:
...blablabla...
self.connect(self, SIGNAL("set"), self.real_set)
...blablabla...
def set_table(self, table, data):
self.emit(SIGNAL('set'), table, data)
def real_set(self, table, data):
"""
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Assuming data is list of dict and table is a QTableWidget.
Get first key and get len of contents
"""
for key in data:
rows = len(data[key])
table.setRowCount(rows)
break
"""
Forbid resizing(speeds up)
"""
table.horizontalHeader().setResizeMode(QHeaderView.Fixed)
table.verticalHeader().setResizeMode(QHeaderView.Fixed)
table.horizontalHeader().setStretchLastSection(False)
table.verticalHeader().setStretchLastSection(False)
"""
Set number of columns too
"""
table.setColumnCount(len(data))
table.setHorizontalHeaderLabels(sorted(data.keys()))
"""
Now fill data
"""
for n, key in enumerate(sorted(data.keys())):
for m, item in enumerate(data[key]):
newitem = QTableWidgetItem(item)
table.setItem(m, n, newitem)
Here a test script which compares a few ways of populating a table.
The custom model is much faster, because it does not have to create all the items up front - but note that it is a very basic implementation, so does not implement sorting, editing, etc. (See Model/View Programming for more details).
from random import shuffle
from PyQt4 import QtCore, QtGui
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data, parent=None):
super(TableModel, self).__init__(parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data)
def columnCount(self, parent=None):
return len(self._data[0]) if self.rowCount() else 0
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
row = index.row()
if 0 <= row < self.rowCount():
column = index.column()
if 0 <= column < self.columnCount():
return self._data[row][column]
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.table = QtGui.QTableView(self)
self.tablewidget = QtGui.QTableWidget(self)
self.tablewidget.setSortingEnabled(True)
self.button1 = QtGui.QPushButton('Custom Model', self)
self.button1.clicked.connect(
lambda: self.populateTable('custom'))
self.button2 = QtGui.QPushButton('StandardItem Model', self)
self.button2.clicked.connect(
lambda: self.populateTable('standard'))
self.button3 = QtGui.QPushButton('TableWidget', self)
self.button3.clicked.connect(
lambda: self.populateTable('widget'))
self.spinbox = QtGui.QSpinBox(self)
self.spinbox.setRange(15000, 1000000)
self.spinbox.setSingleStep(10000)
layout = QtGui.QGridLayout(self)
layout.addWidget(self.table, 0, 0, 1, 4)
layout.addWidget(self.tablewidget, 1, 0, 1, 4)
layout.addWidget(self.button1, 2, 0)
layout.addWidget(self.button2, 2, 1)
layout.addWidget(self.button3, 2, 2)
layout.addWidget(self.spinbox, 2, 3)
self._data = []
def populateTable(self, mode):
if mode == 'widget':
self.tablewidget.clear()
self.tablewidget.setRowCount(self.spinbox.value())
self.tablewidget.setColumnCount(20)
else:
model = self.table.model()
if model is not None:
self.table.setModel(None)
model.deleteLater()
if len(self._data) != self.spinbox.value():
del self._data[:]
rows = list(range(self.spinbox.value()))
shuffle(rows)
for row in rows:
items = []
for column in range(20):
items.append('(%d, %d)' % (row, column))
self._data.append(items)
timer = QtCore.QElapsedTimer()
timer.start()
if mode == 'widget':
self.tablewidget.setSortingEnabled(False)
for row, items in enumerate(self._data):
for column, text in enumerate(items):
item = QtGui.QTableWidgetItem(text)
self.tablewidget.setItem(row, column, item)
self.tablewidget.sortByColumn(0, QtCore.Qt.AscendingOrder)
else:
self.table.setSortingEnabled(False)
if mode == 'custom':
model = TableModel(self._data, self.table)
elif mode == 'standard':
model = QtGui.QStandardItemModel(self.table)
for row in self._data:
items = []
for column in row:
items.append(QtGui.QStandardItem(column))
model.appendRow(items)
self.table.setModel(model)
self.table.setSortingEnabled(True)
self.table.sortByColumn(0, QtCore.Qt.AscendingOrder)
print('%s: %.3g seconds' % (mode, timer.elapsed() / 1000))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 1200, 800)
window.show()
sys.exit(app.exec_())
In GUI applications one comes across a situation where there is a need
to display a lot of items in a tabular or list format (for example
displaying large number of rows in a table). One way to increase the
GUI responsiveness is to load a few items when the screen is displayed
and defer loading of rest of the items based on user action. Qt
provides a solution to address this requirement of loading the data on
demand.
You can find the implementation of this technique called pagination in this link
I have a QTableView to which I want to set a QPushButton for every row. I am doing this as follows within my class derived from QWidget following an example found here:
for index in range(number_rows):
btn_sell = QPushButton("Edit", self)
btn_sell.clicked.connect(self.button_edit)
table_view.setIndexWidget(table_view.model().index(index, 4), btn_sell)
If the table is drawn and I click on one of the QPushButton the method self.button_edit is called - but which one? It does not seem that an 'event' of any sort is given to self.button_edit, so how can I find the row-index of the QPushButton that was clicked within the button_edit method?
Maybe there is a different way altogether to add a button to each row of a table?
Your event handler will look similar to this:
def handleButtonClicked(self):
button = QtGui.qApp.focusWidget()
# or button = self.sender()
index = self.table.indexAt(button.pos())
if index.isValid():
print(index.row(), index.column())
This uses the indexAt function to get the button's position.
For clarity, my script looks like this:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self,parent)
self.table = QtGui.QTableWidget()
self.table.setColumnCount(3)
self.setCentralWidget(self.table)
data1 = ['row1','row2','row3','row4']
data2 = ['1','2.0','3.00000001','3.9999999']
self.table.setRowCount(4)
for index in range(4):
item1 = QtGui.QTableWidgetItem(data1[index])
self.table.setItem(index,0,item1)
item2 = QtGui.QTableWidgetItem(data2[index])
self.table.setItem(index,1,item2)
self.btn_sell = QtGui.QPushButton('Edit')
self.btn_sell.clicked.connect(self.handleButtonClicked)
self.table.setCellWidget(index,2,self.btn_sell)
def handleButtonClicked(self):
button = QtGui.qApp.focusWidget()
# or button = self.sender()
index = self.table.indexAt(button.pos())
if index.isValid():
print(index.row(), index.column())
Which will produce a small GUI like this:
When the Edit buttons are clicked, it prints, to the console:
(0, 2)
(1, 2)
(2, 2)
(3, 2)
The first element is your row index, the second is your column (remember it is 0 based, which is why it shows 2, not 3 - despite the column headers).
Create a custom button.
class IndexedButtonWidget(QPushButton):
def __init__(self, parent=None):
super(QPushButton, self).__init__(parent=parent)
self.button_row = 0
self.button_column = 0
After that create a button object.
self.btn_sell = IndexedButtonWidget('Edit')
self.btn_sell.button_row = row
self.btn_sell.button_column = column
self.btn_sell.clicked.connect(self.handleButtonClicked)
def handleButtonClicked(self):
button = self.sender()
print(button.button_row)
print(button.button_column)
Example:
class IndexedButtonWidget(QPushButton):
def __init__(self, parent=None):
super(QPushButton, self).__init__(parent=parent)
self.button_row = 0
self.button_column = 0
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self,parent)
self.table = QtGui.QTableWidget()
self.table.setColumnCount(3)
self.setCentralWidget(self.table)
data1 = ['row1','row2','row3','row4']
data2 = ['1','2.0','3.00000001','3.9999999']
self.table.setRowCount(4)
for index in range(4):
item1 = QtGui.QTableWidgetItem(data1[index])
self.table.setItem(index,0,item1)
item2 = QtGui.QTableWidgetItem(data2[index])
self.table.setItem(index,1,item2)
self.btn_sell = IndexedButtonWidget('Edit')
self.btn_sell.button_row = index
self.btn_sell.button_column = 2
self.btn_sell.clicked.connect(self.handleButtonClicked)
self.table.setCellWidget(index,2,self.btn_sell)
def handleButtonClicked(self):
button = self.sender()
print(button.button_row)
print(button.button_column)
How can I edit the label of a horizontal header in a qtablewidget by double clicking on it?
What I have so far but does not work. So when someone doubleclicks the top header, a linedit pops up allows for text and then resets the label.
import sys
from PyQt4 import QtGui,QtCore
import generate_file
class WebGetMain(QtGui.QMainWindow,generate_file):
def __init__(self,parent=None):
super(WebGetMain,self).__init__(parent)
self.setupUi(self)
#set these values customizable before user populates the table
self.row_count = 20
self.col_count = 20
#setup actual cols,rows
self.web_get_table.setRowCount(self.row_count)
self.web_get_table.setColumnCount(self.col_count)
#allow horizontal header to be altered
#create and initialize linedit
self.linedit = QtGui.QLineEdit(parent=self.web_get_table.viewport())
self.linedit.setAlignment(QtCore.Qt.AlignTop)
self.linedit.setHidden(True)
self.linedit.editingFinished.connect(self.doneEditing)
#connect double click action on header
self.web_get_table.connect(self.web_get_table.horizontalHeader(),QtCore.SIGNAL('sectionDoubleClicked(int)'),self.on_header_doubleClicked)
#setup vertical scrollbars for adding rows
self.vBar = self.web_get_table.verticalScrollBar()
self._vBar_lastVal = self.vBar.value()
#initialize cols,rows
for column in range(0, 2):
for row in range(0, 3):
print row, column
item = QtGui.QTableWidgetItem()
self.web_get_table.setItem(row, column, item)
#scrollbar value signal to detect for scrolling
self.vBar.valueChanged.connect(self.scrollbarChanged)
def scrollbarChanged(self, val):
#initialize scrollbar
bar = self.vBar
minVal, maxVal = bar.minimum(), bar.maximum()
avg = (minVal+maxVal)/2
rowCount = self.web_get_table.rowCount()
# scrolling down
if val > self._vBar_lastVal and val >= avg:
self.web_get_table.insertRow(rowCount)
# scrolling up
elif val < self._vBar_lastVal:
lastRow = rowCount-20
empty = True
for col in xrange(self.web_get_table.columnCount()):
item = self.web_get_table.item(lastRow, col)
if item and item.text():
empty=False
break
if empty:
#remove rows when scrolling up
self.web_get_table.removeRow(lastRow)
self._vBar_lastVal = val
def doneEditing(self):
self.linedit.blockSignals(True)
self.linedit.setHidden(True)
oldname = self.web_get_table.model().dataset.field(self.sectionedit)
newname = str(self.linedit.text())
self.web_get_table.model().dataset.changeFieldName(oldname,newname)
self.linedit.setText('')
self.web_get_table.setCurrentIndex(QtCore.QModelIndex())
def on_header_doubleClicked(self,item):
self.linedit.setText(self.web_get_table.model().field(item).name)
self.linedit.setHidden(False)
self.linedit.blockSignals(False)
self.linedit.setFocus()
self.linedit.selectAll()
self.sectionedit = item
def main():
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create("Plastique"))
main_window = WebGetMain()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
To be honest, I couldn't figure out what you're trying to do with that QLineEdit. But as far as I can see, you are halfway there. Using sectionDoubleClicked signal of the horizontalHeader() is a good start. But the rest is a big question mark for me.
All you need to do is this: Get the header item with horizontalHeaderItem(index) and use text to get the value or setText to set the new value.
And you might consider QInputDialog.getText to obtain the new value from the user.
Here is a minimal example that shows this:
import sys
from PyQt4 import QtGui
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.table = QtGui.QTableWidget(5,5)
self.table.setHorizontalHeaderLabels(['1', '2', '3', '4', '5'])
self.table.setVerticalHeaderLabels(['1', '2', '3', '4', '5'])
self.table.horizontalHeader().sectionDoubleClicked.connect(self.changeHorizontalHeader)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.table)
self.setLayout(layout)
def changeHorizontalHeader(self, index):
oldHeader = self.table.horizontalHeaderItem(index).text()
newHeader, ok = QtGui.QInputDialog.getText(self,
'Change header label for column %d' % index,
'Header:',
QtGui.QLineEdit.Normal,
oldHeader)
if ok:
self.table.horizontalHeaderItem(index).setText(newHeader)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = MyWindow()
main.show()
sys.exit(app.exec_())
I found this answer from mechsin on QtCentre and it worked for me:
http://www.qtcentre.org/threads/12835-How-to-edit-Horizontal-Header-Item-in-QTableWidget
See last comment in the thread. In summary, it creates a QLineEdit in the viewport of the table header. A slot for double-clicking the header shows the line edit in the proper position and width to cover the header section. A slot for finishing the line edit hides the line edit and captures its text value for the header item.