Sometimes the Tkinter canvas doesn't load - python-3.x

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.

Related

PyQt5 run Multithreaded processes consicutively on single thread

I am working on an application in PyQT5 which has two docks on either side and an OCC 3d viewer and a TextEdit in the middle. The .ExportToImage() method of the OCC viewer allows taking a screenshot of the viewer. But since the Application has a responsive design, the Viewer is resized to be thin(on certain display resolutions) and thus the screenshot also comes out to be thin.
I've tried to resize the window to a particular size and then hide everything except the 3D viewer. This enlarges the viewer thus saving from a cropped screenshot. But when I hide and resize and then take the screenshot, the screenshot still comes out to be thin. Here's the code:
def take_screenshot(self):
Ww=self.frameGeometry().width()
Wh=self.frameGeometry().height()
self.resize(700,500)
self.outputDock.hide() #Dock on the right
self.inputDock.hide() #Dock on the left
self.textEdit.hide() #TextEdit on the Bottom-Middle
self.display.ExportToImage(fName) #Display is the 3d Viewer's display on the Top-Middle
self.resize(Ww,Wh)
self.outputDock.show()
self.inputDock.show()
self.textEdit.show()
I guess this happens because the above .show(), .hide(), .resize() methods of PyQt5 are multithreaded and as soon as I run them they dont run consicutively but parallely. Thus the screenshot is taken before the other processes complete.
Is there a way to resolve this? Or is there a better way?
no multiple threads. events are processed in loops, so called eventloop.
try this:
def take_screenshot(self):
Ww=self.frameGeometry().width()
Wh=self.frameGeometry().height()
self.resize(700,500)
self.outputDock.hide() #Dock on the right
self.inputDock.hide() #Dock on the left
self.textEdit.hide() #TextEdit on the Bottom-Middle
QTimer.singleShot(0, self._capture)
def _capture(self):
# app.processEvents() # open this line if it still doesn't work, I don't know why.
self.display.ExportToImage(fName) #Display is the 3d Viewer's display on the Top-Middle
self.resize(Ww,Wh)
self.outputDock.show()
self.inputDock.show()
self.textEdit.show()

Unable to display image label in second Tkinter frame

I am trying to build a simple gui using Tkinter. The application involves a smaller secondary frame opening up over the primary one upon pressing a button. This secondary frame must contain an image. Image labels appear easily on the primary frame, but on the secondary frame, the image label appears as an empty box the size of the image, with whatever background colour I set.
Here's how I'm doing it:
#send diagram page
def send_diagram():
send_diagram_frame=tk.Frame(frame, bg="#D4BAEC")
send_diagram_frame.place(relx=0.5, rely=0.5, relheight=0.7, relwidth=0.7, anchor="center")
send_diagram_entry_working_image=Image.open('/home/raghav/RemEdi/design/assets/generic_page_entry.png')
send_diagram_entry_image=ImageTk.PhotoImage(send_diagram_entry_working_image)
send_diagram_entry_label=tk.Label(send_diagram_frame, image=send_diagram_entry_image)
send_diagram_entry_label.place(relx=0.5, rely=0.5, anchor="center")
return
As visible, send_diagram() is the command for the button.
I have tried adding another smaller frame inside the secondary frame to contain the image, but that did not work either.
Any help would be greatly helpful. Thanks!
You are creating the new image inside a function, with it's own local namespace. When the function ends the reference to the image will be garbage collected.
You can fix this by saving a reference to the image in the Label widget. Put this line in the function after the image is created:
send_diagram_entry_label.image = send_diagram_entry_image
Here is working code. I have try it and it's working for me.
def make_label_image(parent, img):
label = tk.Label(parent, image=img)
label.place(relx=0.5, rely=0.5, anchor="center")
def send_diagram():
send_diagram_fram = tk.Frame(frame, bg="#D4BAEC")
send_diagram_frame.place(relx=0.5, rely=0.5, relheight=0.7, relwidth=0.7, anchor="center")
send_diagram_fram.pack_propagate(0)
send_diagram_fram.pack()
img = ImageTk.PhotoImage(Image.open('C:/Users/xxxx/Desktop/logo.jpg'))
make_label_image(send_diagram_fram, img)

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) )

How do I increase the speed of RawTurtle?

I'm currently using an embedded turtle canvas in a tkinter window. While it's intuitive that all I need to do is set my turtle to turtle.RawTurtle(canvas), there are some functions that just don't work, and I can't figure out why.
t.clear();t.pu();t.speed(0);t.ht();t.tracer(0)
But I get the error:
AttributeError: 'RawTurtle' object has no attribute 'tracer'
Despite this, many other functions work, such as clear, penup, speed, and hideturtle.
Is there any way of disabling screen updates until the drawing is finished, then manually updating the canvas, with RawTurtle?
The tracer() method is a method of the turtle's screen, not the turtle itself. To get access to it, when embedded under a tkinter window, wrap the canvas in a turtle screen:
screen = turtle.TurtleScreen(canvas)
t = turtle.RawTurtle(screen)
which should give you access to the various screen methods. Then you should be able to use screen.tracer(0) to turn off drawing updates and screen.update() to show the finished drawing. A more complete example:
import tkinter as tk
import turtle
root = tk.Tk()
canvas = turtle.ScrolledCanvas(root)
canvas.pack(side=tk.LEFT)
screen = turtle.TurtleScreen(canvas)
t = turtle.RawTurtle(screen)
t.hideturtle()
# t.speed('fastest')
screen.tracer(0)
t.penup()
t.sety(-100)
t.pendown()
t.circle(100)
screen.update()
screen.mainloop()

Window suddenly shrinks after packing a new Frame in Tkinter

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.

Resources