Difference between "grid" and "pack" geometry managers - python

What's the main difference between the Tkinter geometry managers grid and pack?
What do you use for your projects ?
If grid is better to align object, what the main purpose of pack?

grid is used to lay out widgets in a grid. Another answer says it "overlays a graph" which is a bit of a misnomer. It doesn't overlay anything, it merely arranges widgets along row and column boundaries. It is great for creating tables and other structured types of layouts.
pack lays things out along the sides of a box. It excels at doing layouts where everything is on a single row or in a single column (think rows of buttons in a toolbar or dialog box). It's also useful for very simple layouts such as a navigator on the left and a main work area on the right. It can be used to create very complex layouts but it gets tricky until you fully understand the packing algorithm.
You cannot use both grid and pack with widgets that have a common parent. Your app may work but it is much more likely to get into an infinite loop as each manager tries to layout the widgets, then the other notices the widgets change size and try to adjust, etc. etc.
The third manage is place. Place is great for doing either absolute positioning (ie: place widget at a given x/y) or relative (eg: place a widget on the right edge of some other widget).
While you cannot mix grid and pack within the same container (a container is typically a frame), you can use both grid and pack within a single application. This is very, very common since each has strengths and weaknesses. I use both on a regular basis.

Related

PyQT how to change layouts size

I am trying to make a GUI that is very similar to Spotify using PyQT
I've already designed the main window and I am struggling with applying the design to QT Creator.
This is what I want it to look like
But I'm trying to use Layouts in order to organize every widget.
For example
Image of the main window, split into 3 parts
like in the sketch I've made the software will be split into 3 parts, left bar, mid which is where the explore title is and right bar.
The problem I have is that I can't control the layouts size and the size of what's below them for example in the sketch the mid bar is wider than the right bar and the right bar is wider than the left bar but in the QTCreator I've no idea how to change the width and height of objects inside Layouts.
The first option (which I personally prefer) is to fill your layout with it's contents first (buttons, labels, etc.). This will already start to scale your layout, if there are more buttons in the middle bar than on the other bars. If your layout still isn't what you want it to be, you can use Spacers. They can push and pull puttons and position the in relation to other parts of the layout, as well as to the layout itself. The scaling will be adjusted automatically according to the spacers position.
This option has the advantage, that your application will be correctly scaled and not completely chaotic when it is run on a device with a display aspect ratio which is not the same as the one of the machine your developing it on.
There is, however, also a minimumSize and maximumSize attribute to the layouts, which provide a much more straightforward possibility, but sometimes cause your layouts to become very weird when adding or removing a button, or changing the text of a label. More on this option can be found in the Qt docs:
https://doc.qt.io/qt-5/qlayout.html

How can i fit my tkinter app to any size screen?

im doing an tkinter app in a computer, im using the grid() method to place the widgets. At first of the program i use this code to make the window size like the screen size:
an = self.root.winfo_screenwidth()
al = self.root.winfo_screenheight()
self.tam = '%dx%d'%(an,al)
self.root.geometry(self.tam)
And it works, but this app will be used through a remote desktop with different devices (different screen sizes).
How can I do that the widgets fill on the window like the original design? Thanks
Without any concrete examples of your code, there's no way to give more specific advice than to say that the solution is to design your program so that it resizes well.
Tkinter excels at making widgets fit, so as long as you use the options at your disposal (fill and expand for pack, row and column weights and other options for grid), and you don't hard-code any widths and heights, your GUI will easily work on a variety of systems.
Concrete pieces of advice:
don't use place except in very rare circumstances. While place supports relative positioning and sizing, it requires more work than pack and grid
design the GUI to work on the smallest display possible, and then make sure that when you manually resize the window it behaves properly
When using grid, make sure you always have at least one row and one column with a non-zero weight so that it knows how to allocate extra space
When using pack make sure you use expand and fill properly
Don't turn off the ability for the user to resize the window

Why can't you use the function 'pack' and the 'grid in the same code

I know you can't use pack and grid together, but why? Why does it raise an error?
_tkinter.TclError: cannot use geometry manager pack inside . which already has slaves managed by grid
The reason is that each one wants to control the geometry of all widgets inside a given container (Frame, Toplevel, etc), and each one will reapply its rules when it detects that a widget it is controlling changes size.
For example, if you start by using pack, pack will add widgets to the window according to its own algorithm. Depending on the size and orientation of the widgets, this may cause the window to grow or shrink and may cause other widgets with the same master to grow or shrink.
Now, if you add a widget using grid, it's going to do the same thing -- it will add widgets using its own algorithm. Like pack, this might cause the window to grow or shrink, or the widget to change size.
Next, because pack is managing some widgets, when it detects that the window has changed size, it will reapply its own algorithm, possibly changing the size or position of some widgets.
Next, because some of the widgets are managed by grid it will detect that they have changed size and it will try to reapply its algorithm. This might cause the window to grow or shrink, or change the size of some widgets.
Next, because some of the widgets are managed by pack it will detect that they have changed size so it will try to reapply its algorithm. This might cause the window to grow or shrink, or change the size of some widgets.
Next, because some of the widgets are managed by grid it will detect that they have changed size so it will try to reapply its algorithm. This might cause the window to grow or shrink, or change the size of some widgets.
Next, because some of the widgets are managed by pack it will detect that they have changed size so it will try to reapply its algorithm. This might cause the window to grow or shrink, or change the size of some widgets.
... and so on until the end of time, or until something causes this cycle to end.
It actually is possible to use both for widgets that share a common parent if you are very careful and know exactly what will happen, but I've never come across a valid reason to do so.
You also absolutely can (and should) use both pack and grid in an application as a whole. Both have strengths and weaknesses. For example, you might use pack to add a full width toolbar, a full width statusbar, and middle section for the rest of the app. If the rest of the app is a form, it might make sense to use grid for the widgets inside the middle section of the GUI.

In what cases Tkinter's grid() cannot be mixed with pack()?

I want to eliminate strange extra space that seems to resist any size tweaking in my layout when using grid() alone, but calling in pack() sometimes make things worse: The GUI simply disappear entirely!
I read a few eye-opening layout answers from #Bryan Oakley such as:
When to use pack or grid layouts in tkinter?
and
Tkinter: grid or pack inside a grid?
but when I get down to write my own stuff, I still often have troubles.
My understanding:
I must have a Frame to fill the root window, otherwise there'd be no hope to fill the extra space in the window, however I tweak widgets alone.
For all the child widgets sitting inside a common parent Frame, I must use either pack() or grid() but not both.
When using grid() in a Frame, it's mandatory to specify Frame.grid_rowconfigure() and .grid_columnconfigure() with non-zero weight arguments. Otherwise, nothing would show up.
It's thus possible to have the main Frame using pack(), but its immediate child Frames all using grid(); Inside each of these child Frames on the grid, we could then pack() their own child widgets. In other words, we could interleave grid() and pack() by "regions" or container hierarchy levels, but never mix them in the same container: The only restriction.
By a careful weight design, I could fill a horizontal space in a parent Frame with a child Frame full of widgets laid out horizontally, e.g., all widgets use grid(sticky='nsew'), and the child Frame uses pack(side='top', fill='both', expand=True).
If my understanding was correct, then I could never figure out why #5 couldn't work for me, e.g., there is always unused extra space towards the right end of my horizontal child Frame inside the main Frame of the root window.
UPDATE 2
I figured it out. #5 didn't work for me because I forgot to specify .grid_columnconfigure(0, weight=1) in the main Frame before using grid(). My bad! Case closed.
UPDATE
I'm on macOS High Sierra, running python 3.6.4 Homebrew.
In what cases Tkinter's grid() cannot be mixed with pack()?
In all cases, you cannot use both grid and pack for widgets that have a common master. Within a master, all direct children must use the same geometry manager. Within an application as a whole, you can mix pack and grid all you want as long as you follow that one rule that you can't use them both for widgets that have the same parent.
I must have a Frame to fill the root window, otherwise there'd be no hope to fill the extra space in the window, however I tweak widgets alone.
This is not correct. You can easily fill all of the space in the root window without using a frame.
For all the child widgets sitting inside a common parent Frame, I must use either pack() or grid() but not both.
That is correct. The third option is to use place, though it's rarely the best choice.
When using grid() in a Frame, it's mandatory to specify Frame.grid_rowconfigure() and .grid_columnconfigure() with non-zero weight arguments. Otherwise, nothing would show up.
That is not true -- configuring rows and columns to have a non-zero weight isn't mandatory. It's usually a best practice, but it's not required in order for widgets to show up. The weight only applies to how grid manages extra space. Any widgets with a non-zero size should appear whether you use weights or not.
It's thus possible to have the main Frame using pack(), but its immediate child Frames all using grid()
Correct.
By a careful weight design, I could fill a horizontal space in a parent Frame with a child Frame full of widgets laid out horizontally, e.g., all widgets use grid(sticky='nsew'), and the child Frame uses pack(side='top', fill='both', expand=True).
That is correct.

How do I overlap widgets with the Tkinter pack geometry manager?

I want to put a Canvas with an image in my window, and then I want to pack widgets on top of it, so the Canvas acts as a background.
Is it possible to have two states for the pack manager: one for one set of widgets and another for another set?
The answer to your specific question is no. You can't have two states or otherwise use pack two different ways in the same parent.
However, what I think you want to accomplish is simple. Use the built-in features of the canvas to create an image item that is part of the canvas, then pack things into the canvas as if it were a frame.
You can accomplish a similar thing by creating a label widget with an image, then pack your other widgets into the label.
One advantage to using a canvas is you can easily tile an image to fill the whole canvas with a repeating background image so as the window grows the image will continue to fill the window (of course you can just use a sufficiently large original image...)
I believe that Bryan's answer is probably the best general solution. However, you may also want to look at the place geometry manager. The place geometry manager lets you specify the exact size and position of the widget... which can get tedious quickly, but will get the job done.
... turned out to be unworkable because I wanted to add labels and more canvases to it, but I can't find any way to make their backgrounds transparent
If it is acceptable to load an additional extension, take a look at Tkzinc. From the web site,
Tkzinc (historically called Zinc) widget is very similar to the Tk Canvas in that they both support structured graphics. Like the Canvas, Tkzinc implements items used to display graphical entities. Those items can be manipulated and bindings can be associated with them to implement interaction behaviors. But unlike the Canvas, Tkzinc can structure the items in a hierarchy, has support for scaling and rotation, clipping can be set for sub-trees of the item hierarchy, supports muti-contour curves. It also provides advanced rendering with the help of OpenGL, such as color gradient, antialiasing, transparencies and a triangles item.
I'm currently using it on a tcl project and am quite pleased with the results. Extensions for tcl, perl, and python are available.
Not without swapping widget trees in and out, which I don't think can be done cleanly with Tk. Other toolkits can do this a little more elegantly.
COM/VB/MFC can do this with an ActiveX control - you can hide/show multiple ActiveX controls in the same region. Any of the containers will let you do this by changing the child around. If you're doing a windows-specific program you may be able to accomplish it this way.
QT will also let you do this in a similar manner.
GTK is slightly harder.

Categories

Resources