2

What I'm trying to do is add splitter to a QGridLayout in order to resize the layout with the mouse. So for instance with this :

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys

class SurfViewer(QMainWindow):
    def __init__(self, parent=None):
        super(SurfViewer, self).__init__()
        self.parent = parent
        self.setFixedWidth(300)
        self.setFixedHeight(100)

        self.wid = QWidget()
        self.setCentralWidget(self.wid)

        self.grid = QGridLayout()

        l_a = QLabel('A')
        l_b = QLabel('B')
        l_c = QLabel('C')
        l_d = QLabel('D')
        l_e = QLabel('E')
        l_f = QLabel('F')
        l_g = QLabel('G')
        l_h = QLabel('H')
        l_i = QLabel('I')
        self.grid.addWidget(l_a, 0, 0)
        self.grid.addWidget(l_b, 0, 1)
        self.grid.addWidget(l_c, 0, 2)
        self.grid.addWidget(l_d, 1, 0)
        self.grid.addWidget(l_e, 1, 1)
        self.grid.addWidget(l_f, 1, 2)
        self.grid.addWidget(l_g, 2, 0)
        self.grid.addWidget(l_h, 2, 1)
        self.grid.addWidget(l_i, 2, 2)
        self.wid.setLayout(self.grid)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = SurfViewer(app)
    ex.setWindowTitle('window')
    ex.show()
    sys.exit(app.exec_( ))

I get this:

enter image description here

What I would like is instead of the colored line, have the possibility to click and drag vertically (for green lines) and horizontally (for red lines) the grid borders.

I tried something with QSplitter directly, but I end up with:

enter image description here

The Horizontal splits are okay, but the vertical ones are not aligned any more:

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys

class SurfViewer(QMainWindow):
    def __init__(self, parent=None):
        super(SurfViewer, self).__init__()
        self.parent = parent
        self.setFixedWidth(300)
        self.setFixedHeight(100)

        self.wid = QWidget()
        self.setCentralWidget(self.wid)

        # self.grid = QGridLayout()
        self.globallayout = QVBoxLayout()
        self.split_V = QSplitter(Qt.Vertical)

        l_a = QLabel('A')
        l_b = QLabel('B')
        l_c = QLabel('C')
        l_d = QLabel('D')
        l_e = QLabel('E')
        l_f = QLabel('F')
        l_g = QLabel('G')
        l_h = QLabel('H')
        l_i = QLabel('I')
        split_H = QSplitter(Qt.Horizontal)
        split_H.addWidget(l_a)
        split_H.addWidget(l_b)
        split_H.addWidget(l_c)
        self.split_V.addWidget(split_H)

        split_H = QSplitter(Qt.Horizontal)
        split_H.addWidget(l_d)
        split_H.addWidget(l_e)
        split_H.addWidget(l_f)
        self.split_V.addWidget(split_H)

        split_H = QSplitter(Qt.Horizontal)
        split_H.addWidget(l_g)
        split_H.addWidget(l_h)
        split_H.addWidget(l_i)
        self.split_V.addWidget(split_H)

        self.globallayout.addWidget(self.split_V)

        self.wid.setLayout(self.globallayout)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = SurfViewer(app)
    ex.setWindowTitle('window')
    ex.show()
    sys.exit(app.exec_( ))

Update

I think I almost found a solution where a function is used so that whenever the vertical splits are changed, it re-aligns them:

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys

class SurfViewer(QMainWindow):
    def __init__(self, parent=None):
        super(SurfViewer, self).__init__()
        self.parent = parent
        self.setFixedWidth(300)
        self.setFixedHeight(100)

        self.wid = QWidget()
        self.setCentralWidget(self.wid)

        # self.grid = QGridLayout()
        self.globallayout = QVBoxLayout()
        self.split_V = QSplitter(Qt.Vertical)

        l_a = QLabel('A')
        l_b = QLabel('B')
        l_c = QLabel('C')
        l_d = QLabel('D')
        l_e = QLabel('E')
        l_f = QLabel('F')
        l_g = QLabel('G')
        l_h = QLabel('H')
        l_i = QLabel('I')
        self.split_H1 = QSplitter(Qt.Horizontal)
        self.split_H1.addWidget(l_a)
        self.split_H1.addWidget(l_b)
        self.split_H1.addWidget(l_c)
        self.split_V.addWidget(self.split_H1)

        self.split_H2 = QSplitter(Qt.Horizontal)
        self.split_H2.addWidget(l_d)
        self.split_H2.addWidget(l_e)
        self.split_H2.addWidget(l_f)
        self.split_V.addWidget(self.split_H2)

        self.split_H3 = QSplitter(Qt.Horizontal)
        self.split_H3.addWidget(l_g)
        self.split_H3.addWidget(l_h)
        self.split_H3.addWidget(l_i)
        self.split_V.addWidget(self.split_H3)

        self.globallayout.addWidget(self.split_V)

        self.wid.setLayout(self.globallayout)

        self.split_H1.splitterMoved.connect(self.moveSplitter)
        self.split_H2.splitterMoved.connect(self.moveSplitter)
        self.split_H3.splitterMoved.connect(self.moveSplitter)

        # self.split_H1.splitterMoved
        # self.moveSplitter(0,self.split_H1.at )

    def moveSplitter( self, index, pos ):
        # splt = self._spltA if self.sender() == self._spltB else self._spltB
        self.split_H1.blockSignals(True)
        self.split_H2.blockSignals(True)
        self.split_H3.blockSignals(True)
        self.split_H1.moveSplitter(index, pos)
        self.split_H2.moveSplitter(index, pos)
        self.split_H3.moveSplitter(index, pos)
        self.split_H1.blockSignals(False)
        self.split_H2.blockSignals(False)
        self.split_H3.blockSignals(False)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = SurfViewer(app)
    ex.setWindowTitle('window')
    ex.show()
    sys.exit(app.exec_( ))

However, I still have an issue at the begining - the alignment is not correct :

enter image description here

I don't know How call the function moveSplitter in the __init__

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
ymmx
  • 4,769
  • 5
  • 32
  • 64

1 Answers1

3

It seems that directly calling moveSplitter (which is a protected method) may be problematic. Using Qt-5.10.1 with PyQt-5.10.1 on Linux, I found that it can often result in a core dump when called during __init__. There is probably a good reason why Qt provides setSizes as a public method for changing the position of the splitters, so it may be wise to prefer it over moveSplitter.

With that in mind, I arrived at the following implementation:

class SurfViewer(QMainWindow):
    def __init__(self, parent=None):
        ...
        self.split_H1.splitterMoved.connect(self.moveSplitter)
        self.split_H2.splitterMoved.connect(self.moveSplitter)
        self.split_H3.splitterMoved.connect(self.moveSplitter)

        QTimer.singleShot(0, lambda: self.split_H1.splitterMoved.emit(0, 0))

    def moveSplitter(self, index, pos):
        sizes = self.sender().sizes()
        for index in range(self.split_V.count()):
            self.split_V.widget(index).setSizes(sizes)

The single-shot timer is needed because on some platforms the geometry of the window may not be fully initialized before it is shown on screen. And note that setSizes does not trigger splitterMoved, so there is no need to block signals when using it.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • That is not working, the two last columns are out of screen (to the right). Instead of calling `self.moveSplitter`, I call `self.split_H1.setSizes([100000,100000,100000])`, `self.split_H2.setSizes([100000,100000,100000])`, `self.split_H3.setSizes([100000,100000,100000])`. That's working. However, I should use sys.maxsize to get the int max value but if I do so, I get a weird message: TypeError: index 0 has type 'int' but 'int' is expected – ymmx Mar 08 '18 at 08:15
  • @ymmx. Weird. Yesterday it worked perfectly, and now it's producing all sorts of problems. I have therefore completely revised my answer and provided an alternative solution. It works fine for me on linux, but I have not tested it on other platforms. You may need to add a small delay to the timer to get it to work properly on all systems. – ekhumoro Mar 08 '18 at 17:41
  • That works on Windows. To be honest, your answer is clearly beyond my Qt comprehension, but it works so I take it :) Thks – ymmx Mar 09 '18 at 08:28