How to modify this PyQt5 current setup to enable drag resize between layouts

后端 未结 2 1937
挽巷
挽巷 2021-01-27 13:08

How to modify this current setup to enable resizing(horizontally and vertically) between the layouts shown below? Let\'s say I want to resize the lists in the right toward the l

相关标签:
2条回答
  • 2021-01-27 13:54

    You need to use QSplitter.

    It acts almost like a box layout, but has handles that allow the resizing of each item.

    Be aware that you can only add widgets to a QSplitter, not layouts, so if you need to add a "section" (a label and a widget) that can resize its contents, you'll have to create a container widget with its own layout.

    Also note that using dictionaries for these kind of things is highly discouraged. For versions of Python older than 3.7, dictionary order is completely arbitrary, and while sometimes it might be consistent (for example, when keys are integers), it usually isn't: with your code some times the labels were put all together, sometimes the widgets were inverted, etc., so if somebody is using your program with <=3.6 your interface won't be consistent. Consider that, while python 3.6 will reach end of life in 2022, it's possible that even after that a lot of people will still be using previous versions.
    If you need a way to group objects, it's better to use a list or a tuple, as I did in the following example.

    If you really "need" to use a key based group, then you can use OrderedDict, but it's most likely that there's just something wrong with the logic behind that need to begin with.

    class TestWindow(QMainWindow):
        def __init__(self, left_ratio, right_ratio, window_title):
            super().__init__()
            self.left_ratio = left_ratio
            self.right_ratio = right_ratio
            self.current_image = None
            self.window_title = window_title
            self.setWindowTitle(self.window_title)
            win_rectangle = self.frameGeometry()
            center_point = QDesktopWidget().availableGeometry().center()
            win_rectangle.moveCenter(center_point)
            self.move(win_rectangle.topLeft())
            self.tools = self.addToolBar('Tools')
            self.left_widgets = {'Image': QLabel()}
            self.right_widgets = [(QLabel('List1'), QListWidget()),
                                  (QLabel('List2'), QListWidget())]
            self.central_widget = QSplitter(Qt.Horizontal, self)
            self.setCentralWidget(self.central_widget)
            self.right_splitter = QSplitter(Qt.Vertical, self)
            self.adjust_widgets()
            self.central_widget.setStretchFactor(0, left_ratio)
            self.central_widget.setStretchFactor(1, right_ratio)
            self.show()
    
        def adjust_widgets(self):
            self.central_widget.addWidget(self.left_widgets['Image'])
            self.left_widgets['Image'].setPixmap(QPixmap('test.jpg').scaled(500, 400, Qt.IgnoreAspectRatio,
                                                                            Qt.SmoothTransformation))
            self.left_widgets['Image'].setScaledContents(True)
            self.central_widget.addWidget(self.right_splitter)
            for label, widget in self.right_widgets:
                container = QWidget()
                layout = QVBoxLayout(container)
                layout.addWidget(label)
                layout.addWidget(widget)
                self.right_splitter.addWidget(container)
    
    0 讨论(0)
  • 2021-01-27 13:59

    One way to rescale the image to an arbitrary size while maintaining its aspect ratio is to subclass QWidget and override sizeHint and paintEvent and use that instead of a QLabel for displaying the image, e.g.

    class PixmapWidget(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            self._pixmap = None
    
        def sizeHint(self):
            if self._pixmap:
                return self._pixmap.size()
            else:
                return QSize()
    
        def setPixmap(self, pixmap):
            self._pixmap = pixmap
            self.update()
    
        def paintEvent(self, event):
            painter = QPainter(self)
            super().paintEvent(event)
            if self._pixmap:
                size = self._pixmap.size().scaled(self.size(), Qt.KeepAspectRatio)
                offset = (self.size() - size)/2
                rect = QRect(offset.width(), offset.height(), size.width(), size.height())
                painter.drawPixmap(rect, self._pixmap)
    

    Since you are subclassing QMainWindow you could use DockWidgets to display the lists instead of adding them to the layout of the central widget, e.g.

    class TestWindow(QMainWindow):
        def __init__(self, left_ratio, right_ratio, window_title):
            super().__init__()
            #self.left_ratio = left_ratio    <--- not needed since image and lists
            #self.right_ratio = right_ratio  <--- are not sharing a layout anymore
    
            ...
    
            # use PixmapWidget instead of QLabel for showing image
            # refactor dictionary for storing lists to make adding DockWidgets easier
            self.left_widgets = {'Image': PixmapWidget()}
            self.right_widgets = {'List1': QListWidget(),
                                  'List2': QListWidget()}
            self.central_widget = QWidget(self)
            # self.main_layout = QHBoxLayout()  <-- not needed anymore
            self.left_layout = QVBoxLayout()
    
            self.adjust_widgets()
            self.adjust_layouts()
            self.show()
    
        def adjust_layouts(self):
            self.central_widget.setLayout(self.left_layout)
            self.setCentralWidget(self.central_widget)
    
        def adjust_widgets(self):
            self.left_layout.addWidget(self.left_widgets['Image'])
            self.left_widgets['Image'].setPixmap(QPixmap('test.jpg').scaled(500, 400, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
    
            self.dock_widgets = []
            for text, widget in self.right_widgets.items():
                dock_widget = QDockWidget(text)
                dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures)
                dock_widget.setWidget(widget)
                self.addDockWidget(Qt.RightDockWidgetArea, dock_widget)
                self.dock_widgets.append(dock_widget)
    

    Screenshots

    0 讨论(0)
提交回复
热议问题