display single image in fullscreen mode (like powerpoint) - python

I am trying to open an image in fullscreen to overlap the taskbar and show no menu bar using python.
I got it to work using Tkinter with following code, but since its just a single image that does not need to be updated (ever), the blocking loop of mainloop() is very inconvenient and unnecessary. However, I can't get tkinter to stay open without that update loop. I've tried using root.after() to run the function afterwards but that froze my program (and isn't really what I want to do anyway)
root = tk.Tk()
root.update_idletasks()
root.attributes('-fullscreen', True)
root.overrideredirect(1)
output = self.create_image_from_array(image_array)
canvas = tk.Canvas(root, width=root.winfo_width(), height=root.winfo_height())
canvas.create_image(0, 0, image=output, anchor="nw")
canvas.pack(fill=tk.BOTH, expand=1)
# after freezes my window
# root.after(0,someFunction)
# main loop blocking the function.
root.mainloop()
How can I display a single image on the entire screen using python (in both windows and linux) or how can I "pause" the tkinter loop to prevent the window from closing and updating?

It looks like you will have to handle tkinter's thread yourself rather than relying on mainloop(). Here is one way to do it:
import tkinter as tk
from threading import Thread
import time
class SomeFunc:
def __init__(self):
self.running=True
t = Thread(target=self.func)
t.start()
def func(self):
self.root = tk.Tk()
self.root.update_idletasks()
#root.attributes('-fullscreen', True)
#root.overrideredirect(1)
canvas = tk.Canvas(self.root, width=self.root.winfo_width(), height=self.root.winfo_height())
#canvas.create_image(0, 0, image=output, anchor="nw")
canvas.pack(fill=tk.BOTH, expand=1)
while self.running:
self.root.update()
a = SomeFunc()
x = 10
while x:
x-=1
time.sleep(1)
print(x)

Related

python tkinter: destroy() not working after using threading.Timer()

Code Part 1
from tkinter import *
from PIL import Image, ImageTk
import threading
c1 = "#262626"
root = Tk()
root.overrideredirect(True)
root.configure(bg=c1)
def destroy():
global root
root.destroy()
def pr():
print("asd")
destroy()
def animation():
threading.Timer(0.01, pr).start()
animation()
root.mainloop()
after the destroy () command has worked, the window is closed, but the program continues to work, you can disable it only with the help of the task manager or CMD, but there's no any errors.
Code Part 2
login = Tk()
img = Image.open(selfDir + "\\ok.png").resize((50, 50), Image.ANTIALIAS)
test = ImageTk.PhotoImage(img)
label1 = Label(login, image=test, bg=c1)
label1.image = test
label1.place(x=330, y=145, width=50, height=50)
login.mainloop()
if I run this part after the first one, I see an error:
tkinter.TclError: can't invoke "image" command: application has been destroyed
but if I run only this part, without the first one, then it works and I can see the photo in the window
So the problem is.
I need to close window after threading.Timer()
Then i need to open window, with an image
i just need to write
root.after(10, pr)
instead of
threading.Timer(0.01, pr).start()
special thanks to Matiiss

How to disable the movement of the Tkinter Window without removing the title bar

I have been creating an application for taking the test. So, for that, I have to do two things.
First, disable the drag of the Tkinter window and don't let the user focus on other windows rather than my application window. This means I wanted to make my application such that, No other application can be used while my application is in use.
Try this:
import tkinter as tk
class FocusedWindow(tk.Tk):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Force it to be unminimisable
super().overrideredirect(True)
# Force it to always be on the top
super().attributes("-topmost", True)
# Even if the user unfoceses it, focus it
super().bind("<FocusOut>", lambda event: self.focus_force())
# Take over the whole screen
width = super().winfo_screenwidth()
height = super().winfo_screenheight()
super().geometry("%ix%i+0+0" % (width, height))
root = FocusedWindow()
# You can use it as if it is a normal `tk.Tk()`
button = tk.Button(root, text="Exit", command=root.destroy)
button.pack()
root.mainloop()
That removed the title bar but you can always create your own one by using tkinter.Labels and tkinter.Buttons. I tried making it work with the title bar but I can't refocus the window for some reason.
One way to do this is by the following, another could be to overwrite the .geometry() method of tkinter.
In the following code I simply had get the position by using winfo_rootx and winfo_rooty. After this you can force the window by calling the geometry method via binding the event every time the window is configured.
import tkinter as tk
def get_pos():
global x,y
x = root.winfo_rootx()
y = root.winfo_rooty()
def fix_pos():
root.bind('<Configure>', stay_at)
def stay_at(event):
root.geometry('+%s+%s' % (x,y))
root = tk.Tk()
button1 = tk.Button(root, text='get_pos', command=get_pos)
button2 = tk.Button(root, text='fix_pos', command=fix_pos)
button1.pack()
button2.pack()
root.mainloop()

Why my progressbar in toplevel window is not moving?

I am actually creatng a gui which include searching data.I want to show a loading progress bar (indeterminate) to show results are being loaded .I want this progress bar in a new toplevel window but I tried to place it but it didn't showed at first.So after many research ,I make it to appear on screen but there is a problem with it. It opens the window with progress bar and everything freezes and progressbar doesn't work itself.But window is destroyed on time.I want that progressbar moves so that it looks responsive so that user gets idea that something is going on.I start progress bar just before loading results and then starts loading results which include for loops for 2-3 times then all results are gathered then the toplevel window is completely destroys .
Everything is happening accept the new window is freezing and progressbar is not moving.
I have tried everything I could.
class Loading:
def __init__(self,title):
self.title = title
self.window = Toplevel()
self.window.geometry('350x40+100+100')
self.window.title(self.title)
self.window.lift()
self.window.grab_set()
self.window.focus()
def start(self):
self.pbar = Progressbar(self.window, orient="horizontal",length=300,
mode="indeterminate")
self.pbar.place(x=10,y=5,height = 20,width = 300)
print('starting loading')
self.pbar.start()
self.window.update()
def stop(self):
self.window.destroy()
I want that my toplevel window display progressbar moving till all rseults are loaded from different files and it gets destroyed after this
Maybe you can try this snippet of code !
from tkinter import *
from tkinter.ttk import *
root = Tk()
root.geometry('100x100')
def progressbar():
pwin = Toplevel(root)
pwin.geometry('350x40+100+100')
pwin.title('Sample progressbar')
pbar = Progressbar(pwin, orient="horizontal", length=300,
mode="indeterminate")
pbar.place(x=10, y=5, height=20, width=300)
print('starting loading')
pbar.start()
pwin.update()
Button(root, text='PBAR', command=progressbar).pack()
root.mainloop()
And then you can use pwin.destroy() to destroy the progressbar after loading is finished !

Python Tkinter, destroy toplevel after function

I'm programming some drives with python using Tkinter as GUI. When my machine is running, I'd like to show the user a toplevel window with some information which should close itself after the function completes. This is my minimal example:
from Tkinter import *
import time
def button_1():
window = Toplevel()
window.title("info")
msg = Message(window, text='running...', width=200)
msg.pack()
time.sleep(5.0)
window.destroy()
master = Tk()
frame = Frame(width=500,height=300)
frame.grid()
button_one = Button(frame, text ="Button 1", command = button_1)
button_one.grid(row = 0, column = 0, sticky = W + E)
mainloop()
The main problem is, that the toplevel window just appears after 5 seconds are over. Any suggestions?
Thanks!
time.sleep(5) is launched before the GUI has time to update, that's why the toplevel only appears after the 5 seconds are over. To correct this, you can add window.update_idletasks() before time.sleep(5) to force the update the display.
But, as Bryan Oakley points out in his answer, the GUI is frozen while time.sleep(5) is executed. I guess that your ultimate goal is not to execute time.sleep but some time consuming operation. So, if you do not want to freeze the GUI but do not know how long the execution will take, you can execute your function in a separated thread and check regularly whether it is finished using after:
import Tkinter as tk
import time
import multiprocessing
def function():
time.sleep(5)
def button_1():
window = tk.Toplevel(master)
window.title("info")
msg = tk.Message(window, text='running...', width=200)
msg.pack()
thread = multiprocessing.Process(target=function)
thread.start()
window.after(1000, check_if_running, thread, window)
def check_if_running(thread, window):
"""Check every second if the function is finished."""
if thread.is_alive():
window.after(1000, check_if_running, thread, window)
else:
window.destroy()
master = tk.Tk()
frame = tk.Frame(width=500,height=300)
frame.grid()
button_one = tk.Button(frame, text ="Launch", command=button_1)
button_one.grid(row = 0, column = 0, sticky = "we")
master.mainloop()
A general rule of thumb is that you should never call sleep in the thread that the GUI is running in. The reason is that sleep does exactly what it says, it puts the whole program to sleep. That means that it is unable to refresh the window or react to any events.
If you want to do something after a period of time, the correct way to do that is with after. For example, this will destroy the window after five seconds:
window.after(5000, window.destroy)

program does not terminate (tkinter + openCV + threading)

I try to build a GUI application to grab frames from a camera and display them in a Tkinter GUI. The Tkinter mainloop is executed in the main thread, while the frame grabbing and updating of the gui takes place in a separate thread.
The code below works as a video stream is grabbed and displayed correctly in my gui window. However when I invoke the on_close() method by clicking the "x" to close the gui, the gui will close, but the program won't terminate fully. Last CLI output will be "Mainloop stopped!", but the program does not terminate as I would expect. So, I suspect the additional thread keeps on running, even though I quit the while loop in the threads run() method via the stop event.
This is my code:
import threading
import cv2
import tkinter
from tkinter import ttk
from PIL import Image
from PIL import ImageTk
import camera
class mainThread(threading.Thread):
def __init__(self, gui):
threading.Thread.__init__(self)
self.stop_event = threading.Event()
self.gui = gui
def stop(self):
print("T: Stop method called...")
self.stop_event.set()
def run(self):
print("Connecting to camera...")
cam = camera.Camera(resolution=(1280, 960), exposure=-3, bit_depth=12)
cam.connect(device_id=0)
while (not self.stop_event.is_set()):
print("running..., stop event = " + str(self.stop_event.is_set()))
# retrieve frame and frame time
frame, _, _ = cam.get_frame()
# display frame
self.gui.updater(frame)
# wait for displaying image
cv2.waitKey(1)
class gui(object):
def __init__(self, root):
self.root = root
# create and start thread running the main loop
self.main_thread = mainThread(self)
self.main_thread.start()
# bind on close callback that executes on close of the main window
self.root.protocol("WM_DELETE_WINDOW", self.on_close)
# change window title
self.root.title("MCT Laser Welding Quality Control")
# set min size of root window and make window non resizable
self.root.minsize(600, 400)
self.root.resizable(False, False)
# configure grid layout
self.root.rowconfigure(0, weight=1)
self.root.rowconfigure(1, weight=1)
self.root.columnconfigure(0, weight=1)
# create image panel
self.image_panel = None
def on_close(self):
self.main_thread.stop()
self.root.destroy()
def updater(self, image):
# TODO: resize frame first
# convert image to tkinter image format
image = Image.fromarray(image)
image = ImageTk.PhotoImage(image)
# if the panel does not exist, create and pack it
if self.image_panel is None:
# show the image in the panel
self.image_panel = ttk.Label(self.root, image=image)
self.image_panel.image = image # keep reference
# pack object into grid
self.image_panel.grid(row=0, column=0)
# just update the image on the panel
else:
self.image_panel.configure(image=image)
self.image_panel.image = image # keep reference
def run(self):
self.root.mainloop()
if __name__ == "__main__":
# create a main window
root = tkinter.Tk()
# set style
style = ttk.Style()
style.theme_use("vista")
# create gui instance
user_interface = gui(root)
# run the user interface
root.mainloop()
print("Mainloop stopped!")
EDIT:
I found out, that the line
image = ImageTk.PhotoImage(image)
is preventing the thread from stopping as described above. When I remove this line and simply update the label text with an incrementing number, everything works as excepted and the thread terminates, when I close the gui window. Any ideas, why ImageTk.PhotoImage() causes the thread to not terminate properly?
You need to join the thread to wait for it to close.
def stop(self):
print("T: Stop method called...")
self.stop_event.set()
self.join()
Also, you may need to close the open connection to the camera.
while (not self.stop_event.is_set()):
...
cam.close() # not sure about exact API here

Categories

Resources