sub classing python pyqt5 QHeaderView

42 views Asked by At

file: table.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.resize(400, 300)
        self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
        self.tableWidget = QtWidgets.QTableWidget(Dialog)
        self.tableWidget.setColumnCount(6)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        item.setText("1")
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        item.setText("2")
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        item.setText("3")
        self.tableWidget.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        item.setText("4")
        self.tableWidget.setHorizontalHeaderItem(3, item)
        item = QtWidgets.QTableWidgetItem()
        item.setText("5")
        self.tableWidget.setHorizontalHeaderItem(4, item)
        item = QtWidgets.QTableWidgetItem()
        item.setText("6")
        self.tableWidget.setHorizontalHeaderItem(5, item)
        self.verticalLayout.addWidget(self.tableWidget)

        QtCore.QMetaObject.connectSlotsByName(Dialog)

File: run_me.py

from PyQt5 import QtWidgets,QtCore,QtGui
from table import Ui_Dialog
import sys

class Run_me:
    
    def __init__(self):
        self.app = QtWidgets.QApplication(sys.argv)
        self.Dialog = QtWidgets.QDialog()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self.Dialog)
        self.Dialog.show()
        
        self.columns_min_width = []
        self.ui.tableWidget.resizeColumnsToContents()
        self.find_table_column_min_width()

        horizontal_header = self.ui.tableWidget.horizontalHeader()
        self.ui.tableWidget.setHorizontalHeader(CustomHeader(self.ui.tableWidget,self.columns_min_width))
        self.ui.tableWidget.updateGeometries()
        for column_index in range(0,len(self.columns_min_width)):
            header_item_text = self.ui.tableWidget.horizontalHeaderItem(column_index).text()
            print(header_item_text)
        
        sys.exit(self.app.exec_())

    def find_table_column_min_width(self):
        total_columns = self.ui.tableWidget.columnCount()
        self.columns_min_width = []
        for column_index in range(0,total_columns):
            column_width = self.ui.tableWidget.columnWidth(column_index)
            print(column_width)
            self.columns_min_width.append(column_width)

class CustomHeader(QtWidgets.QHeaderView):
    def __init__(self,table,columns_min_width):
        self.columns_min_width = columns_min_width
        self.total_columns = len(self.columns_min_width)
        self.header_labels = []
        for column_index in range(0,self.total_columns):
            column_text = table.horizontalHeaderItem(column_index).text()
            self.header_labels.append(column_text)
        super().__init__(QtCore.Qt.Horizontal,table)
        self.table = self.parentWidget()
        for column_index in range(0,self.total_columns):
            header_item = self.parentWidget().horizontalHeaderItem(column_index)
            header_item.setText(self.header_labels[column_index])
        self.track_move = False
        self.updateGeometries()
        
    def mousePressEvent(self,event):
        self.track_move = True
        QtWidgets.QHeaderView.mousePressEvent(self, event)
        print("Pressed")
    
    def mouseMoveEvent(self,event):
        if self.track_move:
            print("Moved while pressed")
            for column_index in range(0,self.total_columns):
                column_width = self.parentWidget().columnWidth(column_index)
                if column_width < self.columns_min_width[column_index]:
                    event.ignore()
                    return None
            QtWidgets.QHeaderView.mouseMoveEvent(self, event)
        else:
            QtWidgets.QHeaderView.mouseMoveEvent(self, event)
        
    def mouseReleaseEvent(self,event):
        self.track_move = False
        QtWidgets.QHeaderView.mouseReleaseEvent(self, event)
        print("Released")

        
if __name__ == "__main__":
    program = Run_me()

With this example i am trying to set a constraint on header section resize (column width), but as you can see there is no header visible when running run_me.py

Is there something i am missing?

1

There are 1 answers

2
Chris P On
class CustomHeader(QtWidgets.QHeaderView):
    def __init__(self,table,columns_min_width):
        self.columns_min_width = columns_min_width
        self.total_columns = len(self.columns_min_width)
        self.header_labels = []
        for column_index in range(0,self.total_columns):
            column_text = table.horizontalHeaderItem(column_index).text()
            self.header_labels.append(column_text)
        super().__init__(QtCore.Qt.Horizontal,table)
        self.table = self.parentWidget()
        for column_index in range(0,self.total_columns):
            header_item = self.parentWidget().horizontalHeaderItem(column_index)
            header_item.setText(self.header_labels[column_index])
        self.track_move = False
        self.move_stopped = False
        self.setVisible(True)
        self.updateGeometries()
        
    def mousePressEvent(self,event):
        self.track_move = True
        self.click_x = event.pos().x()
        QtWidgets.QHeaderView.mousePressEvent(self, event)
    
    def mouseMoveEvent(self,event):
        self.moved_x = event.pos().x()
        if self.track_move:
            self.move_cancelled = False
            for column_index in range(0,self.total_columns):
                column_width = self.parentWidget().columnWidth(column_index)
                if self.move_cancelled == False:
                    if column_width <= self.columns_min_width[column_index]:
                        event.ignore()
                        self.table.setColumnWidth(column_index,self.columns_min_width[column_index])
                        self.move_cancelled = True
                        self.index_cancelled = column_index
                        #QtWidgets.QHeaderView.mouseReleaseEvent(self, event)
                        self.setSectionResizeMode(column_index,QtWidgets.QHeaderView.Fixed)
                    else:
                        self.setSectionResizeMode(column_index,QtWidgets.QHeaderView.Interactive)
                else:
                    for column_index in range(0,self.total_columns):
                        column_width = self.parentWidget().columnWidth(column_index)
                        if column_index == self.index_cancelled:
                            pos = self.sectionViewportPosition(self.index_cancelled)+self.table.columnWidth(column_index)
                            if self.moved_x>pos:
                                self.setSectionResizeMode(self.index_cancelled,QtWidgets.QHeaderView.Interactive)
                                self.move_cancelled = False
            if self.move_cancelled == False:
                QtWidgets.QHeaderView.mouseMoveEvent(self, event)
        else:
            QtWidgets.QHeaderView.mouseMoveEvent(self, event)
        
    def mouseReleaseEvent(self,event):
        self.track_move = False
        QtWidgets.QHeaderView.mouseReleaseEvent(self, event)
        for column_index in range(0,self.total_columns):
            self.setSectionResizeMode(column_index,QtWidgets.QHeaderView.Interactive)

The trick was self.setVisible(True). I thought that was True by default but it isn't.

Edit: Full working example