Hiding canvas, part 2 - python-3.x

Gotten further along but now I can't get rid of canvaspopup, even with pack_forget(). Everything else seems to be working okay, granted I'm testing it with a very dumbed down version of the full program so I can post code here without posting 1000+ lines of code.
import tkinter as tk
from tkinter import *
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.keys = dict.fromkeys(('Left', 'Right', 'Up', 'Down', 'Prior', 'Next', 'Shift_L', 'Alt'))
self.frame = tk.Frame(self,bg='gray', width=1366, height=714)
self.frame.pack()
self.canvas = tk.Canvas(self, background="black", width=714, height=714)
self.canvas.pack(fill='both', expand=True)
self.canvas.place(x=652,y=0)
self.canvas.bind("<Alt_L><w>", self.mgram)
self.canvas.bind("<Left>", self.left)
self.canvas.focus_set()
def left(self,event):
print('canvas left')
def popleft(self,event):
print('popup left')
def popescape(self,event):
self.canvas.focus_set()
self.canvaspopup.pack_forget()
def mgram(self,event):
self.canvaspopup = Canvas(self, width=800, height=614)
self.canvaspopup.pack(fill='both', expand=True)
self.canvaspopup.place(x=284,y=52)
self.png = Label(self.canvaspopup)
self.png.pack()
self.popupimage1 = PhotoImage(file='example.png')
self.canvaspopup.bind("<Left>", self.popleft)
self.canvaspopup.bind("<Escape>", self.popescape)
self.canvaspopup.focus_set()
self.png.config(image=self.popupimage1)
s = Canvas(self.canvaspopup, width=800, height=14)
s.pack_propagate(0)
s.place(x=0,y=600)
v = Label(s, fg='black',borderwidth=0,anchor='center',text = 'Image of: ')
v.pack()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
How do I make the popup disappear, supposedly in my previous question Bryan said I could use pack_forget() but that isn't working here. Everything else seems like it working correctly. Still not sure why I couldn't get focus_set to work earlier this morning at home. Not sure what I've changed now???

In an earlier question you asked how to hide a window managed with pack, and I said pack_forget. In this code you're using place so the command would be place_forget.
You call pack but immediately call place right after. Only one geometry manager (pack, place, grid) can manage a widget, and it's always the last one that you use on that widget.

Related

Python scrollable frame and canvas window sizing

I have been trying to find a way to size a frame inside of a canvas window for quite a while to no avail. I finally came across some posts that helped me begin to understand the problem, and eventually dug up a post that gave the solution below:
import tkinter as tk
def onCanvasConfigure(e):
canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
root=tk.Tk()
canvas = tk.Canvas(root, background="blue")
frame = tk.Frame(canvas, background="red")
canvas.pack(expand=True, fill="both")
canvas.create_window((0,0), window=frame, anchor="nw", tags="frame")
canvas.bind("<Configure>", onCanvasConfigure)
root.mainloop()
This completely solves my problem....if I don't have the GUI in a function, which I need to. I have multiple different GUI's that would need to implement this solution. I have come across other solutions that use OOP, but I haven't yet wrapped my head around OOP. I've also found a way to make the above code work inside of a program myself:
import tkinter as tk
def onCanvasConfigure(e):
canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
def test():
window=tk.Tk()
global canvas
canvas = tk.Canvas(master=window)
frame=tk.Frame(master=canvas, background='red')
canvas.pack(expand=True, fill=tk.BOTH)
canvas.create_window((0,0), window=frame, anchor=tk.NW, tags = 'frame')
canvas.bind("<Configure>", onCanvasConfigure)
root.mainloop()
test()
However, this requires the use of a global variable, which I would rather avoid. Is there anything I'm missing that would help me resize the frame inside of the canvas window? If you have any pointers to where I might even find this information that would also be helpful.
The event object that is passed in has a reference to the widget that received the event. So, just replace your global canvas with e.widget, or initialize a local variable named canvas:
def onCanvasConfigure(e):
canvas = e.widget
canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
If it helps, here's an object-oriented version of your application code. Other than the implementation differences, it should behave the same way as the functional version.
import tkinter as tk
class App(tk.Tk): # create a base app class that inherits from Tk
def __init__(self):
super().__init__() # initialize Tk
self.canvas = tk.Canvas(master=self)
self.frame = tk.Frame(master=self.canvas, background='red')
self.canvas.pack(expand=True, fill=tk.BOTH)
self.canvas.create_window(
(0,0),
window=self.frame,
anchor=tk.NW,
tags='frame',
)
self.canvas.bind('<Configure>', self.on_canvas_configure)
def on_canvas_configure(self, event):
self.canvas.itemconfig(
'frame',
height=self.canvas.winfo_height(),
width=self.canvas.winfo_width(),
)
if __name__ == '__main__':
root = App() # instantiate the App class
root.mainloop() # start the app
Since everything here is contained within the App class, you can avoid globals (thanks to self!)

Scheduling order of events with tkinter OOP

I am using tkinter in python 3.6 to display a blue square in the middle of a window. At each click the blue square should disappear and reappear after 2 seconds on a different random location. When running the following code, the blue square (referred as stimulus) does not disappear. Everything else seem to work properly.
This is the code:
import tkinter as TK
import random as RAN
class THR:
def __init__(self, root):
self.root = root
self.root.config(background='black')
self.screenYpixels = 600
self.screenXpixels = 1024
self.ITI = 2000
self.background = TK.Canvas(root, width=1024, height=600, bg='black',
bd=0, highlightthickness=0, relief='ridge')
self.background.pack()
self.newtrial()
def newtrial(self):
self.xpos = RAN.randrange(200, 1000)
self.ypos = RAN.randrange(100, 500)
self.stimulus = TK.Canvas(root,width=100,height=100,bg='blue', bd=0,
highlightthickness=0, relief='ridge')
self.stimulus.place(x=self.xpos, y=self.ypos, anchor="c")
self.stimulus.bind("<Button-1>", self.response)
self.exitbutton()
def response(self, event):
self.stimulus.place_forget()
self.intertrialinterval()
def intertrialinterval(self, *args):
self.root.after(self.ITI,self.newtrial())
def exitbutton(self):
self.exitButton = TK.Button(self.root, bg="green")
self.exitButton.place(relx=0.99, rely=0.01, anchor="c")
self.exitButton.bind("<Button-1>", self.exitprogram)
def exitprogram(self, root):
self.root.quit()
root = TK.Tk()
THR(root)
root.mainloop()
Here a list of things I tried but that did not work
Using time.sleep instead of root.after
Changing the way a newtrial() is called
Putting the sleep or after in different places
The last couple of hours searching the web for similar problems / solutions did not really help. I would like to fix this and at the same time understand what am I doing wrong.
As it turns out the list of links on the right of this webpage provided me with the solution just 3 minutes after posting the question.
This is the original thread Python Tkinter after event OOPS implementation
and here I write the solution:
dropping the parenthesis in the function called with the after method. So this self.root.after(self.ITI,self.newtrial()) is self.root.after(self.ITI,self.newtrial)
Unfortunately I still do not understand how that fixed the problem..

Python3 class function definition confusion about NameError

I'm new to programming and this is my first post on the site. I'm sure I'm making a dumb mistake, but I'd really appreciate a push in the right direction. I'm trying to make a calculator, and want to make a function that produces a Button object for numbers. When I try to run this I get the error:
'NameError: name 'num_but_gen' is not defined'
Here is the code:
from tkinter import *
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 325
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def num_but_gen(self, disp, xloc=0, yloc=0, wid=0, hei=0):
self.Button(text='{}'.format(disp),height=hei, width=wid)
self.place(x=xloc, y=yloc)
def init_window(self):
self.master.title('Calculator')
self.pack(fill=BOTH, expand=1)
Button1 = num_but_gen('1', xloc=0, yloc=200, wid=40, hei=40)
root = Tk()
app = Window(root)
root.geometry("{}x{}".format(WINDOW_WIDTH,WINDOW_HEIGHT))
root.mainloop()
Any help would be greatly appreciated! Also bonus points to anyone with suggestions on how to better phrase my question titles in future posts.
jasonharper is right, you need to add self in front of num_but_gen, but there are other problems in your code.
In num_but_gen:
your window class does not have a Button attribute, so you need to remove self. in front of Button
it is not the Window instance but the button that you want to place
you don't need to use text='{}'.format(disp), text=disp does the same.
In init_window:
you store the result of num_but_gen in a variable, but this function returns nothing so that's useless (and capitalized names should not be used for variables, but for class names only)
the width option of a button displaying text is in letters, not in pixels and its height option is in text lines, so wid=40, hei=40 will create a very big button. If you want to set the button size in pixels, you can do it through the place method instead.
Here is the corresponding code:
import tkinter as tk
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 325
class Window(tk.Frame):
def __init__(self, master = None):
tk.Frame.__init__(self, master)
self.master = master
self.init_window()
def num_but_gen(self, disp, xloc=0, yloc=0, wid=0, hei=0):
button = tk.Button(self, text=disp)
button.place(x=xloc, y=yloc, height=hei, width=wid)
def init_window(self):
self.master.title('Calculator')
self.pack(fill=tk.BOTH, expand=1)
self.num_but_gen('1', xloc=0, yloc=200, wid=40, hei=40)
root = tk.Tk()
app = Window(root)
root.geometry("{}x{}".format(WINDOW_WIDTH,WINDOW_HEIGHT))
root.mainloop()

It seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console (zeromq v4.1.3)

Anaconda3-2.4.0-Windows-x86_64 install including Python 3.5 64-bit and Spyder 2.3.7.
Windows 7 Professional, SP1, 64 bit.
I am trying to follow a series of videos on youtube to add a matplotlib graph to a tkinter window, the first 5 videos cover the initial creation of windows and buttons in the tkinter window which was all fairly simple. The problem occurs when i get to the first matplotlib addition. Even though i copy exactly what the guy in the video does within Python, i get an error where he gets a working program.
Here is a link to the video i am currently attempting to follow:
How to add a Matplotlib Graph to Tkinter Window in Python 3 - Tkinter tutorial Python 3.4 p. 6
There is a list of the other videos in the series to the right when playing.
The error message given within the ipython console is: "It seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console". The message repeats every few seconds untill 'restart kernel' is selected.
In the code below if it is run as is, no windows are opened. If 'canvas.show()' is commented out, a window opens and a python crash error is shown in Windows.
the program runs and the buttons all function correctly if 'canvas.show()', 'canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand = True)' and 'canvas._tkcanvas.pack()' are all commented out.
I have researched this and found the zeromq issue with versions 4.0.6 and 4.1.1 which were fixed in the newer versions. Using the 'conda list' command in the anaconda prompt shows the version number 4.1.3, therefore i do not believe this to be the issue.
I am running in python 3.5 where 'sentdex' (the guy in the video) is running 3.4, could this cause the issue?
Here is my code (minus 3 classes which open windows with buttons on):
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import tkinter as tk
from tkinter import ttk
LARGE_FONT = ("Arial, 12")
class Home(tk.Tk):
def __init__(self, *args):
tk.Tk.__init__(self, *args)
tk.Tk.iconbitmap(self, default="icon1.ico")
tk.Tk.wm_title(self, "Converters & Calculators")
container = tk.Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_columnconfigure(0, weight=1)
container.grid_rowconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, MPL_Tutorial):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Please select a calculator or convertor from the list:", font = LARGE_FONT)
label.pack(pady=20, padx=20)
MPL_Tutorial_button = ttk.Button(self, text = "Graph Page", command=lambda: controller.show_frame(MPL_Tutorial))
MPL_Tutorial_button.pack()
class MPL_Tutorial(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Graph Page", font = LARGE_FONT)
label.pack(pady=20, padx=20)
Home_button = ttk.Button(self, text = "Home", command=lambda: controller.show_frame(StartPage))
Home_button.pack()
f = Figure(figsize = (5,5), dpi=100)
a = f.add_subplot(111)
a.plot([1,2,3,4,5,6,7,8], [5,8,1,3,7,4,9,5])
canvas = FigureCanvasTkAgg(f, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand = True)
toolbar = NavigationToolbar2TkAgg(canvas, self)
toolbar.update()
canvas._tkcanvas.pack()
app = Home()
app.mainloop()
I have edited some of the content while writing the program to reflect the application i eventually want the program for, a calculator / converter for various engineering bits and pieces, such as 'reflection coefficient / VSWR ratio / Return loss conversion'. I believe the only things i have edited are names of things and some padding values.
Any help will be greatly appreciated! I fully expect the answer to be something i have done wrong, i am fairly new to programming as a whole.
Edit: Removed sections of code which referred to the other code i left out as this would undoubtedly cause 'extra' issues if anyone tried to run it.

Python 3 Tkinter Borderless fullscreen application

I have been having a problem whilst creating a Python 3 tkinter application. I am currently developing using a Mac OSX system, but I normally use a Windows OS system.
I would like the application to occupy the entire screen without the Window Manager's titlebar and frame being around the application, often referred to a Fullscreen Borderless Window in gaming.
I have tried using root.attributes("-fullscreen", True) with root.overrideredirect(True) and root.wm_attributes("-topmost", 1). However the inclusion of the root.overrideredirect(True) line doesn't allow it to go proper fullscreen; it still shows the Mac Dock and Taskbar, and it also breaks my keystroke bindings in the application. Without the root.overrideredirect(True) line, the application does go into full screen mode (hiding the dock and the task bar), but the window does not fill the entire screen; it leaves a gap at the bottom, and it also retains the window manager's title bar and frame/border.
Here is an example of my code:
import tkinter as tk
class App(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Fullscreen Application")
self.pack(fill="both", expand=True, side="top")
self.parent.wm_state("zoomed")
self.parent.bind("<F11>", self.fullscreen_toggle)
self.parent.bind("<Escape>", self.fullscreen_cancel)
self.fullscreen_toggle()
self.label = tk.Label(self, text="Fullscreen", font=("default",120), fg="black")
self.label.pack(side="top", fill="both", expand=True)
def fullscreen_toggle(self, event="none"):
self.parent.focus_set()
self.parent.overrideredirect(True)
self.parent.attributes("-fullscreen", True)
self.parent.wm_attributes("-topmost", 1)
def fullscreen_cancel(self, event="none"):
self.parent.overrideredirect(False)
self.parent.attributes("-fullscreen", False)
self.parent.wm_attributes("-topmost", 0)
self.centerWindow()
def centerWindow(self):
sw = self.parent.winfo_screenwidth()
sh = self.parent.winfo_screenheight()
w = sw*0.7
h = sh*0.7
x = (sw-w)/2
y = (sh-h)/2
self.parent.geometry("%dx%d+%d+%d" % (w, h, x, y))
if __name__ == "__main__":
root = tk.Tk()
App(root).pack(side="top", fill="both", expand=True)
root.mainloop()
I hope that someone is able to help! Thank you!
EDIT: I have just tested this on my Windows computer. Without the self.parent.overrideredirect(True), it creates the app and works perfectly as desired (fullscreen without window manager border or title bar). This must just be an OSX problem.
To fix your OS-X Problem i will provide a solution that fixes a similar problem for me. (Had some Issues using fullscreen in between Linux and Windows)
You wanted to get rid of the Window Managers Bar? Take a look at the docs it states an option that removes the window managers items by using -toolwindow option.
Concerning the size of your Application here is what helped me using linux - a "manual zoom":
class MyClass(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.overrideredirect(True) # depending on your needs
self.attributes("-toolwindow", 1) # this line removes the window managers bar
try: # Automatic zoom if possible
self.wm_state("zoomed")
print("Using automatic zoom")
except tk.TclError: # Manual zoom
# Bad Argument Error is a TclError
print("Using manual zoom")
# get the screen dimensions
width = self.winfo_screenwidth()
height = self.winfo_screenheight()
# build a geometry string.
# form: width x height + x_offset + y_offset
geom_string = "%dx%d+0+0" % (width, height)
self.wm_geometry(geom_string)
Please note that I am not using an unconfigured tk.Tk() - Instance here - my class is the tk.Tk() - Instance. So I do not need to overwrite the parent but only "myself" speaking of the POV of the class.
#!/usr/bin/python
import Tkinter as tk
class App(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Fullscreen Application")
self.pack(fill="both", expand=True, side="top")
self.parent.wm_state("zoomed")
self.parent.bind("<F11>", self.fullscreen_toggle)
self.parent.bind("<Escape>", self.fullscreen_cancel)
self.fullscreen_toggle()
self.label = tk.Label(self, text="Fullscreen", font=("default",120), fg="black")
self.label.pack(side="top", fill="both", expand=True)
def fullscreen_toggle(self, event="none"):
self.parent.focus_set()
self.parent.overrideredirect(True)
self.parent.overrideredirect(False) #added for a toggle effect, not fully sure why it's like this on Mac OS
self.parent.attributes("-fullscreen", True)
self.parent.wm_attributes("-topmost", 1)
def fullscreen_cancel(self, event="none"):
self.parent.overrideredirect(False)
self.parent.attributes("-fullscreen", False)
self.parent.wm_attributes("-topmost", 0)
self.centerWindow()
def centerWindow(self):
sw = self.parent.winfo_screenwidth()
sh = self.parent.winfo_screenheight()
w = sw*0.7
h = sh*0.7
x = (sw-w)/2
y = (sh-h)/2
self.parent.geometry("%dx%d+%d+%d" % (w, h, x, y))
if __name__ == "__main__":
root = tk.Tk()
App(root).pack(side="top", fill="both", expand=True)
root.mainloop()

Resources