7

enter image description here

I need to reduce the size of first column(the column with vertical headers)

How can I resize a vertical header of QTableView in PyQt4?

I have very large headers and small GUI block to show this table so, please help me!

I am able to resize all the columns but not able to figure out resizing the vertical headers?

I have tried many things like:

self.tableView.setColumnWidth(0, 30)  // only able to change the data columns in table not headers

self.tableView.verticalHeader().setResizeMode(QHeaderView.Interactive)  //Able to change the height of headers but not width of them

I want to make the headers which can be resized by the user and has no dependencies on any other parameters of the project like window size, size of other columns....

Can someone help me with this?

learncode
  • 1,105
  • 4
  • 18
  • 36
  • Have you considered using Qt Designer? – Ronikos Dec 22 '16 at 20:50
  • I am sorry I am new to this can you elaborate? How can I use Qt Designer? – learncode Dec 22 '16 at 20:51
  • Qt Designer allows you to create UIs using drag and drop elements. It is a lot easier to use than manually coding the UI and it cleans up your code. If this is for a big project, I would thoroughly recommend considering switching to Qt Designer - albeit some things still have to be coded explicitly, and ironically this is one of them – Ronikos Dec 22 '16 at 21:06

3 Answers3

8

You can limit the size of the vertical header using setMaximWidth, and use setTextElideMode to adjust how the text is cropped.

However, there is no built-in method for making the vertical header width resizable by the user. So as a work-around, you could use the resize-event to adjust the width of the vertical header to a percentage of the total width. That way, resizing the window will also change the width of the vertical header.

Here is a demo script that implements all of that:

import sys
from PyQt4 import QtCore, QtGui

class Window(QtGui.QWidget):
    def __init__(self, rows, columns):
        super(Window, self).__init__()
        self.table = QtGui.QTableView(self)
        self.table.installEventFilter(self)
        self.table.verticalHeader().setTextElideMode(QtCore.Qt.ElideRight)
        model =  QtGui.QStandardItemModel(rows, columns, self.table)
        for row in range(rows):
            item = QtGui.QStandardItem('FOO_BAR_123_AB_CD_%s' % row)
            model.setVerticalHeaderItem(row, item)
            for column in range(columns):
                item = QtGui.QStandardItem('(%d, %d)' % (row, column))
                item.setTextAlignment(QtCore.Qt.AlignCenter)
                model.setItem(row, column, item)
        self.table.setModel(model)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.Resize and source is self.table:
            source.verticalHeader().setMaximumWidth(source.width() / 4)
        return super(Window, self).eventFilter(source, event)

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window(15, 5)
    window.setGeometry(600, 100, 400, 300)
    window.show()
    sys.exit(app.exec_())

UPDATE:

I had a go at writing a custom header class that implements user resizing of the vertical header width. It almost works, but I cannot find a way to update the view properly whilst dragging with the mouse. Anyway, here is a demo of what I have so far in case anyone can see how to make it work:

import sys
from PyQt4 import QtCore, QtGui

class HeaderView(QtGui.QHeaderView):
    def __init__(self, table):
        super(HeaderView, self).__init__(QtCore.Qt.Horizontal, table)
        self.setClickable(True)
        self.setHighlightSections(True)
        self.setResizeMode(QtGui.QHeaderView.Interactive)
        self._vheader = table.verticalHeader()
        self._resizing = False
        self._start_position = -1
        self._start_width = -1

    def mouseMoveEvent(self, event):
        if self._resizing:
            width = event.x() - self._start_position + self._start_width
            if width > 0:
                self._vheader.setFixedWidth(width)
                # TODO: find a proper replacement for this
                self.geometriesChanged.emit()
        else:
            super(HeaderView, self).mouseMoveEvent(event)
            if 0 <= event.x() <= 3:
                if not self.testAttribute(QtCore.Qt.WA_SetCursor):
                    self.setCursor(QtCore.Qt.SplitHCursor)

    def mousePressEvent(self, event):
        if not self._resizing and event.button() == QtCore.Qt.LeftButton:
            if 0 <= event.x() <= 3:
                self._start_position = event.x()
                self._start_width = self._vheader.width()
                self._resizing = True
                return
        super(HeaderView, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        self._resizing = False
        super(HeaderView, self).mouseReleaseEvent(event)

class Window(QtGui.QWidget):
    def __init__(self, rows, columns):
        super(Window, self).__init__()
        self.table = QtGui.QTableView(self)
        self.table.setHorizontalHeader(HeaderView(self.table))
        self.table.verticalHeader().setTextElideMode(QtCore.Qt.ElideRight)
        model =  QtGui.QStandardItemModel(rows, columns, self.table)
        for row in range(rows):
            item = QtGui.QStandardItem('FOO_BAR_123_AB_CD_%s' % row)
            item.setToolTip(item.text())
            model.setVerticalHeaderItem(row, item)
            for column in range(columns):
                item = QtGui.QStandardItem('(%d, %d)' % (row, column))
                item.setTextAlignment(QtCore.Qt.AlignCenter)
                model.setItem(row, column, item)
        self.table.setModel(model)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window(15, 5)
    window.setGeometry(600, 100, 400, 300)
    window.show()
    sys.exit(app.exec_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • thanks for the answer! I have been thinking of this alternative about having a fixed maxWidth, but that is not exactly what I have been looking for. So you are saying that there is no way a user can resize this header on his will. – learncode Jan 10 '17 at 18:57
  • This is a big deal because some of header just have one character and some files have like some 1000 characters, So I do not want fixing the size with maxWidth, – learncode Jan 10 '17 at 18:59
  • 1
    @VenkataNarsiReddyVaddula. The maximum-width is not fixed. It is adjusted in the resize-event when the window is resized. I used a percentage of the table width, but you can use whatever method you like to set it. – ekhumoro Jan 10 '17 at 19:03
  • Yeah but the Window size of the project is fixed here. I will look for a couple of days for another alternative, if not then I will accept and award the answer. – learncode Jan 10 '17 at 19:06
  • 1
    @VenkataNarsiReddyVaddula. Okay. I think you should make it clear in your question that you want to allow the user to resize the vertical header width. I think that may still be possible, but it would require a different kind of solution (probably a custom header class). – ekhumoro Jan 10 '17 at 19:09
  • OMG! Aren't you the best! This works almost perfectly! – learncode Jan 11 '17 at 22:19
2

Ekhumoro's solution works nearly perfect. The choppy drag behavior seen during resize has to do with the coordinate space changing while the resize happens.

To fix this choppy behavior and have a smooth resize you should use the global coordinate space rather than the local coordinate for the headerView. To do this simply replace event.x() with event.globalX()

        #width = event.x() - self._start_position + self._start_width
        width = event.globalX() - self._start_position + self._start_width

            #self._start_position = event.x()
            self._start_position = event.globalX()

I also change the geometried Changed signal to be emited from the vertical header and not the horizontal header. Since it was the vertical header that actually changed size. However in my test case I couldn't tell that it mattered much but conceptually it seem correct.

            #self.geometriesChanged.emit()
            self._vheader.geometriesChanged.emit()
TexasRaptor
  • 105
  • 5
-1

PyQt tends to have some poor documentation, but this page gives all possible information about the QTableView class.

I am not sure what your specific problem is, however you could try:

self.tableView.horizontalHeader().setSectionResizeMode(3)

The documentation of .setSectionResize()

Alternatively, if you want to resize each header to a set size, try:

self.tableView.horizontalHeader().resizeSection(0, pixelSize1)
self.tableView.horizontalHeader().resizeSection(1, pixelSize2)
self.tableView.horizontalHeader().resizeSection(2, pixelSize3)
...

The documentation of .resizeSection()

Ronikos
  • 437
  • 1
  • 6
  • 18
  • I have uploaded an Image which can help you better understand the problem. Also QHeaderView Does not have setSectionResizeMode(), I have tried setResizeMode() – learncode Dec 22 '16 at 21:59