How to make tkinter::pack() place label on top left corner in below program? - python-3.x

I am using pack() to align label and buttons on tkinter.The following code:
from tkinter import *
wind=Tk()
wind.geometry('450x450')
l1=Label(wind,text='Did you mean:')
l1.pack(side=LEFT)
b1=Button(wind,text='Button1',width=450)
b1.pack()
b2=Button(wind,text='Button2',width=450)
b2.pack()
wind.mainloop()
gives output:1
I tried removing the side=LEFT from l1.pack(side=LEFT) it gives: 2.
For me expected output is label l1 on top left corner and the buttons stacked below it.

pack works with a box model, aligning widgets along one side of the empty space in a container. Thus, to put something along the top, you need to use side="top" (or side=TOP if you prefer using a named constant), and it needs to come before other widgets.
In your specific case, to get the widget aligned at the top, you would do the following:
l1.pack(side=TOP)
By default this will center the widget along the top edge. If you also want the label aligned to the left, you would use the anchor option, which takes points of a compass ("n", "s", "e", "w", "nw", etc).
Thus, to place the widget at the top, and anchor it to the top-left corner you would do something like this:
l1.pack(side=TOP, anchor=NW)

Related

Using tk.Scrollbar to update images in tk.canvas

I developed a small tkinter GUI to display (and export) images stored in a proprietary format. I managed to load the data into a 3D numpy uint8 array. I want to display one slice of that 3D array in a tkinter.canvas. To do so I used ImageTk.PhotoImage.
Underneath the Canvas I inserted a tk.Scrollbar. My goal is to use that scrollbar to let the user actively "scroll" though the 3D Array. Basically when the slider is moved or any of the arrows is pressed the slice corresponding to the slider position should be displayed in the canvas.
Right now I have the issue that I don't understand how to set the range of the scrollbar to my z-Dimension and then how to bind the scrollbar events to the movement or arrow actions to update the canvas.
Right now I don't have example code since this is a more conceptual problem.
Could you point me in the right direction how to solve this?
Best TMC
edit: Photo
Tkinter Gui with Canvas and Scrollbar
The solution is to use tkinter.Scale instead of tkinter.Scrollbar . As a side note, the command within:
scale = tk.Scale(yourTkFrame, from_=min, to=max, orient='horizontal', command=doSomething)
passes two values to the function doSomething.
Using the Scale allows to use the scale.get() method to retrieve the position between min and max values. Those can then be used to set the image corresponding to the selected position on the slide.

Matplotlib Text artist - how to get size? (not using pyplot)

Background
I've moved some code to use matplotlib only, instead of pyplot (The reason is it's generating png files in multi-process with futures, and pyplot isn't thread/process safe that way).
So I'm creating my figure and axis via matplotlib.Figure(), not via pyplot.
I've got some code that draw's a text 'table' on a figure, with one side right justified and the other left. In order to keep the spacing between the two sides constant, I was previously using get_window_extent() on my left-side text artist to figure out the location of the right hand side:
# draw left text at figure normalised coordinates, right justified
txt1 = figure.text(x, y, left_str,
ha='right', color=left_color, fontsize=fontsize)
# get the bounding box of that text - this is in display coords
bbox = txt1.get_window_extent(renderer=figure.canvas.get_renderer())
# get x location for right hand side offset from left's bbox
trans = figure.transFigure.inverted()
xr, _ = trans.transform((bbox.x1 + spacing, bbox.y0))
# draw right side text, using lhs's y value
figure.text(xr, y, right_str,
ha='left', color=right_color, fontsize=fontsize)
Problem
My problem is now that I'm not using pyplot, the above code fails to work, because figure.canvas.get_renderer() fails to return a renderer, as I haven't set one, and am simply using Figure.savefig(path) to save my file when I'm done.
So is there a way to find out the bounding box size of an Artist in a Figure without having a renderer set?
From looking at legend, which allows you to use a bounding box line with variable text size, I'm presuming there is, but I can't find how.
I've had a look at https://matplotlib.org/3.1.3/tutorials/intermediate/artists.html, and also tried matplotlib.artist.getp(txt1) on the above, but didn't find any seemingly helpful properties.

positioning of a 3 Frames layout with tkinter

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.

PyQt - Keeping spacing at zero during window resize, grid layout

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.

Drawing on canvas with python and tkinter

I am trying to display mouse position on timer. I use winfo_pointerxy(), here is part of the code from my_func():
curr_x, curr_y = mouseFrame.winfo_pointerxy()
curr_x = mouseFrame.canvasx(curr_x)
curr_y = mouseFrame.canvasy(curr_y)
mouseFrame.create_oval(curr_x, curr_y, curr_x + 5, curr_y + 5, fill='green')
start_btn.after(time_interval, my_func)
It seems like I use canvasx() wrong cause it still returns position counted from the left-up corner of the screen.
According to this tkinter reference (which I use constantly)
Because the canvas may be larger than the window, and equipped with
scrollbars to move the overall canvas around in the window, there are
two coordinate systems for each canvas:
The window coordinates of a point are relative to the top left
corner of the area on the display where the canvas appears.
The canvas coordinates of a point are relative to the top left
corner of the total canvas.
If your canvas is against the upper left corner of the window (display) and you have not scrolled the canvas, the two sets of coordinates should be the same.

Resources