Run .py file in system tray rather than in taskbar window - python

I have a .py file that runs via Task Scheduler when I log into my computer, and it's constantly listening for specific keyboard events to happen. Rather than having it run in the Python Console window on the Taskbar, I'd like to get it to run in the system tray with a Python icon to make it more clean.
I have discovered the pysystray library, and can successfully make a Python icon in the system tray with a right-click menu which will allow me to quit the icon.
from os import path as path
import PIL.Image
import pystray
def on_clicked(icon, item):
if str(item) == "Quit":
icon.stop()
dir_path = path.dirname(path.realpath(__file__))
icon_image = "\\Icon\\Python.ico"
full_path = dir_path + icon_image
image = PIL.Image.open(full_path)
icon = pystray.Icon(
"Testing",
image,
menu=pystray.Menu(pystray.MenuItem("Quit", on_clicked)),
)
icon.run()
However, I can't figure out how to get my existing Python file to run under the icon. It seems like there are two separate events happening; one for launching the icon, and another for running the rest of the code. I would want to make sure that if the "Quit" option is chosen, then the .py file would also stop.
Also, it would be really helpful to have a separate right-click menu item which will display either Python cmd line text since it's constantly being overwritten in my main .py file with a print statement.
Any help/insight would be really appreciated!

The pystray.run function accepts an optional callback function as a parameter. The callback function is run in a separate thread and should be closed normally by ln 9: icon.stop().

Related

tkinter GUI executable that will run py files

I'm building a GUI app with tkinter that based on a button it will run another py file that is in the same folder as this GUI. When i'm running the app in my working environment, everything is working fine. When i'm creating an executable app and pressing the buttons, for a second i see a black window being open and then it's being closed and the function that the i assigned the button too is continuing to the next line (a messagebox to say when the script is done running) without running the py file. I'm running the .py files by using os.system('python filename.py') and not sure if that is what causing my problem. Would appreciate any help!
Edit: I saw that for some reason it says that my json file to use for google sheets is looking in the output folder where the auto-py-to-exe is directing it. i added the json file in the bundle but it still looking for it in the wrong place.
Here are a few functions that are assigned to a button click:
# This function will run the python script to create new and styled worksheet.
def run_new_worksheet_code():
return os.system("python script_to_create_automated_sheets_each_week.py")
# This function will let the code run without freezing the GUI and creating the loading bar
# once you pressed the "Create Worksheet" button.
def new_thread_for_wks_code():
global submit_thread
global progress_bar
submit_thread = threading.Thread(target=run_new_worksheet_code)
submit_thread.daemon = True
submit_thread.start()
team_lead_window.after(20, check_when_wks_is_ready)
style = ttk.Style()
style.configure("TProgressbar", background='#30D5C8', troughcolor='#DBDBDB',
bordercolor='#DBDBDB', lightcolor='#30D5C8', darkcolor='#30D5C8')
# Creating a progress bar to indicate that the hours are loading into the google sheets.
progress_bar = ttk.Progressbar(team_lead_window, orient = 'horizontal',
length =300, mode = 'determinate')
progress_bar.place(relx=0.08, rely=0.6, width=250, height=15)
# This function will check every 20 ms if the worksheet is still being prepared
# and when it will finish - a pop-up message will say it's done!
def check_when_wks_is_ready():
if submit_thread.is_alive():
team_lead_window.after(20, check_when_wks_is_ready)
loading_bar_for_wks_update()
else:
progress_bar.destroy()
tk.messagebox.showinfo('New Worksheet', 'New Worksheet Has Created Succsefully!')

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.

change the icon of a windows shortcut .lnk file using python

I want to change the icon of a .lnk file after I have created it. This is my main code so far:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut("shortcut.lnk")
shortcut.Targetpath = "C:\\Users\Benjie\AppData\Local\Programs\Python\Python36\python.exe"
shortcut.save()
This creates a shortcut with the python icon, but I want to change it to a different icon, if possible, to the icon of a different .exe file.
How can I do this?
I'd preferably use one of the windows api librarys, but if this is not possible, an external library would work aswell.
Thanks
Ok, after a few hours of researching stuff with the help of this, I managed to find what I was looking for: shortcut.IconLocation. This sets the icon of a shortcut to an icon from an .exe, .dll .icl or .ico file. For example:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortcut("shortcut.lnk")
shortcut.TargetPath = "C:\\Users\Benjie\AppData\Local\Programs\Python\Python36\python.exe"
shortcut.IconLocation = "C:\path_to_.exe,1"
shortcut.Save()
The icon path is a path to the file, with a comma and the number of the icon in the file. You can see the icons for a file if you create a shortcut and change its icon in its properties, Then browse for the file.

Which is the best way to interact with already open native OS dialog boxes like (Save AS) using Python?

Is there any efficient way using any Python module like PyWind32 to interact with already existing Native OS dialog boxes like 'Save As' boxes?
I tried searching on Google but no help.
EDIT:
1: The Save As dialog box is triggered when user clicks on a Save As dialog box on a web application.
2: Any suggestion are welcome to handle any native OS dialog boxes which are already triggered using Python.(Need not be specific to Selenium webdriver, I am looking for a generic suggestion.)
(When I was posting the question I thought that by 'interacting with a dialog box' will implicitly mean that it is an existing one as if I am able to create one then surely I can interact with it as it is under my programs control. After reading the first 2 answers I realized I was not explicitly clear. Thats why the EDIT )
Thanks
While looking for a possible solution for this I came across several solutions on SO and otherwise.
Some of them were using AutoIT, or editing browsers profile to make it store the file directly without a prompt.
I found all this solution too specific like you can overcome the issue for Save As dialog by editing the browser profile but if later you need to handle some other window then you are stuck.
For using AutoIT is overkill, this directly collides for the fact I choose Python to do this task. (I mean Python is itself so powerful, depending on some other tool is strict NO NO for any Pythonist)
So after a long search for a possible generic solution to this problem which not only serves any one who is looking to handle any Native OS dialog boxes like 'Save As', 'File Upload' etc in the process of automating a web application using selenium web driver but also to any one who wants to interact with a specific window using only Python APIs.
This solution makes use of Win32gui, SendKeys modules of Python.
I will explain first a generic method to get hold of any window desired then a small code addition which will also make this usable while automating a web application using Selenium Webdriver.
Generic Solution::
import win32gui
import re
import SendKeys
class WindowFinder:
"""Class to find and make focus on a particular Native OS dialog/Window """
def __init__ (self):
self._handle = None
def find_window(self, class_name, window_name = None):
"""Pass a window class name & window name directly if known to get the window """
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
'''Call back func which checks each open window and matches the name of window using reg ex'''
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) != None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
""" This function takes a string as input and calls EnumWindows to enumerate through all open windows """
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""Get the focus on the desired open window"""
win32gui.SetForegroundWindow(self._handle)
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
To use this code you need to provide a string which is the name of the window you want to get which in this case is 'Save As'. So similarly you can provide any name and get that window focused.
Once you have the focus of the desired window then you can use SendKeys module to send key strokes to the window which in this case includes sending file path where you want to save the file and ENTER.
Specific to Selenium Webdriver::
The above specified code segment can be used to handle native OS dialog boxes which are triggered through a web application during the automation using Selenium Webdriver with the addition of little bit of code.
The issue you will face which I faced while using this code is that once your automation code clicks on any Web Element which triggers a native OS dialog window, the control will stuck at that point waiting for any action on the native OS dialog window. So basically you are stuck at this point.
The work around is to generate a new thread using Python threading module and use it to click on the Web Element to trigger the native OS dialog box and your parent thread will be moving on normally to find the window using the code I showed above.
#Assume that at this point you are on the page where you need to click on a Web Element to trigger native OS window/dialog box
def _action_on_trigger_element(_element):
_element.click()
trigger_element = driver.find_element_by_id('ID of the Web Element which triggers the window')
th = threading.Thread(target = _action_on_trigger_element, args = [trigger_element]) #Thread is created here to call private func to click on Save button
th.start() #Thread starts execution here
time.sleep(1) #Simple Thread Synchronization handle this case.
#Call WindowFinder Class
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
#At this point the native OS window is interacted with and closed after passing a key stroke ENTER.
# Go forward with what ever your Automation code is doing after this point
NOTE::
When using the above code in automating a web application, check the name of the window you want to find and pass that to find_window_wildcard(). The name of windows are browser dependent. E.g. A window which is triggered when you click on an Element to Upload a file is called 'File Upload' in Firefox and Open in Chrome.
Uses Python2.7
I hope this will help any one who is looking for a similar solution whether to use it in any generic form or in automating a web application.
EDIT:
If you are trying to run your code through command line arguments then try to use the thread to find the window using Win32gui and use the original program thread to click on the element (which is clicked here using the thread). The reason being the urllib library will throw an error while creating a new connection using the thread.)
References::
SO Question
SenKeys
Win32gui
There is a Python module called win32ui. Its found in the Python for Windows extensions package. You want the CreateFileDialog function.
Documentation
Edit:
This is a save dialog example. Check the documentation for the other settings.
import win32ui
if __name__ == "__main__":
select_dlg = win32ui.CreateFileDialog(0, ".txt", "default_name", 0, "TXT Files (*.txt)|*.txt|All Files (*.*)|*.*|")
select_dlg.DoModal()
selected_file = select_dlg.GetPathName()
print selected_file
from time import sleep
from pywinauto import Application, keyboard
app = Application(backend='uia').start('notepad.exe')
app.top_window().set_focus()
main = app.window(title='Untitled - Notepad', control_type='Window')
main.draw_outline(colour='red', thickness=2)
fileMenu = main.child_window(title='File')
fileMenu.draw_outline(colour='red', thickness=2)
fileMenu.click_input()
keyboard.send_keys("{VK_CONTROL}{VK_SHIFT}{S}")
sleep(2)
dlg = main.window(title='Save As')
dlg.draw_outline(colour='red', thickness=2)
savebtn = dlg.child_window(title='Save')
filePath = dlg.child_window(title='File name:', control_type='Edit')
savebtn.draw_outline(colour='red', thickness=2)
filePath.set_text('D:\\aa.txt')
filePath.draw_outline(colour='red', thickness=2)
savebtn.click_input()
I recommend using pywinauto lib.
If you have opened notepad and "Save as" dialog window:
from pywinauto import application
app = application.Application()
app.connect(title='test1.txt - Notepad')
app.SaveAs.Edit1.type_keys("test2.txt")
app.SaveAs.Save.click()

Use selected explorer file or folder path as a variable in a python script for a contextual menu

I'm writing a python script that takes a file one at a time or recursively through folders and moves them to a new location. The script takes one parameter (the current path of the file). I want to be able to use the selected item in an explorer window as the variable.
I am making a contextual menu through the regedit files that is labeled "Send to Server". I currently have the appropriate regedit files created and pointed to the location of the command python.exe "path\to\python\file.py
long story short, I want a contextual menu to pop up that says "Send to Server" when a file is right clicked and when executed uses the selected file's or folder's path as the only variable I need. So far I have come across tkFileDialog (not quite what I want) ctypes and win32 modules but I can't quite figure out the last three modules or whether or not they will help
As a side note. I have created a python script that does this exact thing on mac osx. Much easier with macs 'services' feature.
If you put a shortcut to this script (written for Python 3) in the user's "SendTo" folder (%USERPROFILE%\SendTo), it will pop up a directory dialog when selected from the right-click SendTo menu. The dialog works for network locations as well. When the script runs, the full path to the selected file/folder is in sys.argv[1]. Currently it just shows the selected destination path in a message box. You can change the extension to pyw if you don't want a console.
import os, sys
from tkinter import Tk, filedialog
from tkinter.messagebox import showinfo
class Dialog:
def __init__(self, path):
self.path = path
self.dst_path = ''
self.root = root = Tk()
root.iconify()
root.after_idle(self.askdirectory)
root.mainloop()
def askdirectory(self):
self.dst_path = filedialog.askdirectory(initialdir=self.path)
showinfo('Selected Path', self.dst_path)
self.root.destroy()
if __name__ == '__main__':
if len(sys.argv) > 1:
path = sys.argv[1]
if os.path.isfile(path):
path = os.path.dirname(path)
dialog = Dialog(path)
#if dialog.dst_path: do_something(dialog.dst_path)

Categories