Python tkinter askopenfilename() not opening and responding - python

Python tkinter askopenfilename() is not opening and responding. I searched and tried everything but it didn't work.
What's wrong?
Code:
from tkinter.filedialog import askopenfilenames
import pygame, keyboard, time
import tkinter as tk
from tkinter.filedialog import askopenfilename
from pygame import key
pygame.init()
pygame.mixer.init()
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 450
BLACK = (0, 0, 0)
pyscreen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.RESIZABLE)
def play():
tkscreen = tk.Tk()
tkscreen.iconbitmap("void.ico")
tkscreen.title("Select music file...")
print("b")
global music
tkscreen.update()
music = askopenfilename() # Not responding!!
tkscreen.update()
print("c")
try:
pygame.mixer.music.load(music)
print("d")
pygame.mixer.music.play()
print("e")
except:
print("e-1")
print(f"{music} is not a music file. ")
print("a")
play()
pausecooldown = True
paused = False
while True:
if keyboard.is_pressed("f9"):
if keyboard.is_pressed("space") and pausecooldown:
if paused:
pygame.mixer.music.unpause()
paused = False
time.sleep(0.1)
pausecooldown = False
elif not paused:
pygame.mixer.music.pause()
paused = True
time.sleep(0.1)
pausecooldown = False
pausecooldown = True
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
Version: Python 3.9.1
OS: Windows 10 Home
It prints 'b', and opens tkinter and pygame window, but 'Select file' window is not opening, and tkinter and pygame window is not responding.

I found an answer:
Every tkinter instance requires a mainloop() method to be run. This method essentially
monitors what's happening with the app and reacts to user inputs. Without it, Tkinter
just doesn't work. You can add tkscreen.mainloop() just before the first try block and
it should hopefully solve issues.
from above comment (by pavel)

I know and can understand your problem.
I was doing a Tkinter project once. Well there is no better solution to this. BUT I can help you with this anyways.
I have two solutions which I would like to share it with you.
1. Using Debugger
I used this solution for the program when I was just editing my program. If you use an IDE like VS Code or Pycharm. Run the code in Debugger mode. The program would work and respond.
It will work as wanted.
2. Using .exe file of the project
Use this link to understand how to do that. It will just make your python program run as an application and as per me when I did this, the problem you mentioned didn't come up.
https://datatofish.com/executable-pyinstaller/
If you still get any problem, please comment below and ask anything you want.

Related

Python: Can bring window to front but cannot set focus (win32gui.SetForegroundWindow)

My program pops up a window every time the user presses F2 (in any application).
I'm using pynput to capture the F2 button (works ok)
I'm using tkinter to create the popup window (works ok)
I'm using win32gui.SetForegroundWindow(windowHandel) to bring the tkinter window to the front and set the focus. And there is the problem.
If the python windows is selected when I press F2, everything works ok, and the tkinter window both moves to front and gets focus.
BUT - if any other window is selected when I press F2, the tkinter window does moves to the front, but it is not selected (i.e. focused).
Here is the relevant section from the code (find full code below):
while not windowFound and counter < MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW:
try:
windowHandel = win32gui.FindWindow(None, windowName)
win32gui.SetForegroundWindow(windowHandel)
except:
windowFound = False
else:
print("Success, Window found on the " + str(counter + 1) + " tries")
windowFound = True
After looking for an answer for a while, I found someone saying that this can be solved by using win32process. So I tried adding:
windowHandelID, _ = win32process.GetWindowThreadProcessId(windowHandel)
win32process.AttachThreadInput(win32api.GetCurrentThreadId(), windowHandelID, True)
win32gui.SetFocus(windowHandel)
Yet, it resulted in the same behavior.
Here below is the full (simplified, without exit conditions) code.
Try pressing F2 while pythong is focused.
And then try pressing F2 while any other window (e.g. notepad) is focused.
You'll see that in one case you can just start writing and the tkinter window will receive the input while in the other case, you'll still have to click the window.
I'd appreciate any help or suggestions.
import pyautogui # For keyboard shortcuts and moving the cursor and selecting the window
import time # For the delay function
from pynput import keyboard # For catching keyboard strokes
import tkinter # GUI
import threading # For Threading
import win32gui # For Setting Focus on the Flipper Window
import win32process
import win32api
# Resetting Variables / Settings
start_flipping_text_sequence = False
ContinueThreads = True
SearchForFlipperWindow = False
window_name = "tk"
MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW = 10
# This function runs in a separate thread
def selectFlipperWindow(windowName):
# Since the thread runs constantly, it will only start looking for the flipper window when this variable is True
global SearchForFlipperWindow
# How many loops should the program go through before it gives up on finding the window
global MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW
# While program was not ended
while True:
# This is False, unless F2 is pressed
if SearchForFlipperWindow:
# Did the program find the flipper window
windowFound = False
counter = 0
while not windowFound and counter < MAX_TRIES_TO_FIND_THE_FLIPPER_WINDOW:
try:
windowHandel = win32gui.FindWindow(None, windowName)
win32gui.SetForegroundWindow(windowHandel)
except:
windowFound = False
else:
print("Success, Window found on the " + str(counter + 1) + " tries")
windowHandelID, _ = win32process.GetWindowThreadProcessId(windowHandel)
win32process.AttachThreadInput(win32api.GetCurrentThreadId(), windowHandelID, True)
win32gui.SetFocus(windowHandel)
windowFound = True
counter += 1
time.sleep(0.1)
SearchForFlipperWindow = False
time.sleep(0.1)
# Execute functions based on the clicked key
def on_press(key):
global start_flipping_text_sequence
# If the user pressed the F2 key
if key == keyboard.Key.f2:
start_flipping_text_sequence = True
def okButton():
root.destroy()
def enter(event):
okButton()
# Assigning event to function
listener = keyboard.Listener(on_press=on_press)
# initiating listener
listener.start()
# Start a thread for searching for the flipper window
selectWindowThread = threading.Thread(target=selectFlipperWindow, args=(window_name,))
selectWindowThread.start()
while 1 == 1:
time.sleep(.05)
if start_flipping_text_sequence:
SearchForFlipperWindow = True
root = tkinter.Tk()
tk_window_input = tkinter.Entry(root, width=100)
tk_window_input.pack(padx=20)
tk_window_input.focus()
# Binds the OK button to the okButton function above
tk_window_ok = tkinter.Button(root, width=20, text="OK", command=okButton)
tk_window_ok.pack(pady=20)
# Binds the "Enter" keyboard key to the "enter" event above
tk_window_input.bind('<Return>', enter)
# the main looper of the tkinter window
# runs until root.destroy() to executed above
root.mainloop()
start_flipping_text_sequence = False
```
What you see is an intentional restriction in Windows. The restriction is described by Raymond Chen in article Foreground activation permission is like love: You can’t steal it, it has to be given to you. Remarks section of the SetForegroundWindow documentation gives more technical details about the restriction.
There are ways to be exempt from the restriction. One good way to do so is described by Raymond Chen in article Pressing a registered hotkey gives you the foreground activation love.
The following code shows one more, strange way to bypass the restriction:
kbd.press(keyboard.Key.alt)
try:
win32gui.SetForegroundWindow(windowHandel)
finally:
kbd.release(keyboard.Key.alt)
where kbd was created like this:
from pynput.keyboard import Controller
kbd = Controller()
Here is an explanation why this workaround works: link.
A good way to get rid of this workaround may be to require a user to press Alt-F2 in order to switch to your application.
Good luck with coding ;-)

tkinter user non closeable window

I am trying to write a program where i have removed the main window close options and providing a exit button to the user to close the program.
After pressing i need to do some processing in the background which would be time consuming, i don't want user to close the program while that is going on accidentally. Is there a way to remove all buttons from the messagebox which is presented ?
import tkinter as tk
from win32api import GetSystemMetrics
from tkinter import messagebox
def on_closing():
pass
def exit():
messagebox.showinfo("Wait", "Please wait for background process to complete")
root.destroy()
root = tk.Tk()
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)
width = GetSystemMetrics(0)
height = GetSystemMetrics(1)
root.geometry('{}x{}'.format(width,height))
exitButton = tk.Button(root,text="Exit",width=15,command=exit)
exitButton.grid(row=0,column=1,padx=6,pady=6)
root.overrideredirect(True)
root.mainloop()
In the Background : There are some files generated on user's machine and i would like to archive them using python library. The files can go maybe sometime at 1GB so i think it would take more amount of time, if the laptop on which it is run is having very less computing power. And this would be the case for my base hence i want them just to wait until that popup is closed. This i can define in user manual.
I am not sure what work you want to do, but for this example I'm doing a work of printing something and then sleeping and then printing it. So this takes about 20 seconds. And in those 20 seconds you wont be able to exit the GUI.
import tkinter as tk
from win32api import GetSystemMetrics
from tkinter import messagebox
import time
import threading
def on_closing():
if started == False: #if work is not going on, then quit
root.destroy()
else: # else show the message.
messagebox.showinfo("Wait", "Please wait for background process to complete")
def work():
global started
started = True #mentioning that the work started
print('Hey')
time.sleep(5)
print('There')
time.sleep(5)
print('Whats Up')
time.sleep(5)
print('Cool?')
time.sleep(5)
started = False #mentioning that the work stopped
started = False #initially work is not started
root = tk.Tk()
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)
width = GetSystemMetrics(0)
height = GetSystemMetrics(1)
root.geometry('{}x{}'.format(width,height))
exitButton = tk.Button(root,text="Exit",width=15,command=on_closing)
exitButton.grid(row=0,column=1,padx=6,pady=6)
Button = tk.Button(root,text="Work",width=15,command=threading.Thread(target=work).start)
Button.grid(row=1,column=1,padx=6,pady=6)
# root.overrideredirect(True)
root.mainloop()
Here, started acts like a flag. You have to set it to True before starting your work and set it to False after it ends.
You can ignore the fact that I created a new button and used threading, it was just to simulate to you an example of work done. Threading helps the GUI to not freeze. Though I'm not sure if this will work with root.overrideredirect(True), but I think you can get rid of it.

Nothing being displayed by Pygame

I am using Pygame for the first time so I am following a tutorial online. I recreated the code almost exactly and the window opens normally but nothing will display. I've tried copying other examples for simple Pygame setup and each time I run it the window opens but nothing else happens.
I am using python 3.7.5
Here is my code for reference:
import pygame
from pygame.locals import *
def game_init() :
global SURFACE_MAIN
SURFACE_MAIN = pygame.display.set_mode( ( GAME_WIDTH, GAME_HEIGHT ) )
pygame.display.set_caption( "rouge_like" )
def game_draw() :
# Clear surface
SURFACE_MAIN.fill( COLOR_DEFAULT_BG )
# Draw map
# Draw player
SURFACE_MAIN.blit( PLAYER_SPRITE, ( 100, 100 ) )
# Update display
pygame.display.flip()
pygame.display.update()
def game_main_loop() :
while True :
# Process events
for event in pygame.event.get() :
#print(event)
if event.type == QUIT :
pygame.quit()
sys.exit()
# Draw Game
game_draw()
def main() :
pygame.init()
game_init()
game_main_loop()
if __name__ == '__main__' :
main()
Turns out this is an issue with VSCode. Running the program from my regular computer terminal works just fine.

tkinter windows don't go away after being destroyed

I am writing a python script to display images, play music, and show a video on a raspberry pi when inputs are triggered. While i was working on this project I decided I wanted to add a webcam into the script. I played around with different webcam streamers until I found MPlayer which seemed to have the fastest frame rate and used the least resources. problem was, the MPlayer gui was hidden behind the tkinter windows that I was using to display the images. I tried several different things to bring the MPlayer window to the front and to make the tkinter windows go away but nothing seemed to work. Here's my code:
import sys
import os
import time
import subprocess
import RPi.GPIO as GPIO
if sys.version_info[0] == 2:
import Tkinter
tkinter = Tkinter
else:
import tkinter
from PIL import Image, ImageTk
import board
import neopixel
x=1
GPIO.setmode(GPIO.BCM)
pixels = neopixel.NeoPixel(board.D10, 38)
pixels.fill((0, 0, 0))
GPIO.setwarnings(False)
GPIO.setup(17,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(27,GPIO.OUT)
GPIO.setup(22,GPIO.OUT)
GPIO.setup(24,GPIO.OUT)
GPIO.setup(25,GPIO.OUT)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.output(17,GPIO.LOW)
GPIO.output(18,GPIO.LOW)
GPIO.output(27,GPIO.LOW)
GPIO.output(22,GPIO.LOW)
GPIO.output(24,GPIO.LOW)
GPIO.output(25,GPIO.LOW)
def showPIL(pilImage, exVar = 0):
try:
root.withdraw()
root.destroy()
root.update()
except:
pass
root = tkinter.Toplevel()
if x == 1:
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
else:
w, h = 100, 100
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
#root.focus_set()
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
canvas = tkinter.Canvas(root,width=w,height=h)
canvas.pack()
canvas.configure(background='black')
imgWidth, imgHeight = pilImage.size
if imgWidth > w or imgHeight > h:
ratio = min(w/imgWidth, h/imgHeight)
imgWidth = int(imgWidth*ratio)
imgHeight = int(imgHeight*ratio)
pilImage = pilImage.resize((imgWidth,imgHeight), Image.ANTIALIAS)
image = ImageTk.PhotoImage(pilImage)
imagesprite = canvas.create_image(w/2,h/2,image=image)
root.update()
showPIL(Image.open("Data/blank.png"))
while not GPIO.input(4):
pass
music = subprocess.Popen(['cvlc', '/home/pi/Desktop/Data/music.mp3'])
showPIL(Image.open("Data/trophy.png"))
time.sleep(1)
GPIO.output(22,GPIO.HIGH)
time.sleep(0.5)
GPIO.output(27,GPIO.HIGH)
time.sleep(0.5)
GPIO.output(18,GPIO.HIGH)
time.sleep(0.5)
GPIO.output(17,GPIO.HIGH)
time.sleep(1)
showPIL(Image.open("Data/poison.png"))
pixels.fill((0, 255, 0))
os.system("pkill tk")
x=0
showPIL(Image.open("Data/blank.png"))
x=1
camera = subprocess.Popen(['mplayer', '-fs', 'tv://'])
os.system("wmctrl -a MPlayer")
time.sleep(8)
camera.kill()
os.system("omxplayer -b '/home/pi/Desktop/Data/movie.mp4'")
showPIL(Image.open("Data/gun.png"))
GPIO.output(24,GPIO.HIGH)
GPIO.output(25,GPIO.HIGH)
while not GPIO.input(23):
pass
pixels.fill((0, 0, 0))
showPIL(Image.open("Data/dumbell.png"))
time.sleep(1)
showPIL(Image.open("Data/pipe.png"))
time.sleep(1)
showPIL(Image.open("Data/noose.png"))
time.sleep(1)
music.kill()
showPIL(Image.open("Data/blank.png"))
end = subprocess.Popen(['cvlc', '/home/pi/Desktop/Data/end.wav'])
time.sleep(8)
end.kill()
Ok so there are a few key problems here.
One massive problem is your try/except statement.
Your try except is always going to do pass. There is never going to be a time where you can destroy() something and then call update() on it. This will always result in an error and therefor the except statement of pass will run.
Next root = tkinter.Toplevel() is a problem. Because you never define the tkinter instance or what root should be you create a toplevel window instead and this will result in an instance of tkinter being opened but without a variable name to work with. That said root here is only defined locally to the function and thus any time the function is called again there is not record of root for the function to try to destroy because it has not been created yet as far as the function knows. You will need to define your root as a global variable for something like this.
Even if this works for you there should be 2 problems. One is an extra blank window showing up and 2 is that window not closing on its own when you close the toplevel window.
Next you are trying to use sleep while also running a tkinter instance. These things are not compatible without the use of threading so you need to either work threading into this or preferable learn how to use after(). After() is how tkinter manages timed events.
Instead of using Toplevel() here you needto be using Tk(). Instead of destroying and rebuilding your GUI each update you should just update your window instead. Going on the scope of what you are attempting to do you should probably play around a bit more with tkinter and learn how the event manager works before trying the raspberry pi project. Once you have a sold grasp on the Tkinter GUI and its event based process you will be able to do the more complicated stuff easier.

Message Box or Progess Bar While Running Program

I've been creating a program for myself and my company recently wants to use it. However the end-users have no python experience so I'm making a GUI for them using EasyGUI. All they have to do is click a shortcut on their desktop (I'm using pythonw.exe so no box shows up). The Process takes roughly 10 seconds to run but there is a blank screen when doing so.
My question is: Can i have a message box that says "Running..." while the function runs and then close when the entire process completes?
Bonus points: to have a progress bar while process runs.
Now I've done some searching but some of this stuff is over my head (I'm fairly new to Python). I'm not sure how to incorporate these parts into my code. Is there anything that is easy like EasyGUI to solve my problem? Thanks!
Related posts:
Python- Displaying a message box that can be closed in the code (no user intervention)
How to pop up a message while processing - python
Python to print out status bar and percentage
If you absolutely need to see my code i can try and re-create it without giving away information. The higher-ups would appreciate me not giving away information about this project - Security is tight here.
I've written a little demo for you. Don't know if it's exactly what you wanted...
The code uses threading to update the progressbar while doing other stuff.
import time
import threading
try:
import Tkinter as tkinter
import ttk
except ImportError:
import tkinter
from tkinter import ttk
class GUI(object):
def __init__(self):
self.root = tkinter.Tk()
self.progbar = ttk.Progressbar(self.root)
self.progbar.config(maximum=10, mode='determinate')
self.progbar.pack()
self.i = 0
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.work_thread = threading.Thread(target=work)
self.work_thread.start()
self.root.after(50, self.check_thread)
self.root.after(50, self.update)
def check_thread(self):
if self.work_thread.is_alive():
self.root.after(50, self.check_thread)
else:
self.root.destroy()
def update(self):
#Updates the progressbar
self.progbar["value"] = self.i
if self.work_thread.is_alive():
self.root.after(50, self.update)#method is called all 50ms
gui = GUI()
def work():
#Do your work :D
for i in range(11):
gui.i = i
time.sleep(0.1)
gui.root.mainloop()
Let me know if that helps :)

Categories

Resources