tkFileDialog and pyperclip prevent script from exiting - python

A script with both tkFileDialog and pyperclip imported won't exit. (Python2.7)
Working examples, where my script exits as expected:
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
root.destroy()
As well as:
import pyperclip
print ('whatever')
Yet the following will prevent my script from exiting (raise SystemExit added for emphasis):
import Tkinter, tkFileDialog
import pyperclip
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
root.destroy()
raise SystemExit
Just importing both modules works fine, a tkFileDialog must be opened in order to create the error.
Calling os._exit() or any code that raises SystemExit soft-locks the interpreter or the python-process, when called as a script.
It seems, that the problem occurs when pyperclip is loaded when opening a tkFileDialog, since the following fragment works as expected:
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
root.destroy()
import pyperclip
raise SystemExit
In any case, though, every line of code after the critical part is executed as expected, raising SystemExit will create a soft-lock though.
This can't be used as a workaround though since python doesn't allow unloading of modules.
What am I doing wrong? Any ideas for a workaround?

Not a real solution, but the best I could come up with:
Switching from python 2.7 to python 3.7 (and therefore from Tkinter 8.5 to 8.6) does the trick for me.
Of course, this has a lot of other implications, that I couldn't test.
On a sidenote - since others couldn't replicate the issue:
I got the chance to run my code-snippet on yet another Windows 10-machine - it worked flawlessly with the same setup. So the problem definitely has something to do with the underlying system, not pyperclip or Tkinter itself.

Related

TKinter, pygubu - opening filedialog.askdirectory() hangs program

I'm building an application using tkinter (and pygubu, this is relevant). In my application I need to browse a directory. This is the code of a button click callback (it's just inserting the path of the directory in a textbox object.
def chose_folder(self):
folder = fd.askdirectory()
txt = self.builder.get_object('folder_txt')
txt.delete(0, tk.END)
txt.insert(0, folder)
I've tried this code on Linux Mint 20.2, and it works fine, but on Windows 10, when I click the button, the program hangs and need to be forcibly terminated. However, if I change fd.askdirectory() with fd.askopenfilename(), or literally any other type of dialog, it works fine. The main difference I can see is that on linux, tkinter uses a custom dialog, while on Windows is uses the native Windows Explorer dialog.
Weirdly enough, I tried this minimal code
from tkinter import *
from tkinter.messagebox import *
from tkinter import filedialog as fd
file_name = fd.askdirectory()
print (file_name)
and it works perfectly, so I'm guessing the problem must be that I'm using pygubu to design the ui, but I can't figure out where the problem precisely is. I've found some old issues about this.

Tkinter freezing Python and Windows Explorer

I often used Tkinter to prompt users and get the path to a file. However, I am facing a recurrent issue, when the filedialog appears it often crashes Windows.
The screen freezes, everything is blocked and when I enter the Task Manager I can see "Python is not responding", when I try to kill Python, then the Task Manager itself freezes and my only option then is to reboot my laptop.
Here is a sample code of what I usually do :
import tkinter as tk
from tkinter import filedialog
import os
window=tk.Tk()
currdir=os.getcwd()
path=filedialog.askopenfilename(parent=window, initialdir=currdir, title="Select file")
Am I doing something wrong ? Any tips ? Is it just bad performances from this library ?
Have you tried using a try/ except block so that you'll exit the loop even if you hit an exception?
import tkinter as tk
from tkinter import filedialog
import os
try:
window=tk.Tk()
currdir=os.getcwd()
path=filedialog.askopenfilename(parent=window, initialdir=currdir, title="Select file")
finally:
window.mainloop()
Include
window.mainloop()
At the end of your GUI file and see if it works that way
Thanks #User9701 and #Linden
Following your advice, I updated my code as per the below :
import tkinter as tk
from tkinter import filedialog
import os
try:
window=tk.Tk()
currdir=sos.getcwd()
path=filedalog.asopenfilename(parent=window,initialdir=currdir, title="Select file")
finally:
window.destroy()

When using Tkinter, error: TclError: image "pyimage8" doesn't exist

I keep getting the error, TclError: image "pyimage8" doesn't exist.
It is strange, as the number increases every time I run it?
I'm running python using spyder, dunno whether this affects anything.
Here is my code:
#import tkinter
import Tkinter as tk
homescreenImage = PhotoImage(file="Homescreen.gif")
#create a GUI window.
root = Tk()
#set the title.
root.title("Welcome to the Pit!")
#set the size.
root.geometry("1100x700")
homescreenFrame = tk.Frame(root, width=1100, height = 700)
homescreenFrame.pack()
homescreenLabel = tk.Label(homescreenFrame, image=homescreenImage)
homescreenLabel.pack()
#start the GUI
root.mainloop()
I found that my script would run once and then give me an error on subsequent runs. If I restarted the console, it would run again. I solved the problem by using the following code in the beginning of my script:
import sys
if "Tkinter" not in sys.modules:
from Tkinter import *
It works every time now.
If you import Tkinter as tk you should use the alias tk when calling tk, eg. root = tk.Tk(). Otherwise Python will not find Tk.
You don't need to import PIL for this.
You can not create a Photoimage before you create Tk.
Try this:
import Tkinter as tk
root = tk.Tk()
root.title("Welcome to the Pit!")
root.geometry("1100x700")
homescreenImage = tk.PhotoImage(file="Homescreen.gif")
homescreenFrame = tk.Frame(root, width=1100, height = 700,)
homescreenFrame.pack()
homescreenLabel = tk.Label(homescreenFrame, image=homescreenImage)
homescreenLabel.pack()
root.mainloop()
Be kind and paste the whole error message in your question also.
Following could be the errors:
1) Give the whole path to the file name
eg: "/home/user/Homescreen.gif"
2) If you are using windows and the above doesn't work:
use "\\C:\\home\\Homescreen.gif" (this is because, windows gets confused)
3) If that also, doesn't work, ensure that the directory of your python
program is the same as that of the image.
4) Also, create the photoimage only after you have created the root
window.
5) For some reason, while running in the debugger, if any previous
executions had thrown errors I get the "pyimage doesn't exist" error.
However, if I restart the debugger (or no previously executed scripts
have thrown errors), then the program runs fine.
6) Also, don't import PIL, it's not required.
Try all the above, if it doesn't work let me know.
Hope this helps.
i think this could be due to :
tkinter only supports .png format for images
Yet, there are other ways to add .gif`` instead of PhotoImage```
In my case, it was because I forgot to keep a reference to the image. Try adding this line after creating the label:
homescreenLabel.image=homescreenImage.
You should use Toplevel window that is directly managed by the window manager.
Just change :
root = Tk() to root = Toplevel()

Python tkinter 8.5 import messagebox

The following code runs fine within IDLE, but otherwise I get "NameError: global name 'messagebox' is not defined". However, if I explicitly state from tkinter import messagebox, it runs fine from where ever.
from tkinter import *
from tkinter import ttk
root = Tk()
mainFrame = ttk.Frame(root)
messagebox.showinfo("My title", "My message", icon="warning", parent=mainFrame)
Why does IDLE not need the explicit import statement but elsewhere it is required?
the messagebox is a separate submodule of tkinter, so simply doing a complete import from tkinter:
from tkinter import *
doesn't import messagebox
it has to be explicitly imported like so:
from tkinter import messagebox
in the same way that ttk has to be imported explicitly
the reason it works in idle is because idle imports messagebox for its own purposes, and because of the way idle works, its imports are accessible while working in idle
IDLE is written in Python and uses Tkinter for the GUI, so it looks like your program is using the import statements that IDLE itself is using. However, you should explicitly include the import statement for the messagebox if you want to execute your program outside the IDLE process.
messagebox.showinfo is defined inside tkinter/showinfo.py but when you use from tkinter import * you only import tkinter/__init__.py which holds the definitions of Label, Entry, Button, ... That is how python imports work.
When you use from tkinter import messagebox it looks for messagebox inside tkinter/__init__.py but it can't find it so it tries to import tkinter/messagebox.py
As for the IDLE anomaly, it is a bug in IDLE and I believe that it was patched.

Python Message Box Without huge library dependency

Is there a messagebox class where I can just display a simple message box without a huge GUI library or any library upon program success or failure. (My script only does 1 thing).
Also, I only need it to run on Windows.
You can use the ctypes library, which comes installed with Python:
import ctypes
MessageBox = ctypes.windll.user32.MessageBoxW
MessageBox(None, 'Hello', 'Window title', 0)
Above code is for Python 3.x. For Python 2.x, use MessageBoxA instead of MessageBoxW as Python 2 uses non-unicode strings by default.
There are also a couple prototyped in the default libraries without using ctypes.
Simple message box:
import win32ui
win32ui.MessageBox("Message", "Title")
Other Options
if win32ui.MessageBox("Message", "Title", win32con.MB_YESNOCANCEL) == win32con.IDYES:
win32ui.MessageBox("You pressed 'Yes'")
There's also a roughly equivalent one in win32gui and another in win32api. Docs for all appear to be in C:\Python{nn}\Lib\site-packages\PyWin32.chm
The PyMsgBox module uses Python's tkinter, so it doesn't depend on any other third-party modules. You can install it with pip install pymsgbox.
The function names are similar to JavaScript's alert(), confirm(), and prompt() functions:
>>> import pymsgbox
>>> pymsgbox.alert('This is an alert!')
>>> user_response = pymsgbox.prompt('What is your favorite color?')
A quick and dirty way is to call OS and use "zenity" command (subprocess module should be included by default in any python distribution, zenity is also present in all major linux). Try this short example script, it works in my Ubuntu 14.04.
import subprocess as SP
# call an OS subprocess $ zenity --entry --text "some text"
# (this will ask OS to open a window with the dialog)
res=SP.Popen(['zenity','--entry','--text',
'please write some text'], stdout=SP.PIPE)
# get the user input string back
usertext=str(res.communicate()[0][:-1])
# adjust user input string
text=usertext[2:-1]
print("I got this text from the user: %s"%text)
See the zenity --help for more complex dialogs
You can also use the messagebox class from tkinter:
from tkinter import messagebox
unless tkinter is that huge GUI you want to avoid.
Usage is simple, ie:
messagebox.FunctionName(title, message [, options])
with FuntionName in (showinfo, showwarning, showerror, askquestion, askokcancel, askyesno, askretrycancel).
This one with tkinter.
from tkinter import * #required.
from tkinter import messagebox #for messagebox.
App = Tk() #required.
App.withdraw() #for hide window.
print("Message Box in Console")
messagebox.showinfo("Notification", "Hello World!") #msgbox
App.mainloop() #required.

Categories