Hey @PedanticHacker thanks for the post and welcome to the forum. Sorry for the delay in replying – this wasn’t so simple to figure out (more the logic than Qt specific stuff).
If I understand your problem correctly, you want the user to basically “overwrite” previous moves in the list at any time, by selecting a row and then adding the move? If a row is selected and there are subsequent rows then you want to delete the following.
The tricky part really is how to handle the current white/black state. I’ve gone for the following –
- if a row is selected all subsequent rows are deleted.
- if the next move is a white move, it will always add a new row after the selected row
- if the next move is a black move, it will always replace the black move on the selected row
The following is a fully-working example. I’ve defined a simply Move
class and a move generator, which toggles between white/black turns and returns positions that follow chess rules (I think, I don’t really know).
from PyQt5.QtWidgets import QTableWidget, QVBoxLayout, QWidget, QPushButton, QTableWidgetItem, QMainWindow, QApplication
from random import choice
WHITE = 0
BLACK = 1
class Move:
def __init__(self, turn, move):
self.turn = turn
self.move = move
def __str__(self):
return "{} {}".format(self.turn, self.move)
def chess_moves():
"""
Generator producing chess moves.
"""
turn = WHITE
while True:
move_str = choice('KQRBN ') + choice('abcdefgh') + choice('12345678')
yield Move(turn, move_str)
# Cycle the white/black turns.
if turn == WHITE:
turn = BLACK
else:
turn = WHITE
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.moves = chess_moves()
self.moves_widget = QTableWidget()
self.moves_widget.setColumnCount(2)
self.moves_widget.setHorizontalHeaderLabels(['White','Black'])
self.btn = QPushButton("Move")
self.btn.pressed.connect(self.add_chess_move)
layout = QVBoxLayout()
layout.addWidget(self.moves_widget)
layout.addWidget(self.btn)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
def add_chess_move(self):
move = next(self.moves)
selected = self.moves_widget.selectedIndexes()
if selected:
row = selected[0].row() # Get row of first selected item.
# Row is selected, delete all after.
while self.moves_widget.rowCount() > row + 1:
self.moves_widget.removeRow(row + 1)
# Clear selection so we don't re-delete next time.
self.moves_widget.clearSelection()
row = self.moves_widget.rowCount()-1
# White always adds a new row.
if move.turn == WHITE:
row += 1
self.moves_widget.setRowCount(row+1)
item = QTableWidgetItem(move.move)
column = move.turn
self.moves_widget.setItem(row, move.turn, item)
self.moves_widget.scrollToBottom()
app = QApplication([])
w = MainWindow()
w.show()
app.exec_()
As shown, for deleting rows the simplest option is to delete the row after the current one repeatedly, until the table is empty. Since the subsequent moves will “scroll up” by doing this you empty the list after that point.
Saying all this, I wonder if you’ve come across the model views? Using these you could represent your list of moves as a simple Python list and avoid all this manipulation in the table itself – you could just chop off the end of a list and be done.
Hope this helps!