问题
Consider a QTableWidget and two buttons "move up" and "move down". Clicking on move up, the current row should move up one row, analogously for "move down".
What's the easiest way to implement the corresponding move up and move down functions?
回答1:
I have revised my answer because it did not have enough detail previously
The process involves connecting your buttons to a slot (or slots) that will look at the current selection, and move them by taking them from the view, and inserting them into new locations.
The following example is actually using a QTableView + QStandardItemModel. The reason is because QTableWidget is much more limited since you can only use methods from the widget. Its a lot easier to be able to work directly with the model and selection model. Although, it is possible to rework this example for a QTableWidget if you use takeItem()
multiple times to build up each row...
Here is a fully working example:
from PyQt4 import QtCore, QtGui
from functools import partial
class Dialog(QtGui.QDialog):
DOWN = 1
UP = -1
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.resize(800,600)
self.table = QtGui.QTableView(self)
self.table.setSelectionBehavior(self.table.SelectRows)
self.model = QtGui.QStandardItemModel(20, 6, self)
self.table.setModel(self.model)
self.upBtn = QtGui.QPushButton('Up', self)
self.downBtn = QtGui.QPushButton('Down', self)
self.mainLayout = QtGui.QVBoxLayout(self)
self.mainLayout.addWidget(self.table)
self.buttonLayout = QtGui.QHBoxLayout()
self.buttonLayout.addWidget(self.upBtn)
self.buttonLayout.addWidget(self.downBtn)
self.mainLayout.addLayout(self.buttonLayout)
self.upBtn.clicked.connect(partial(self.moveCurrentRow, self.UP))
self.downBtn.clicked.connect(partial(self.moveCurrentRow, self.DOWN))
self._initTable()
def _initTable(self):
for row in xrange(self.model.rowCount()):
for col in xrange(self.model.columnCount()):
item = QtGui.QStandardItem('%d_%d' % (row+1, col+1))
self.model.setItem(row, col, item)
def moveCurrentRow(self, direction=DOWN):
if direction not in (self.DOWN, self.UP):
return
model = self.model
selModel = self.table.selectionModel()
selected = selModel.selectedRows()
if not selected:
return
items = []
indexes = sorted(selected, key=lambda x: x.row(), reverse=(direction==self.DOWN))
for idx in indexes:
items.append(model.itemFromIndex(idx))
rowNum = idx.row()
newRow = rowNum+direction
if not (0 <= newRow < model.rowCount()):
continue
rowItems = model.takeRow(rowNum)
model.insertRow(newRow, rowItems)
selModel.clear()
for item in items:
selModel.select(item.index(), selModel.Select|selModel.Rows)
if __name__ == "__main__":
app = QtGui.QApplication([])
d = Dialog()
d.show()
d.raise_()
app.exec_()
The init is simple and just sets up the table, model, and buttons. We connect both buttons to the same method using functools.partial
, which is really convenient for wrapping up the same function call with different args. Then the table is just filled with 20x6 data.
When a button is clicked, we make sure they have selected rows. For each selected row, we resolve its item (for re-selection later once it has moved), and determine the new row number by either adding or subtracting one. We also make sure its within the valid range to move, otherwise we skip it. Finally, we call takeRow()
to remove the entire row as a list of indexes, and then insert that row back into the new row number. After that loop, we use the items we saved to look up the new indexes and reselect them again.
回答2:
I managed to do it using only QTableWidget
, here is a full example:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class mtable(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.move_up = QtGui.QAction("Move_Up", self)
self.connect(self.move_up, QtCore.SIGNAL('triggered()'), self.moveUp)
self.move_down = QtGui.QAction("Move_Down",self)
self.connect(self.move_down, QtCore.SIGNAL('triggered()'), self.moveDown)
self.toolbar = self.addToolBar('Toolbar')
self.toolbar.addAction(self.move_up)
self.toolbar.addAction(self.move_down)
##Init Table
self.table = QtGui.QTableWidget(4,3)
for i in range(0,4):
for j in range(0,4):
self.table.setItem(i,j,QtGui.QTableWidgetItem("a_"+str(i)+str(j)))
self.setCentralWidget(self.table)
def moveDown(self):
row = self.table.currentRow()
column = self.table.currentColumn();
if row < self.table.rowCount()-1:
self.table.insertRow(row+2)
for i in range(self.table.columnCount()):
self.table.setItem(row+2,i,self.table.takeItem(row,i))
self.table.setCurrentCell(row+2,column)
self.table.removeRow(row)
def moveUp(self):
row = self.table.currentRow()
column = self.table.currentColumn();
if row > 0:
self.table.insertRow(row-1)
for i in range(self.table.columnCount()):
self.table.setItem(row-1,i,self.table.takeItem(row+1,i))
self.table.setCurrentCell(row-1,column)
self.table.removeRow(row+1)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
tb = mtable()
tb.show()
sys.exit(app.exec_())
来源:https://stackoverflow.com/questions/9166087/move-row-up-and-down-in-pyqt4