Restoring deleted picture on canvas in python tkinter - python-3.x

I am creating a program where I make use of a check button to delete and recover a background image in python tkinter. I got the deleting part. Can someone help with the recovering part? I want to recover the image on clicking the check button.

You can do the same with your background image as I have done with the hi variable.
Like this:
import tkinter as tk
root = tk.Tk()
var = tk.IntVar()
def test():
if var.get():
hi.grid_remove()
else:
hi.grid(column=0, row=1)
hi = tk.Label(text="hi")
hi.grid(column=0, row=1)
c_b = tk.Checkbutton(text="Check", variable=var, command=test)
c_b.grid(column=0, row=0)
root.mainloop()
Note: Don't use destroy(). Use grid.remove or grid.forget() instead, otherwise your image won't be recovered.
I prefer using grid.remove, because if you need to do some change to a widget after making it disappear. Then to get it back grid.forget also won't work properly.

Related

Can You Implement OSX-Native Tab+Space Navigation with ttk.Button()?

Summarize the Problem
I am looking to use tkinter to implement the actions of tabbing and using the spacebar to cycle and activate UI elements, respectively. I am hoping to do so in a way that mimics the native behavior of OSX, as seen in the attachment below. This works fine on most other widgets in ttk.
This is made up of the following:
Allow components to be "focused" by the user using the tab key
Cause components to become visually highlighted when focused
Component can be triggered using the spacebar while focused
And the tricky part, it uses (close to) the native GUI appearance of the Operating System
Other StackOverflow Questions About This
The closest answer on this site I could find was this question. It may be important to note that the question is asked about Python 2.7.9. This question is interesting, because it suggests that changing a tk.Button() to ttk.Button() alleviates the issue, while when testing the code, I have found the opposite to be true. A tk.Button() will (inelegantly) highlight on OSX, whereas a ttk.Button() provides no visual feedback. Below is the code (with imports modified to run in 2021 on Python 3.X)
import tkinter as tk
from tkinter import ttk
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
entry1 = tk.Entry(self)
entry1.pack()
entry2 = tk.Entry(self)
entry2.pack()
# CHANGE BELOW LINE TO A ttk.Button() TO TEST
button1 = tk.Button(self, text="Test", command=self.yup)
button1.pack()
def yup(self):
print("yup")
root = tk.Tk()
app = App(root).pack()
root.mainloop()
My Attempted Solutions
Solution One: Implement buttons normally using ttk.Button() with ttk.Style() to indicate focus
Pros:
Uses native OS style
Accepts focus via tab and activation via spacebar
Cons:
Does not visually indicate focus unless forced to with ttk.Style()
To the extent of my knowledge cannot be given a highlighted border by ttk.Style(), so we must settle for colored text
Example Code:
from tkinter import Tk
from tkinter import ttk
root = Tk()
def one():
print("one")
def two():
print("two")
style = ttk.Style()
style.configure('C.TButton')
style.map('C.TButton',
foreground = [('pressed','red'),('focus','blue')],
background = [('pressed','!disabled','red'),('focus','white')],
relief=[('pressed', 'sunken'),
('!pressed', 'raised')])
# Define lower GUI
B1 = ttk.Button(root, text='Button One', command=one, style='C.TButton')
B1.pack(padx=20, pady=(20,0))
B2 = ttk.Button(root, text='Button Two', command=two, style='C.TButton')
B2.pack(padx=20, pady=10)
root.mainloop()
Solution Two: Use tk.Button() instead
Pros:
Accepts focus via tab and activation via spacebar
Natively highlights button using a border
Cons:
Does not look that appealing, border is misaligned and a plain rectangle
I cannot get many parameters to work properly on OSX, particularly activebackground and highlightthickness, limiting aesthetic options.
Example Code:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
def one():
print("one")
def two():
print("two")
B1 = tk.Button(root, text='Button One', command=one)
B1.pack(padx=20, pady=(20,0))
B2 = tk.Button(root, text='Button Two', command=two)
B2.pack(padx=20, pady=10)
root.mainloop()
Solution Three: Use the tkmacosx library's Button()
Pros:
Custom made for this problem
Highlights on tab-press with OSX-like style
Really, just everything I'm looking for
Cons:
Does not trigger button on spacebar
This last part is interesting, because based on the documentation (takefocus), this should be the expected behavior. On my machine, this is not the case (Catalina 10.15.7)
Example Code:
from tkinter import Tk
from tkmacosx import Button
root = Tk()
def one():
print("one")
def two():
print("two")
B1 = Button(root, text='Button One', command=one)
B1.pack(padx=20, pady=(20,0))
B2 = Button(root, text='Button Two', command=two)
B2.pack(padx=20, pady=10)
root.mainloop()
Concluding
Historically I understand that tkinter and OSX have not always played together perfectly, and that if I want more precise native control, I might switch to QT. I am immensely thankful for the existence of tkinter, and hope I am not asking too much.
I do want to be sure however that I'm not making an error before I try forking a repo or pursuing other solutions.
Regarding tkmacosx, it seems like this solution should be working the way it is described in the documentation, and I was hoping to get confirmation of this problem from another user, to see if it would be appropriate to file an issue on the github page.
Thank you very much for reading this post. Please feel free to ask for any additional info!

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)

Asking for user input without input() python 3

for my program i am using tkinter for the GUI. Before the program starts i want to have to input a password. I used the input() function. When running my code in my jupyter notebook everything works fine. So i created and executable file with pyinstaller, but when double clicking it won´t start and ask for the input. Since i often used pyinstaller i don´t think i failed in creating the exe-file, so my guess is, that the problem lies within the input() function. Is there another way to ask for user input?
I tried creating a window with an entry widget via Toplevel but i am not quite sure how to implement it since i want to start the program AFTER i entered the password.
My relevant code:
if __name__=='__main__':
root = tkinter.Tk()
asd = input("Enter the password:")
if asd == str(12345):
app = GUI(master=root)
app.master.title("Programm Links")
app.master.minsize(600,400)
root.config(menu=app.menubar)
app.center(root)
app.mainloop()
else:
root.destroy()
So with help of the comments on my question i got an answer:
import tkinter
from tkinter import messagebox
from tkinter import simpledialog
if __name__=='__main__':
root = tkinter.Tk()
root.withdraw()
asd = tkinter.simpledialog.askstring("Password","Enter the password:")
if asd == str(12345):
app = GUI(master=root)
app.master.title("Programm Links")
app.master.minsize(600,400)
root.config(menu=app.menubar)
app.center(root)
app.mainloop()
else:
messagebox.showwarning("WRONG PASSWORD","You entered a wrong password")
root.destroy()
This creates a dialogbox that asks for a user input. root.withdraw() hides the root window frame that gets created by root = tkinter.Tk() which is needed for the dialogbox to run.

Tkinter best button text flipping method

So I'm working on a tkinter project and one issue I come across is finding a way to flip/rotate a button object's text vertically. One way I can kinda cheat into making this happen is putting a canvas object on top of the button with the canvas being drawn last (as shown below) but is there a cleaner way to approach this by just manipulating the Button object attributes?
from tkinter import*
root = Tk()
windowDimensions = (1300,600)
root.title("Mapper")
root.geometry(str(windowDimensions[0])+"x"+str(windowDimensions[1]))
button1=Button(root,text='',width=2,height=9)
button1.place(x=0,y=20)
can = Canvas(root,width=15,height=80)
can.place(x=2,y=30)
can.create_text(0, 80, anchor="nw", angle=90,text='hello',font=("Purisa", 12))
root.mainloop()
Edit: A problem I get with doing it this way is any place where the canvas is on the button, it obstructs the ability to click where the canvas is.
Your best option (which isn't a great option) is to screenshot the button, rotate it in an image editor, and then use that image in your button instead of text.
from tkinter import*
root = Tk()
# .gif file encoded as base64
vert_button_data = '''
R0lGODlhDQBCAKUkAAAAAAAANgAAYDYAADYANjYAYGAAAGAANmAAYAA2hzY2h4c2AGA2h4c2NgBg
qzZghzZgq2BgNqtgAKtgNjaHhzaHzmCHh6uHNs6HNmCr8PCrYIfOq4fO8PDOh6vwq6vw8M7w8PDw
q/DwzvDw8P//////////////////////////////////////////////////////////////////
/////////////////////////////////////////////ywAAAAADQBCAAAG/sCRcEgsGo+cpFJJ
PDgPAWeUGKpSMFUM5Qggdo2DDAiU+RY7BoXCoDmOQp1OqLisN5/4A7XqqfqPAm5EERVLRwcGeEd+
f0gZGR9uGQQSEgUcXB1CHQFcXkdhHx8ZA0doBQUGmotxc0QOhZFuGhgNCLAcskUiIbQNAg5HHxwV
DwYSRMQQCBMYckUHAxYbIG5wtQwOGRzVRrwaFwEJRiAcGQ4KCxhE5woNEhodIkXw8oL3+Pn35pCS
lJaYjABYxcnTEDNEQo0qZeRUqlVG4DxLVofJkDyKhjDaSLFiQCEYn+zZWEVYhWAjUBYRoOELwoMj
XILiAOBDBYZnoj201oqOIMePI/BEEanRDwBGbl4avKdUyBIAhi4+SURUCMlG+oIAADs=
'''
windowDimensions = (1300,600)
root.title("Mapper")
root.geometry("{}x{}".format(*windowDimensions))
button1_image = PhotoImage(data=vert_button_data)
button1=Button(root,image=button1_image)
button1.place(x=0,y=20)
root.mainloop()
You'll lose the hover animation but again that's something you can recreate with images.
To get the base64 encoded data from a .gif you can use this:
import codecs
with open('export.gif', 'rb') as f:
print(codecs.encode(f.read(), 'base64').decode())

Stuck in infinite loop after destroying parent window (python, Tkinter)

The problem I am encountering is that I appear to be stuck in an infinite loop, (If I am not, please correct me). I am using tkinter for python 3.6 (64 bit) on windows 10.
In the module I am having an issue with I have 3 entry widgets and 2 buttons. Both buttons call the "destroy()" function in order to kill the parent window.
Below is a heavily abstracted version of my module, the purpose of the module is to take inputs from the entry widget and write them to a file.
def Create():
parent = Tk()
parent.MakeItlookNice
entry1 = Entry(parent)
entry1.insert(INSERT, "Please enter your desired username here")
entry2 = Entry(parent)
entry2.insert(INSERT, "Please enter your desired password here")
entry3 = Entry(parent)
entry3.insert(INSERT, "What is your mother's maiden name")
Submit = tk.Button(parent,
text ="Click here to submit your stuff",
command = lambda: [parent.destroy(),
submit.function()])
Cancel = tk.Button(parent,
text ="Click here to cancel your request",
command = lambda: parent.destroy())
parent.mainloop()
This function is contained within the module "RegisterNewUser". The "Menu" module is the module that called this function. As far as I am aware once parent.destroy() is called there is no more code to execute since it is all contained within parent.mainloop(), therefore the function is finished and the "Menu" module should continue executing.
What should happen:
I want the Submit button to destroy the window, execute the function and then return to the "Menu" module.
I want the cancel button to destroy the window and return to the "Menu" module.
What actually happens:
The window closes, like it is supposed to
But the code inside the "Menu" module does not start executing again
When I go to close the python shell, it warns me that the program is still running
Ultimately my question is, what code is still running and why hasn't it stopped?
Thank you for reading this and if you require more detail please let me know.
EDIT: I have done research on this topic before posting this question. I have read the documentation on both the tk.destroy() function and the tk.mainloop() function, I have also opened up the Tkinter module in IDLE to try and understand what happens at a deeper level but after all this, I was still unable to figure out a solution. This is my first question on stack overflow, please forgive me if I have done anything wrong.
Hmmm, so you say multiple windows? an easier way to achieve a complex UI as such is using a concept called frames. Tkinter allows you to completely change you screen and layout if you switch to a new frames. This might require you to reprogram alot of code. for an example see Switch between two frames in tkinter
Also, Some guy built a really nice Bitcoin monitoring app using tkinter and frames on youtube
I think you would probably benefit from using Toplevel() here.
I have taken the code you provided and added it to a class used to create the main window and to manage the pop up window.
I noticed a few things with you code.
Its obvoious you are importing tkinter twice like this:
import tkinter as tk
from tkinter import *
I can tell from how you have written your entry fields vs your buttons. Do not do this. Instead just used one or the other. I recommend just using import tkinter as tk.
You are using a function to create a new tkinter instance and judging by your question you all ready have a tkinter instance created for your menu. Instead of creating new Tk() instances you can use Toplevel() instead to open a new window that can inherit everything from the main window and should be easier to manage.
You do not really need to use lambda in this situation. I have also removed the lambda function and replaced with a simple command that will work here.
Take a look at the below code and let me know if you have any questions.
import tkinter as tk
class MyApp(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.master.title("Main MENU")
tk.Button(self.master, text="Open top level", command = self.create).pack()
def create(self):
self.top = tk.Toplevel(self.master)
entry1 = tk.Entry(self.top, width = 35)
entry1.pack()
entry1.insert(0, "Please enter your desired username here")
entry2 = tk.Entry(self.top, width = 35)
entry2.pack()
entry2.insert(0, "Please enter your desired password here")
entry3 = tk.Entry(self.top, width = 35)
entry3.pack()
entry3.insert(0, "What is your mother's maiden name")
tk.Button(self.top, text ="Click here to submit your stuff",
command = self.Submit).pack()
tk.Button(self.top, text ="Click here to cancel your request",
command = self.top.destroy).pack()
def Submit(self):
print("Do something here to submit data")
self.top.destroy()
if __name__ == "__main__":
root = tk.Tk()
app1 = MyApp(root)
tk.mainloop()
You can use toplevel() and its library function wait_window() just prior to (or instead of) your mainloop() and your problem will be solved
wait_window() mentioned above worked for me in the code below replacing popup.mainloop() with it, the mainloop() kept my code in an infinite loop upon the popup even after okay button was hit

Resources