When you create an application with a GUI using Tkinter in Python, the name of your application appears as "Python" in the menu bar on OS X. How can you get it to appear as something else?
My answer is based on one buried in the middle of some forums. It was a bit difficult to find that solution, but I liked it because it allows you to distribute your application as a single cross platform script. There's no need to run it through py2app or anything similar, which would then leave you with an OS X specific package.
Anyways, I'm sharing my cleaned up version here to give it a bit more attention then it was getting there. You'll need to install pyobjc via pip to get the Foundation module used in the code.
from sys import platform
# Check if we're on OS X, first.
if platform == 'darwin':
from Foundation import NSBundle
bundle = NSBundle.mainBundle()
if bundle:
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
if info and info['CFBundleName'] == 'Python':
info['CFBundleName'] = <Your application name here>
May not be quite what you need but I am surprised no one has mentioned the simple, platform independent way (works with Python 3.x on Win 7) :
from tkinter import Tk
root = Tk()
root.title( "Your title here" ) # or root.wm_title
and if you want to change the icon:
''' Replace the default "Tk" icon with an Application-specific icon '''
''' (that is located in the same folder as the python source code). '''
import sys
from tkinter import PhotoImage
program_directory = sys.path[ 0 ]
IconFile = os.path.join( program_directory ) + "\ApplicationIcon.gif"
IconImage = PhotoImage( file = IconFile )
root.tk.call( 'wm', 'iconphoto', root._w, IconImage )
root.mainloop()
Related
I have an application built with PyQT5 for both Windows and macOS. Currently, the user checks for updates by clicking the button and when there is a new update available I am redirecting them to the browser to my server to download the latest .exe (Windows) or .pkg (macOS). The issue is for say if the user downloads and installs the latest version in a different location than the previous one which will result in two instances of the same application.
I want to improve the user experience and make an auto-updater like all the established applications. When the user clicks the updates the application should download the new updates without making any hassles for the users and update the application for both the OS.
For Windows, I am using Pyinstaller to make the .exe file and then Inno Setup to make it executable. Moreover, for macOS I am using setuptools to make the .app and macOS packages app to make it executable.
It would be really great if someone could help me to implement an update feature for my PyQT5 application.
Hi there I have made a program where it can update itself, I was using tkinter, but it should work for PyQT5 if the same widgets are there. We are using GitHub for the program to download from there. Also I am on windows so I don't know if this works on mac. Here is my code:
import tkinter as tk #for you it is pyqt5
from tkinter import * #MessageBox and Button
import requests #pip install requests
import os #part of standard library
import sys #part of standard library
VERSION = 4
b1 = Button(frame, text = "Back", command = homepage)
b1.pack(ipadx= 10, ipady = 10, fill = X, expand = False, side = TOP)
checkupdate = Label(frame, text = "Looking for updates", font = ("Arial", 14))
checkupdate.pack()
try:
link = "https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/version.txt"
check = requests.get(link)
if float(VERSION) < float(check.text):
mb1 = messagebox.askyesno('Update Available', 'There is an update available. Click yes to update.')
if mb1 is True:
filename = os.path.basename(sys.argv[0])
for file in os.listdir():
if file == filename:
pass
else:
os.remove(file)
exename = f'NameOfYourApp{float(check.text)}.exe'
code = requests.get("https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/NewUpdate.exe", allow_redirects = True)
open(exename, 'wb').write(code.content)
root.destroy()
os.remove(sys.argv[0])
sys.exit()
elif mb1 == 'No':
pass
else:
messagebox.showinfo('Updates Not Available', 'No updates are available')
except Exception as e:
pass
Make sure you convert all the tkinter code to PyQt5 widgets. Also this is windows change the "https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/NewUpdate.exe" to "https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/NewUpdate.app" for mac. If this does not work comment and I will try my best to convert this to PyQt5.
I made a python application in python.
Swaminarayan.py
import tkinter as Tkinter
def main():
top = Tkinter.Tk()
top.geometry("500x500")
B = Tkinter.Button(top, text ="Ghanshyam Maharaj")
B.pack()
top.mainloop()
if __name__ == '__main__':
main()
I converted this code to .exe using cx_freeze in virtual environment
setup.py
import sys
from cx_Freeze import setup, Executable
build_exe_options = {"packages": ["os"], "excludes": []}
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup(
name = "Swaminarayan",
url='https://github.com/ajinzrathod/Swaminarayan',
version = "0.1",
description = "My GUI application!",
options = {
"build_exe": build_exe_options,
"bdist_msi": {
'install_icon': r"F:\executable-python-windows\Swaminarayan\icon.ico",
}
},
executables = [
Executable("Swaminarayan.py",
base=base,
icon=r"path\to\icon.ico",
shortcutName='Swaminarayan',
shortcutDir='DesktopFolder',
)
]
)
and then created MS Installer
py setup.py bdist_msi
This creates an installer and when I installed Swaminarayan.exe, it opens normally without a console window.
I wanted my Swaminarayan Application to start at boot.
So I created a batch file in C:\Users\Ajinkya Rathod\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup named open.bat
And pasted the following code
"C:\Users\Ajinkya Rathod\AppData\Local\Programs\Swaminarayan\Swaminarayan.exe"
Which is the path to my installed Swaminarayan.exe
It works perfectly till here.
The main problem is here. When I double-click on the batch file, it also loads a console window. I just want the GUI created with Tkinter. But it also loads the console window.
I can't find the source but some StackOverflow answers asked to used start in a batch script like this
start "C:\Users\Ajinkya Rathod\AppData\Local\Programs\Swaminarayan\Swaminarayan.exe"
But the problem is that including start in batch scripts removes the console window along with the GUI application. I don't want my GUI to vanish. I just want to get rid of Console Window.
Trying this since long time. Any help would be appreciable.
According to Microsoft's documentaiton, start's "title" is optional, but depending on the options used, it can become an issue, like you are experiencing here. Currently your script is starting cmd with a title of the script path.
So include the empty title ""
start "" "%localappdata%\Programs\Swaminarayan\Swaminarayan.exe"
I want to autologin to an app via its system tray icon.
I use the pywinauto module to interact with the tray icon to launch the app and now I have a popup who ask me to log on.
But... I don't know how to interact with it !
This is my icon :
Tray Icon
Here, an extract of my code (works fine) :
_pApp = Application().connect(path='my_app_dir')
taskbar.ClickSystemTrayIcon(1)
_pApp.PopupMenu.menu_item('submenu').click_input()
_pApp.PopupMenu.menu_item('another_submenu').click_input()
How can I interact with the popup authentication window below ?
Popup window
Thanks for your help.
I finally found a solution with pywinauto.keyboard. I don't know if it's clean but it works.
from pywinauto.keyboard import *
[...]
send_keys(_user)
send_keys("{VK_TAB}")
send_keys(_pass)
send_keys("{ENTER}")
Finally, I found exactly the expected behavior :)
Thank you Vasily Ryabov ! Your method is very helpful !
I do not use 'send_keys' anymore.
from tkinter.messagebox import *
from pywinauto import taskbar
from pywinauto.application import Application
[...]
_user = "TOTO"
_pass = "TOTOPASS"
app_dir = r'C:\Program Files\Common Files\App\App.exe'
icon_list = list()
# Getting the name of the icons in the sys tray
for i in range(0, 13):
app_sti = taskbar.SystemTrayIcons.wrapper_object()
app_stv = pulse_sti.button(i).info.text
icon_list.append(app_stv)
# Looking for my app name
try:
if "App_name" in str(icon_list):
app = Application().connect(path=app_dir)
taskbar.ClickSystemTrayIcon("App name")
app.PopupMenu.menu_item('menu').click_input()
app.PopupMenu.menu_item('menu -> submenu').click_input()
app_auth = Application(backend="uia").connect(title_re="Title*", timeout=5)
app_auth_window = app_auth.window(title_re="Title*")
app_auth_window.child_window(title="User :", control_type="Edit").set_text(_user)
app_auth_window.child_window(title="Password :", control_type="Edit").set_text(_pass)
app_auth_window.child_window(title="Connect", control_type="Button").click()
except Exception as error:
showwarning("Error", "App not found !")
Currently, I re-write a part of my code :
from pywinauto import taskbar
from pywinauto.application import Application
from pywinauto.keyboard import *
from tkinter.messagebox import *
[...]
app_dir = r'C:\Program Files\Common Files\App\App.exe'
_user = "TOTO"
_pass = "TOTOPASS"
# Check if my app has its system tray icon in the taskbar
for i in range(0, 10):
tsIcon = taskbar.SystemTrayIcons.wrapper_object()
tsValue = tsIcon.button(i).info.text
# If I find it, the code click on it and its menu until the popup auth window come
if "App_name" in tsValue:
_pApp = Application().connect(path=app_dir)
taskbar.ClickSystemTrayIcon(i)
_pApp.PopupMenu.menu_item('menu').click_input()
_pApp.PopupMenu.menu_item('menu->submenu').click_input()
time.sleep(2)
# When the popup window comes out, I run 'send_keys'
send_keys(_user)
send_keys("{VK_TAB}")
send_keys(_pass)
send_keys("{ENTER}")
else:
showwarning("Error", "App not found !")
[...]
My code seems good ? There is another "most clean" method ?
Thanks
I want to display a custom icon in a PyQt window after freezing the baseline with cx_Freeze. The icon displays fine when the unfrozen script is executed from within the IDE (Spyder, for me). I'm using PyQt5, Python 3.6, and Windows 10. Here is my Python script (IconTest.py) that creates a main window and shows the path to the icon and whether the path exists. The icon file needs to be in the same directory as IconTest.py:
import sys, os
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtGui import QIcon
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(200, 300, 600, 100)
if getattr(sys, 'frozen', False): #If frozen with cx_Freeze
self.homePath = os.path.dirname(sys.executable)
else: # Otherwise, if running as a script (e.g., within Spyder)
self.homePath = os.path.dirname(__file__)
self.iconFileName = os.path.join(self.homePath, 'myIcon.ico')
self.setWindowIcon(QIcon(self.iconFileName))
self.setWindowTitle('Icon')
self.label1 = QLabel(self)
self.label2 = QLabel(self)
self.label1.move(10, 20)
self.label2.move(10, 40)
self.label1.setText("Path to icon file: " + str(self.iconFileName))
self.label2.setText("Does file exit? " + str(os.path.exists(self.iconFileName)))
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Here is my result when running the script from within Spyder (unfrozen). As you can see, there is an icon displayed that resembles a stopwatch:
Here is my setup.py for creating the frozen baseline:
from cx_Freeze import setup, Executable
import os, sys
exeDir = os.path.dirname(sys.executable)
platformsPath = os.path.join(exeDir, "Library\\Plugins\\Platforms\\")
iconPath = os.path.join(os.path.dirname(__file__), "myIcon.ico")
exe=Executable(script="IconTest.py", base = "Win32GUI", icon = iconPath)
includes=[iconPath, platformsPath]
excludes=[]
packages=[]
setup(
version = "0.1",
description = "My Icon Demo",
options = {'build_exe': {'excludes':excludes,'packages':packages,'include_files':includes}},
executables = [exe]
)
Here is my result when running the frozen script (the executable in the build directory). As you can see, the stopwatch icon is replaced with a generic windows icon:
Suggestions?
Interesting question and nice minimal example. After some searching I guess it could have to do with PyQt5 missing a plugin/DLL to display .ico image files in the frozen application. See e.g. How to load .ico files in PyQt4 from network.
If this is true, you have 2 options:
Try the same example with a .png file as window icon
If the plugins directory is included in the frozen application but it cannot find it, try to add the following statements
pyqt_dir = os.path.dirname(PyQt5.__file__)
QApplication.addLibraryPath(os.path.join(pyqt_dir, "plugins"))`
before
app = QApplication(sys.argv)
in your main script. See this answer.
If the plugins directory is not included in the frozen application, you need to tell cx_Freeze to include it using the include_files entry of the build_exe option. Either you manage to dynamically let your setup script include it at the place where PyQt5 is looking for it, using a tuple (source_path_to_plugins, destination_path_to_plugins) in include_files, or you tell PyQt5 where to look for it, using QApplication.addLibraryPath.
In your previous question to this issue you actually had an entry to include a Plugins\\Platforms directory in your setup script, maybe you simply need to repair this include. Please note that cx_Freeze version 5.1.1 (current) and 5.1.0 move all packages into a lib subdirectory of the build directory, in contrary to the other versions.
ANSWER: I have been using the Anaconda platform and read in other posts that there are issues between PyInstaller and Anaconda because of the way Anaconda structures its content. Thinking the same issue might exist with cx_Freeze, I installed Python (no Anaconda) on a different machine and froze the script from this new Python installation.
The icon appeared as expected in the frozen script. To make the icon display properly, I made the following changes to the setup.py script:
Removed import sys
Removed the line exeDir = ...
Removed the line platformsPath = ...
Removed platformsPath from the includes = list
I had a similar problem and posted an answer here.
Basically, I have overcome this issue by storing the file in a python byte array and loading it via QPixmap into a QIcon.
I am doing GUI programming using Tkinter on Python. I am using the grid manager to make widgets. I have created several buttons and I want to upload an image on top of them. When I enter this code, it gives me an escape sequence error.
I heard using PIL is not a good idea? Is that true?
cookImage = PhotoImage(file = "image/C:\Users\terimaa\AppData\Local\Programs\Python\Python36-32\cook.gif")
Windows filenames must be entered as raw strings:
cookImage = PhotoImage(file=r"C:\Users\terimaa\AppData\Local\Programs\Python\Python36-32\cook.gif")
This applies to all of Python, not just PIL.
Use:
path = r"a string with the path of the photo"
Note the r prefix, it means a raw string.
...
img = ImageTk.PhotoImage(Image.open(file=path))
label = tk.Label(root, image = img)
label.something() #pack/grid/place
...
The path can be:
Absolute ("C:\Users\terimaa\AppData\Local\Programs\Python\Python36-32\cook.gif")
Relative ("\cook.gif", depends on where the Python code is)
If you have an image file that is exactly what you want, just open it with BitmapImage or PhotoImage. Note that Tcl/Tk 8.6, which you should have with 3.6 on Windows, also reads .png files. On Windows, prefix the filename with 'r' or use forward slashes: 'C:/User/...'.
The actual PIL package is no longer maintained and only works on 2.x. That is what a new user should not use. The compatible successor, pillow (installed for instance with python -m pip install pillow) is actively maintained and works with 3.x. The compatibility extends to the import statement: import PIL imports pillow. Pillows allows one to manipulate images and to convert many formats to tk format (the ImageTk class).
this is exact code which is most help for move image
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
import os, shutil
class Root(Tk):
def __init__(self):
super(Root,self).__init__()
self.title("thinter Dialog Widget")
self.minsize(640,400)
self.labelFrame = ttk.LabelFrame(self,text="Open A File")
self.labelFrame.grid(column=0,row=1,padx= 20, pady= 20)
self.btton()
def btton(self):
self.button = ttk.Button(self.labelFrame, text="Browse Afile", command=self.fileDailog)
self.button.grid(column=1,row=1)
def fileDailog(self):
self.fileName = filedialog.askopenfilename(initialdir = "/", title="Select A File",filetype=(("jpeg","*.jpg"),("png","*.png")))
self.label = ttk.Label(self.labelFrame, text="")
self.label.grid(column =1,row = 2)
self.label.configure(text = self.fileName)
os.chdir('e:\\')
os.system('mkdir BACKUP')
shutil.move(self.fileName,'e:\\')
if __name__ == '__main__':
root = Root()
root.mainloop()
you could not move image to c drive due to Permission denied: this code work successfully on python 3.8, 3,7