I have a QDockWidget with a QGroupBox as the top widget, with QVBoxLayout applied. This contains a QChartView and a QLabel.
The QLabel contains a text composed of several lines of different length.
First, I would like to have the QChartView the same width and height as the QLabel.
Second, as the content of the QLabel is updated by setText(...) in a Slot method, its content changes in height and width. So I would like to update the width and height of the QChartView above it.
For the moment, I only achieved to grow the entire DockPanel to accomodate longer and wider QLabel content, but not to shrink back when this content is narrower and less wide...
I did play with a lot of things, without real success (here a snippet of the Slot method that updates the QLabel content...):
self.fkChartView.hide()
self.pPDetailsLabel.hide()
self.pPDetailsLabel.parent().hide()
self.pPDetailsLabel.setText(self.canvas.grayScottModel.getPearsonPatternDescription(specie=type))
self.pPDetailsLabel.updateGeometry()
self.pPDetailsLabel.parent().updateGeometry()
self.pPDetailsLabel.parent().update()
self.pPDetailsDock.updateGeometry()
self.pPDetailsDock.update()
self.pPDetailsLabel.show()
self.pPDetailsLabel.parent().show()
# print(self.pPDetailsLabel.sizeHint())
self.fkChartView.setMinimumHeight(self.pPDetailsLabel.size().width())
self.fkChartView.setMaximumHeight(self.pPDetailsLabel.size().width())
self.fkChartView.setMinimumWidth(self.pPDetailsLabel.size().width())
self.fkChartView.setMaximumWidth(self.pPDetailsLabel.size().width())
self.fkChartView.updateGeometry()
self.fkChartView.show()
self.fkChartView.updateGeometry()
self.fkChartView.update()
I tried to hide the widgets, so they forget their sizes and/or sizeHint (not sure).
I tried a few updateGeometry() and update() too, but it does not seem to help.
An idea, anyone?
I had it working as intended in the end by doing this in the slot method that updates the content of the QLabel:
self.fkChartView.hide()
self.pPDetailsLabel.setText(self.canvas.grayScottModel.getPearsonPatternDescription(specie=type))
self.pPDetailsLabel.adjustSize()
self.pPDetailsLabel.parent().adjustSize()
# Sets the dimensions of the chart folowing the label width
self.fkChartView.setMinimumHeight(self.pPDetailsLabel.size().width())
self.fkChartView.setMaximumHeight(self.pPDetailsLabel.size().width())
self.fkChartView.setMinimumWidth(self.pPDetailsLabel.size().width())
self.fkChartView.setMaximumWidth(self.pPDetailsLabel.size().width())
self.fkChartView.adjustSize()
self.fkChartView.show()
self.pPDetailsDock.adjustSize()
So I first hide the QChartView, so its dimensions do not interfere,
then all the magic happens thanks to the adjustSize() call. I do this on the QLabel after its content has been updated,
then on its parent (the top QGroupBox in my QDockWidget).
The QChartView still hidden, I force its dimensions,
then show it and
finally adjust the size of the QDockWidget.
It does precisely what I want:
if text is broader, the chart expands but stays square,
if text is narrower, chart shrinks, but stays square.
Total height of the dock adjusts also to fit, without staying the longer and longer it had to be to accomodate for a big former QLabel content.
I'm re-doing from scratch a GUI for a program beacuse I realise that a new layout would make it easier to use but I'm getting very confused.
I've been looking for scripts resulting in similar layout, but the more I read about it, the less I understand.
The image below is the very basic structure of the GUI that I'm trying to make:
I'm aware that it is a very simple question, but the docs and previous questions are not making the understading process any easier. I believe that having just the code of the main layout would be a huge help to finally understand how to organise frames.
NOTE: The background color and text are there just to make the layout more clear. I'm only asking for the very basic frame's arrangement.
As always, thanks a lot to anyone who help.
Cheers
You can use pack, place, or grid. They all can produce this layout, though I personally recommend not using grid.
For me, pack is the natural choice. pack excels at layouts where widgets take up an entire side of a region. If that fits your design, pack requires fewer lines of code than grid, and fewer and less confusing options than place.
In this specific case the blue area clearly takes up the whole left side, and the yellow and red fill up the top and bottom of the right side, so pack is well suited to the task.
For the following examples, we'll start with this code:
import tkinter as tk
root = tk.Tk()
f1 = tk.Frame(root, bg='blue', width=200,height=400)
f2 = tk.Frame(root, bg='yellow', width=400, height=300)
f3 = tk.Frame(root, bg='red', width=400, height=100)
do_layout()
root.mainloop()
Using pack
pack works by placing widgets along a side of an empty area. In this case, the blue area is clearly taking up the left side. In the space that remains after adding the blue area, the yellow space takes up the top part of the remaining space and the red area takes the bottom.
def do_layout():
f1.pack(side="left", fill="both", expand=True)
f2.pack(side="top", fill="both", expand=True)
f3.pack(side="bottom", fill="both", expand=True)
Whether this is what you actually want or not is hard to say. It depends a lot on how you want the widgets to react when you add children or you resize the window. You may want to change the expand and/or fill options for some of the windows, though that depends on how you want the widgets to react when the user resizes the window.
Using grid
Grid is often the easiest technique to grasp. You specify positions within a row or column, and can decide if an item should span one or more rows or column.
In your case you clearly have two rows and two columns, and the blue area spans both rows.
def do_layout():
f1.grid(row=0, column=0, rowspan=2, sticky="nsew")
f2.grid(row=0, column=1, sticky="nsew")
f3.grid(row=1, column=1, sticky="nsew")
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(1, weight=1)
Like with the pack example, it's hard to say if this meets your actual needs. Again, it depends on how you want the UI to behave when you add widgets to the frames and when you resize the window.
Notice that grid requires a couple of extra lines of code. As a general rule of thumb you should always give at least one row and one column a non-default weight so that grid knows what to do with unallocated space.
Using place
place is arguably the weakest choice of the three for this type of layout. Nevertheless, you can achieve the same result as with the others.
def do_layout():
f1.place(x=0, y=0, relwidth=.3, relheight=1.0)
f2.place(relx=.3, y=0, relwidth=.7, relheight=.6)
f3.place(relx=.3, rely=.6, relwidth=.7, relheight=.4)
One of the significant differences between place and the other options is that the use of place will not cause the containing window to grow or shrink to fit contents. You are required to make sure that the containing widget (in this case, root) is the correct size.
Other options
Another option would be to use a paned widget, in the case where you want the user to be able to adjust the proportions of an area. For example, you could use a horizontal paned widget to allow the user to make the blue area widget or narrower. Likewise, you could use a vertical paned window if you want the user to be able to adjust the relative height of each area.
You can also mix and match. While you can't use both grid and pack directly within the root window, you could use pack to lay out a frame on the left and a frame on the right, and then use grid within the right to lay out one frame on top and one on bottom. Or visa versa.
I'm making an emacs-esque toy text editor. At startup, there's one large window (a QTextEdit derivative) in the top center of the screen, with a minibuffer (QLineEdit derivative) underneath. Both of the actual editing widgets are contained in the grids of parent classes called Window and MiniWindow (Window also keeps track of a QLabel that appears directly beneath the QTextEdit).
My Window object is at location 1, 1 in the grid, and my MiniWindow object is at 2, 1. I've set content margins to 0 and spacing to 0, which looks great at first, but when I try to grow the window by dragging on the corner, this starts to happen:
As you can see, the screen is divided into two rows (as it should be), but half of the vertical length of the screen is dedicated to each row. What I need is for the top Window to stretch its length during resizing so that it is always adjacent to the MiniWindow underneath. Is there some other option I need to be setting?
Nevermind, got it.
I was having this problem because the QLineEdit object was in the grid of my container class, MiniWindow. The height of a MiniWindow object is free to vary with the window resizing in a way that a QLineEdit alone would not be. The fix was set to the maximumHeight of MiniWindow to approximately the height of a QLineEdit, which is around 16.
Works great now.
So if I have two elements in a fixed size window to start with, instead of staying at their layoutVerticalSpacing, the elements auto expand the vertical spaces as I resize the main window.
I've tried self.gridLayout.setVerticalSpacing(0) but that doesn't quite work...
Is there a way to just set the spacing to stay the same no matter what the mainWindow size is?
While writing a UITextView control, I realized that if I change the width (via setFrame) of the view on scale gesture, the word wrap gets screwed up. It becomes prominent on rescaling multiple times.
By screwing up, I mean that the text starts wrapping from a smaller frame/rectangle than the current width of the textview. This is what I am doing on scale gesture:
[textview setFrame:CGRectMake(textview.frame.origin.x, textview.frame.origin.y, textview.frame.size.width * [gestureRecognizer scale], textview.frame.size.height )];
Also, if it helps to debug, I have subclassed the UITextView and set the canBecomeFirstResponder to NO to block the keyboard & menu. Also I have disabled scrolling by setScrollEnable:NO and setEditable:NO