![]() |
[PyQt] QTableView: scroll to top cell of the screen - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [PyQt] QTableView: scroll to top cell of the screen (/thread-38387.html) |
QTableView: scroll to top cell of the screen - random_nick - Oct-07-2022 Hi everyone! I have created a table in Qt using a QTableView widget. Cells are navigated using arrow keys. The current cell to which I have navigated using Down key has a border in red. I try to do the following. If the current cell is not visible at all or is partially visible, pressing Down should act almost like PgDn - the table should be scrolled down one page, the cell that was not previously fully visible should appear at the very top of the page (currently visible area). I achieve that by calculating Y position of the current page and scrolling to that position. However, I haven't managed yet to overcome the following problems: 1) I have to precalculate Y positions of cells because if the table scrollbar is not at the beginning, different Y values may be output - I don't know why. 2) With my algorithm, current pages are not calculated precisely. In a common scenario, rows have the same height, but I may move to rows of a different height in the future. Here's my code: import sys import PyQt5 import PyQt5.QtWidgets class TableDelegate(PyQt5.QtWidgets.QStyledItemDelegate): # akej74, https://stackoverflow.com/questions/35397943/how-to-make-a-fast-qtableview-with-html-formatted-and-clickable-cells def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) self.rowno = 0 self.colno = 0 def paint(self,painter,option,index): options = PyQt5.QtWidgets.QStyleOptionViewItem(option) self.initStyleOption(options,index) style = options.widget.style() style.drawControl(PyQt5.QtWidgets.QStyle.CE_ItemViewItem,options,painter) if index.row() == self.rowno and index.column() == self.colno: color = PyQt5.QtGui.QColor('red') pen = PyQt5.QtGui.QPen(color,2) painter.setPen(pen) # Avoid intersecting cell borders and artifacts as the result x1, y1, x2, y2 = option.rect.getCoords() option.rect.setCoords(x1+1,y1+1,x2-1,y2-1) painter.drawRect(option.rect) painter.save() painter.restore() class TableModel(PyQt5.QtCore.QAbstractTableModel): def __init__(self, data): super(TableModel, self).__init__() self._data = data def data(self,index,role): if role == PyQt5.QtCore.Qt.DisplayRole: return self._data[index.row()][index.column()] def rowCount(self,index): return len(self._data) def columnCount(self,index): return len(self._data[0]) def update(self,rowno,colno): index_ = self.index(rowno,colno) self.dataChanged.emit(index_,index_) class Table(PyQt5.QtWidgets.QTableView): def __init__(self): super().__init__() self.rownum = 200 self.colnum = 10 self.y = [] self.delegate = TableDelegate() self.setItemDelegate(self.delegate) self.vscroll = self.verticalScrollBar() self.set_row_height() def set_row_height(self,height=42): for no in range(self.rownum): self.setRowHeight(no,height) def select(self,rowno,colno): if rowno == self.delegate.rowno and colno == self.delegate.colno: return self.mymodel.update(self.delegate.rowno,self.delegate.colno) self.mymodel.update(rowno,colno) self.delegate.rowno = rowno self.delegate.colno = colno def set_all_y(self): for rowno in range(self.rownum): self.y.append(self.rowViewportPosition(rowno)) print(self.y) def get_page_y(self,y): height = self.height() print() print('Table height:',height) page_no = int(y/height) print('Page #{}'.format(page_no)) y = page_no * height mes = 'Page Y: {}'.format(y) print(mes) return y def get_scroll(self,y): max_ = self.vscroll.maximum() y = self.get_page_y(y) scrolly = int((max_*y)/(self.y[-1])) mes = 'Scrolling percentage: {}'.format(scrolly) print(mes) return scrolly def print_cell(self): mes = '"' + self.matrix[self.delegate.rowno][self.delegate.colno] + '"' print(mes) def set_scroll(self,y): self.vscroll.setValue(0) percent = self.get_scroll(y) self.vscroll.setValue(percent) self.print_cell() def show_row(self,rowno): y = self.y[rowno] self.set_scroll(y) def go_down(self): rowno = self.delegate.rowno colno = self.delegate.colno rowno += 1 self.select(rowno,colno) self.show_row(rowno) class App(PyQt5.QtWidgets.QMainWindow): def __init__(self): super().__init__() self.table = Table() self.setCentralWidget(self.table) self.setGeometry(300,200,800,600) PyQt5.QtWidgets.QShortcut(PyQt5.QtGui.QKeySequence('Down'),self).activated.connect(self.table.go_down) def fill(self): self.table.matrix = [] for i in range(self.table.rownum): row = [] for j in range(self.table.colnum): mes = 'Row {}. Column {}'.format(i+1,j+1) row.append(mes) self.table.matrix.append(row) self.table.mymodel = TableModel(self.table.matrix) self.table.setModel(self.table.mymodel) if __name__ == '__main__': exe = PyQt5.QtWidgets.QApplication(sys.argv) app = App() app.fill() app.table.set_all_y() app.show() exe.exec_()Any idea what's wrong with it? RE: QTableView: scroll to top cell of the screen - Axel_Erfurt - Oct-07-2022 You can do something like that, but it woks only using Ctrl+Down Key It scrolls selected row to top class Table(PyQt5.QtWidgets.QTableView): def __init__(self): super().__init__() self.rownum = 200 self.colnum = 10 self.y = [] self.delegate = TableDelegate() self.setItemDelegate(self.delegate) self.vscroll = self.verticalScrollBar() self.set_row_height() def keyPressEvent(self, event): if event.key() == PyQt5.QtCore.Qt.Key_Down: print("down key") index = self.currentIndex() self.scrollTo(index, PyQt5.QtWidgets.QAbstractItemView.PositionAtTop) super().keyPressEvent(event) .... RE: QTableView: scroll to top cell of the screen - random_nick - Oct-08-2022 (Oct-07-2022, 09:42 AM)Axel_Erfurt Wrote: You can do something like thatThanks! Looks like I've got it. |