PySide Qt: Auto vertical growth for TextEdit Widget, and spacing between widgets in a vertical layout

后端 未结 2 1521
无人及你
无人及你 2020-12-31 18:21

\"enter

I need to Solve two problems With my widget above.

  1. I\'d like to
相关标签:
2条回答
  • 2020-12-31 18:52

    1) Layouts

    The other answer on here is very unclear and possibly off about how layout margins work. Its actually very straightforward.

    1. Layouts have content margins
    2. Widgets have content margins

    Both of these define a padding around what they contain. A margin setting of 2 on a layout means 2 pixels of padding on all sides. If you have parent-child widgets and layouts, which is always the case when you compose your UI, each object can specific margins which take effect individually. That is... a parent layout specifying a margin of 2, with a child layout specifying a margin of 2, will effectively have 4 pixels of margin being displayed (obviously with some frame drawing in between if the widget has a frame.

    A simple layout example illustrates this:

    w = QtGui.QWidget()
    w.resize(600,400)
    
    layout = QtGui.QVBoxLayout(w)
    layout.setMargin(10)
    frame = QtGui.QFrame()
    frame.setFrameShape(frame.Box)
    layout.addWidget(frame)
    
    layout2 = QtGui.QVBoxLayout(frame)
    layout2.setMargin(20)
    frame2 = QtGui.QFrame()
    frame2.setFrameShape(frame2.Box)
    layout2.addWidget(frame2)
    

    Layout Image Example

    You can see that the top level margin is 10 on each side, and the child layout is 20 on each side. Nothing really complicated in terms of math.

    Margin can also be specified on a per-side basis:

    # left: 20, top: 0, right: 20, bottom: 0
    layout.setContentsMargins(20,0,20,0)
    

    There is also the option of setting spacing on a layout. Spacing is the pixel amount that is placed between each child of the layout. Setting it to 0 means they are right up against each other. Spacing is a feature of the layout, while margin is a feature of the entire object. A layout can have margin around it, and also spacing between its children. And, the children of the widget can have their own margins which are part of their individual displays.

    layout.setSpacing(10) # 10 pixels between each layout item
    

    2) Auto-Resizing QTextEdit

    Now for the second part of your question. There are a few ways to create a auto-resizing QTextEdit I am sure. But one way to approach it is to watch for content changes in the document, and then adjust the widget based on the document height:

    class Window(QtGui.QDialog):
    
        def __init__(self):
            super(Window, self).__init__()
            self.resize(600,400)
    
            self.mainLayout = QtGui.QVBoxLayout(self)
            self.mainLayout.setMargin(10)
    
            self.scroll = QtGui.QScrollArea()
            self.scroll.setWidgetResizable(True)
            self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
            self.mainLayout.addWidget(self.scroll)
    
            scrollContents = QtGui.QWidget()
            self.scroll.setWidget(scrollContents)
    
            self.textLayout = QtGui.QVBoxLayout(scrollContents)
            self.textLayout.setMargin(10)
    
            for _ in xrange(5):
                text = GrowingTextEdit()
                text.setMinimumHeight(50)
                self.textLayout.addWidget(text)
    
    
    class GrowingTextEdit(QtGui.QTextEdit):
    
        def __init__(self, *args, **kwargs):
            super(GrowingTextEdit, self).__init__(*args, **kwargs)  
            self.document().contentsChanged.connect(self.sizeChange)
    
            self.heightMin = 0
            self.heightMax = 65000
    
        def sizeChange(self):
            docHeight = self.document().size().height()
            if self.heightMin <= docHeight <= self.heightMax:
                self.setMinimumHeight(docHeight)
    

    I subclassed QTextEdit -> GrowingTextEdit, and connected the signal emitted from its document to a slot sizeChange that checks the document height. I also included a heightMin and heightMax attribute to let you specify how large or small its allowed to autogrow. If you try this out, you will see that as you type into the box, the widget will start to resize itself, and also shrink back when you remove lines. You can also turn off the scrollbars if you want. Right now each text edit has its own bars, in addition to the parent scroll area. Also, I think you could add a small pad value to the docHeight so that it expands just enough to not show scrollbars for the content.

    This approach is not really low level. It uses the commonly exposed signals and child members of the widget for you to receive notifications of state changes. Its pretty common to make use of the signals for extending functionality of existing widgets.

    Auto-Growing widget example picture

    0 讨论(0)
  • 2020-12-31 18:56

    To Address Question 1:

    Parent Widgets and Layouts both have margins, in addition to the spacing parameter of the layout itself. From some cause and affect testing It is apprent that margins apply both to the outer region of a parent as well as an internal region.

    So, for example if a 2 pixel margin is specified to a parent the vertical border has <--2 pixel | 2 pixel --> margin in addition to the margins of the layout (A HBoxLayout in this case). If the layout has a 2 pixel margin as well the area around horizontal line would look like:

    <-- 2 pixel | 2 pixel --> <-- 2 pixel (L) 2 pixel--> (W)

    edit Perhaps its more like this: | 2 pixel --> (L) 2 pixel --> <-- 2 pixel (W)

    Where | is the vertical line of the parent (L) is the vertical line of the Layout and (W) is the border of the embedded widget in the horizontal layout.

    The spacing of the layout is an additional parameter that controls how much space is inserted between widgets of the layout in addition to any layout margins.

    The description above might not be accurate( so feel free to edit it where it is inaccurate), but setting the margins of the parent and the layout to zero as well as the layouts spacing to zero produces the result you are after.

    For point 2:

    I do not think there is a straight forward way to address this issue (you probably have to resort to hooking in at a lower level, which requires a deeper understanding of the API). I think you should use the QLabel Widget instead of the QTextEdit widget. Labels do not have a view and thus expand as needed, at least thats how they work by default, as long as the parent isn't constrained in it's movement.

    So, change the QTextEdit to Qlabel and add a scrolling view to the parent and everything should work as you want. I have a feeling you chose QTextEdit because of it's background. Research the way HTML works in QT widgets and you might be able to alter the background via HTML.

    edit

    enter image description here

    This widget (excuse the size) is created by the following code on OS X with PyQT:

    import sys
    from PyQt4 import Qt
    
    class PostMeta(Qt.QWidget):
        posted_at_base_text = "<b> Posted At:</b>"
        posted_by_base_text = "<b> Posted By:</b>"
    
        def __init__(self):
            Qt.QWidget.__init__(self)
            self._posted_by_label = Qt.QLabel()
            self._posted_at_label = Qt.QLabel()
            layout = Qt.QVBoxLayout()
            layout.setMargin(0)
            layout.setSpacing(5)
            layout.addWidget(self._posted_by_label)
            layout.addWidget(self._posted_at_label)
            layout.addStretch()
            self.setLayout(layout)
            self._posted_by_label.setText(PostMeta.posted_by_base_text)
            self._posted_at_label.setText(PostMeta.posted_at_base_text)
    
    
    class FramePost(Qt.QFrame):
        def __init__(self):
            Qt.QFrame.__init__(self)
            layout = Qt.QHBoxLayout()
            layout.setMargin(10)
            self.te = Qt.QLabel()
            self.te.setStyleSheet("QLabel { background : rgb(245,245,245) }")
            self.te.setFrameStyle( Qt.QFrame.Panel |  Qt.QFrame.Sunken)
            self.te.setLineWidth(1)
            self._post_meta = PostMeta()
            layout.addWidget(self._post_meta)
            vline = Qt.QFrame()
            vline.setFrameShape(Qt.QFrame.VLine)
            layout.addWidget(vline)
            layout.addWidget(self.te)
            self.te.setText(
                """            line one
                line two
                line three
                line four
                line five
                line six
                line seven
                line eight
                line nine
                line ten
                line eleven
                line twelve
                line thirteen""")
            self.setLayout(layout)
    
            self.setFrameStyle(Qt.QFrame.Box)
            self.setLineWidth(2)
    
    app = Qt.QApplication(sys.argv)
    w = Qt.QWidget()
    layout = Qt.QHBoxLayout()
    fp = FramePost()
    layout.addWidget(fp)
    w.setLayout(layout)
    w.show()
    app.exec_()
    

    The labels in the left widget show the spacer and margin tweaking done, and I've used a QLabel for the post text. Notice I've tweaked the label to look a bit more like a default QTextEdit

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