Window suddenly shrinks after packing a new Frame in Tkinter - python-3.x

I am working on an app that is supposed to take up the whole screen. When I started, my initial intention was to switch Frames a lot. for example, let's say the user wanted to go to the settings. it would switch from the home Frame and go to the settings Frame.
However, do to some complications with other frames, I found that I would have to add another Frame underneath the first frame. For example, it would have a main Frame underneath the home screen Frame, so that if the user decided to open settings, it would forget the home Frame, and display the settings Frame also on the main Frame.
My problem is that when I added the underneath-everything Frame, things went haywire. the Tk() widget got small, and when I clicked and dragged it bigger, everything was out of proportion. however, it did display the settings when I clicked on the button :D.
here is my code:
self.main = Tk()
self.mainFrame = Frame()
self.mainFrame.pack(expand = True, fill = "both")
self.padding = Canvas(self.mainFrame, width = 1439, height =
self.CANVAS_HEIGHT)
self.padding.pack()
self.startFrame = Frame(self.mainFrame)
self.startFrame.place(relx = 0.0, rely = 0.0, anchor = "nw")
self.settingsFrame = Frame(self.mainFrame)
self.settingsCanvas = Canvas(self.settingsFrame, bg = "saddleBrown", width = self.CANVAS_WIDTH, height = self.CANVAS_HEIGHT)
self.settingsCanvas.pack()
self.startFrame.pack_forget()
self.settingsFrame.pack()
once again, when I run the code, everything is out of proportion.
If this is not clear, then please tell me.

Note that you are placing startFrame and then you are pack_forgetting it, but you should place_forget it.
Besides some weirds things in your code, I do not recommend to use place in this case, pack should do the work better (actually I have just used place in a few specific cases).
If you decide to use the pack geometry manager, and if you want your Frames to resize along with the window, you should indicate that you want them to expand and to fill eventually free space in both directions, vertically and horizontally (or y and x):
startFrame.pack(expand=True, fill='both') # both = x and y
If you want to give a fixed size to a Frame, see this post by Bryan Oakley.
I really recommend you to watch some tutorials or references on the pack geometry manager, because it's really useful, if you want to do something seriously. You can start reading from the Effbot's reference.

Related

How to change the slider length of the scrollbar according to the size of canvas object?

I am drawing canvas line objects by specifying the coordinates of them via scale and spinbox widgets.
Below is the function that runs when i use scale or spinbox.
def change_borehole_depth(self, var):
for k, v in self.objects.items():
self.delete(v)
self.objects = {}
if var.get():
self.depth_of_borehole(meter=var.get(), size=75)
Normally, if I call self.depth_of_borehole function with precise parameters in the __init__ method, the slider of the scrollbar automatically reshapes according to the coordinates of the line objects. However when I use the scale and spinbox to change the minimum and maximum y coordinates, the shape of the slider doesn't change. But it changes after I click to the slider.
The other functions work well, so I am just sharing the self.change_borehole_depth function because this is the function that specifies the size of the lines when I us the scale and spinbox. There's no problem in creating the lines according the var. The problem is just that, the slider of the scrollbar is not resizing/reshaping automatically. In order to solve this problem I tried to use update method first. But it didn't work. Then I used update_idletasks method but it neither worked. I also used set but it worked neither.
I am sharing some screenshots that I hope they can be helpful to make the problem being understood well.
Before clicking the slider of the scrollbar:
After clicking the slider of the scrollbar:
When the coordinates of the line object has been changed, the length of the slider isn't changing.
So, what should I do to solve this problem. If necesarry I can share the rest of the codes.
Thanks in advance.
The problem is solved when I changed the change_borehole_depth function to below.
def change_borehole_depth(self, var):
for k, v in self.objects.items():
self.delete(v)
self.objects = {}
if var.get():
self.depth_of_borehole(meter=var.get(), size=75)
self.configure(scrollregion=self.bbox("all"))
self.update()

Tkinter: pack()ing frames that use grid()

I am working on a UI for a data-display applet. I started with a tutorial and have since expanded it well beyond the scope of the tutorial, but some legacy bits remain from the tutorial that are now causing me difficulty. In particular relating to pack() and grid().
Following the tutorial I have defined a class Window(Frame) object, which I then declare as app = Window(root) where root = Tk(). Within the Window object is an initializing function def init_window(self), where my problems arise. Here is the relevant code in init_window():
def init_window(self):
self.master.title('Data Explorer') #changing the widget title
self.pack(fill=BOTH,expand=1) # allow widget to take full space of root
# Initializing a grid to place objects on
self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
self.mainframe.columnconfigure(0, weight = 1)
self.mainframe.rowconfigure(0, weight = 1)
self.mainframe.pack(pady = 10, padx = 10)
where the object self.mainframe contains a number of data selection dropdowns and buttons later on.
If I understand what this code is expected to do: it sets up the full window to be pack()ed with various frames. It then initializes a frame, self.mainframe, and within that frame initializes a grid(). Thus pack() and grid() do not collide. This setup was built by following the aforementioned tutorial.
This works correctly on my computer where I am developing the applet. However, when a collaborator compiles, they receive
_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack
on the line self.mainframe.grid(...). I have replaced the mainframe.pack() command with a mainframe.place() command, but this has not resolved the issue (since his compile does not reach that point); I have not figured out a way to remove the self.pack() command without causing all other elements of my UI to vanish.
Can anyone help us understand what is going wrong? For reference, we are both using MacOS, and compiling with Python3. I can provide additional information as requested, within limits.
The error is telling you exactly what is wrong. You can't use grid on a widget in the root window when you've already used pack to manage a widget in the root window.
You wrote:
It then initializes a frame, self.mainframe, and within that frame initializes a grid()
No, that is not what your code is doing. It is not setting up a grid within the frame, it's attempting to use grid to add the widget to the root window.
First you have this line of code which uses pack on a widget in the root window:
self.pack(fill=BOTH,expand=1)
Later, you try to use grid for another window in the root window:
self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
The above isn't setting up a grid within self.mainframe, it's using grid to add the widget to the root window.
You need to use one or the other, you can't use both for different windows that are both direct children of the root window.
In other words, you're doing this:
self.pack(fill=BOTH,expand=1)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
but since both self and self.mainframe are a direct child of the root window, you can't do that. You need to either use pack for both:
self.pack(fill=BOTH,expand=1)
self.mainframe.pack(...)
... or grid for both:
self.grid(...)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )

Maximize figures before saving

The question about how to do maximize a window before saving has been asked several times and has several questions (still no one is portable, though), How to maximize a plt.show() window using Python
and How do you change the size of figures drawn with matplotlib?
I created a small function to maximize a figure window before saving the plots. It works with QT5Agg backend.
import matplotlib.pyplot as plt
def maximize_figure_window(figures=None, tight=True):
"""
Maximize all the open figure windows or the ones indicated in figures
Parameters
----------
figures: (list) figure numbers
tight : (bool) if True, applies the tight_layout option
:return:
"""
if figures is None:
figures = plt.get_fignums()
for fig in figures:
plt.figure(fig)
manager = plt.get_current_fig_manager()
manager.window.showMaximized()
if tight is True:
plt.tight_layout()
Problems:
I have to wait for the windows to be actually maximized before using the plt.savefig() command, otherwise it is saved with as not maximized. This is a problem if I simply want to use the above function in a script
(minor problems:)
2. I have to use the above function twice in order to get the tight_layout option working, i.e. the first time tight=True has no effect.
The solution is not portable. Of course I can add all the possible backend I might use, but that's kind of ugly.
Questions:
how to make the script wait for the windows to be maximized? I don't want to use time.sleep(tot_seconds) because tot_seconds would be kind of arbitrary and makes the function even less portable
how to solve problem 2 ? I guess it is related to problem 1.
is there a portable solution to "maximize all the open windows" problem?
-- Edit --
For problem 3. #DavidG suggestion sounds good. I use tkinter to automatically get width and height, convert them to inches, and use them in fig.set_size_inches or directly during the figure creation via fig = plt.figure(figsize=(width, height)).
So a more portable solution is, for example.
import tkinter as tk
import matplotlib.pyplot as plt
def maximize_figure(figure=None):
root = tk.Tk()
width = root.winfo_screenmmwidth() / 25.4
height = root.winfo_screenmmheight() / 25.4
if figure is not None:
plt.figure(figure).set_size_inches(width, height)
return width, height
where I allow the figure to be None so that I can use the function to just retrieve width and height and use them later.
Problem 1 is still there, though.
I use maximize_figure() in a plot function that I created (let's say my_plot_func()) but still the saved figure doesn't have the right dimensions when saved on file.
I also tried with time.sleep(5) in my_plot_func() right after the figure creation. Not working.
It works only if a manually run in the console maximize_figure() and then run my_plot_func(figure=maximized_figure) with the figure already maximized. Which means that dimension calculation and saving parameters are correct.
It does not work if I run in the console maximize_figure() and my_plot_func(figure=maximized_figure) altogether, i.e. with one call the the console! I really don't get why.
I also tried with a non-interactive backend like 'Agg', so that the figure doesn't get actually created on screen. Not working (wrong dimensions) no matter if I call the functions altogether or one after the other.
To summarize and clarify (problem 1):
by running these two pieces of code in console, figure gets saved correctly.
plt.close('all')
plt.switch_backend('Qt5Agg')
fig = plt.figure()
w, h = maximize_figure(fig.number)
followed by:
my_plot_func(out_file='filename.eps', figure=fig.number)
by running them together (like it would be in a script) figure is not saved correctly.
plt.close('all')
plt.switch_backend('Qt5Agg')
fig = plt.figure()
w, h = maximize_figure(fig.number)
my_plot_func(out_file='filename.eps', figure=fig.number)
Using
plt.switch_backend('Agg')
instead of
plt.switch_backend('Qt5Agg')
it does not work in both cases.

Sometimes the Tkinter canvas doesn't load

I am writing a program which involves drawing rectangles on the tkinter canvas. Here is the code that creates the canvas and the window it is a part of. (This is within an object, and the root.mainloop() is called later outside of the object, and the drawing is also present elsewhere)
self.root = tk.Tk()
self.root.title = "Game"
self.root.resizable(0,0)
self.root.geometry('{}x{}'.format(500, 500))
#create drawing canvas
self.canvas_base = tk.Canvas(self.root, bg="white", width=500, height=500)
self.canvas_base.bind("<Up>", lambda event1, arg1=0: self.arrow_press(event1,arg1)) #bind up key
self.canvas_base.bind("<Right>", lambda event2, arg2=1: self.arrow_press(event2,arg2)) #bind right key
self.canvas_base.bind("<Down>", lambda event3, arg3=2: self.arrow_press(event3,arg3)) #bind down key
self.canvas_base.bind("<Left>", lambda event4, arg4=3: self.arrow_press(event4,arg4)) #bind left key
self.canvas_base.focus_set() #give the window keyboard focus
self.canvas_base.pack()
The problem I've noticed is that sometimes no canvas is generated, as the white background fails to appear and I'm left with the grey/off-white basic background for the window. No errors pop up in the console, and other code within the window runs fine. Is this a normal problem with the canvas, or is it something to do with the code?
Considering the size of my code, and the fact that I have no idea what is exactly causing the issue. I decided to just post the main initialization, but if the drawing/anything else within the code would have an impact, I can post that too. Also, nothing else is being done with the canvas.
EDIT:
This seems to occur with the creation of the canvas full stop, and is probably a Python error. By running similar code as the entirety of a file, and waiting a period before I run it each time, the first time I run it after an hour or so causes no Canvas to appear.

How to implement picking point by mouse under paraview web

Our company want our paraview web application to show relevant data when users click left mouse button.
The other part like how to extract data from a selected point was straightforward. But I cannot solve the problem: how to pick a point in the coordinates while mouse click?
I added a callback function to handle left button press in the python script. And then I tried to use an vtkPointPicker to cast a ray and pick the point in the coordinates. But the pvpython program which run the python script as the server will crash when click mouse (to be exact when the picker run pick()). Under debugging I found that the VTK code dereferencing null pointer was why the program crash. But the VTK source code is just too complicated to delve into further.
Here is my callback function:
def CustomLeftButtonPress(obj, event):
x = obj.GetEventPosition()[0]
y = obj.GetEventPosition()[1]
picker = vtkPointPicker()
obj.SetPicker(picker)
currentRenderer = obj.FindPokedRenderer(x,y)
picker.Pick(x, y, 0.0, currentRenderer)
...
picker.Pick(x, y, 0.0, currentRenderer) causes the crash.
I should use vtkWorldPointPicker instead of vtkPointPicker.

Resources