Python notification not working after pyinstaller - python

I have a python script that sends notifications, it works fine in my ide but when I use pyinstaller to create an EXE. the script doesn't seem to work and no notification is played. There is also no errors when opening the EXE, it's just that no notification is being played. Also, when im creating the exe with pyinstaller I make sure to use --hidden-import pyler.platforms.win.notification.
from plyer import notification
def notifyMe(title, message):
notification.notify(
title = title,
message = message,
app_name= "Remind",
app_icon = "clock.ico",
timeout = 25,
)
notifyMe("Test", "test")
This is the code.

You can use the toast notifier library instead of pyler
from win10toast import ToastNotifier
toast = ToastNotifier()
toast.show_toast("Title here", "Message here", duration=10)
If you want to use pyler only, then I'm pretty sure that the problem is with the app icon destination folder. Try changing it to a specific path.
Instead of app_icon = "clock.ico"
Write app_icon = "C:\Users\clock.ico"
If this doesn't work, you can also write app_icon = "C:\\Users\\clock.ico"
When you use Pyinstaller, the location of the python file will no longer be the same which will give an error if you don't specify the exact destination folder.
Hope this answer helps!

Related

Python 3 Windows Service starts only in debug mode

I first posted an answer in this post, but it didn't conform to the forum standards. I hope this time te answer fits the forum standards. This code should be more clear and easy to read.
In Python 3+ I have the following class that I use to build a Windows Service (it does nothing, just writes a log file):
#MyWindowsService.py
import win32serviceutil
import servicemanager
import win32service
import win32event
import sys
import logging
import win32api
class MyWindowsService(win32serviceutil.ServiceFramework):
_svc_name_ = 'ServiceName'
_svc_display_name_ = 'Service Display Name'
_svc_description_ = 'Service Full Description'
logging.basicConfig(
filename = 'c:\\Temp\\{}.log'.format(_svc_name_),
level = logging.DEBUG,
format = '%(levelname)-7.7s # %(asctime)s: %(message)s'
)
def __init__(self, *args):
self.log('Initializing service {}'.format(self._svc_name_))
win32serviceutil.ServiceFramework.__init__(self, *args)
self.stop_event = win32event.CreateEvent(None, 0, 0, None)
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
try:
self.log('START: Service start')
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.start()
win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
except Exception as e:
self.log('Exception: {}'.format(e))
self.SvcStop()
def SvcStop(self):
self.log('STOP: Service stopping...')
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.stop()
win32event.SetEvent(self.stop_event)
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def log(self, msg):
servicemanager.LogInfoMsg(str(msg)) #system log
logging.info(str(msg)) #text log
def start(self):
self.runflag = True
while self.runflag:
win32api.Sleep((2*1000), True)
self.log('Service alive')
def stop(self):
self.runflag = False
self.log('Stop received')
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(MyWindowsService)
In the script I use a log file to check if it's working properly. I'm running python3.6 (also tried with python3.4) on Windows 7 and I'm experiencing the following problem. When I run python MyWindowsService.py install the prompt says that the service has been installed (but nothing is written in the log file). If I try to start the service, I get Service Error: 1 - More info NET HELPMSG 3547 which doesn't say much about the error. If I run python MyWindowsService.py debug, the program runs just fine (the log file is written), but still I don't have any control over the service: if I open another prompt and try to stop/start the service I still got the same results as stated above.
I also tryed to insert some debug code inside the init function, and when I run python MyWindowsService.py install it seems it doesn't get called. Is it possible?
I've checked for multiple solution and workarounds around the net, but I didn't find anything suitable. What am I missing?
As pointed out by eriksun in the comment to the first post, the problem came from the location of the python script, that was in a drive mapped with an UNC path - I'm working with a virtual machine. Moving the python script in the same drive as the python installation did the job.
To sum it up for future uses, if the service fails to start and you're pretty sure about your code, these are helpful actions to try and solve your issues:
use sc start ServiceName, sc query ServiceName and sc stop ServiceName to get info about the service.
check if your file is in a physical drive or in a UNC-mapped drive. If the latter try to run the script using the UNC path (for example python \\Server\share\python\your-folder\script.py) or move your script in the same drive as the python installation
make sure that "python36.dll", "vcruntime140.dll", and "pywintypes36.dll" are either symlink'd to the directory that has PythonService.exe; or symlink'd to the System32 directory; or that the directories with these DLLs are in the system (not user) Path
Check the system register with command reg query HKLM\System\CurrentControlSet\Services\your_service_name /s to get more information about the script
PLease, feel free to complete, change, modify the last so that it can be usefull for anyone that like me encounder this issue.
EDIT: One more thing... My project was thought to work actually with network folders (and UNC-mapped drive) and it failed when I tried to make it run as service. One very useful (day-saving) resource that I used to make it work is the SysinternalsSuite by Mark Russinovich that I found in this post. Hope this helps.

Have icon overlays persist after machine restart in Python

So, thanks to the Tim Golden guide and other questions here I have a script that will show overlays on files and folders based on their "state" similar to Tortoise SVN or Dropbox.
My problem is that once I restart the explorer.exe process or the OS itself and open explorer there are no longer any overlays.
My first thought:
Have the service that actually manages file state detect that no requests have come in and just re-register the overlay handler
The problem here is that registration requires elevated permissions which is acceptable on initial install of the application by the end user but not every time they restart their machine.
Can anyone suggest what I might be missing here?
I have the class BaseOverlay and its children in a single .py file and register from my main app by calling this script using subprocess.
subprocess.check_call(script_path, shell=True)
Is Explorer not able to re-load the script as it is Python? Do I need to compile into a DLL or EXE? Would that change the registration process?
Here's the registration call:
win32com.server.register.UseCommandLine(BaseOverlay)
Here's the class(simplified):
class BaseOverlay:
_reg_clsid_ = '{8D4B1C5D-F8AC-4FDA-961F-A0143CD97C41}'
_reg_progid_ = 'someoverlays'
_reg_desc_ = 'Icon Overlay Handler'
_public_methods_ = ['GetOverlayInfo', 'GetPriority', 'IsMemberOf']
_com_interfaces_ = [shell.IID_IShellIconOverlayIdentifier]
def GetOverlayInfo(self):
return icon_path, 0, shellcon.ISIOI_ICONFILE
def GetPriority(self):
return 50
def IsMemberOf(self, fname, attributes):
return winerror.S_OK

NTEventLogHandler from a Python executable

import logging, logging.handlers
def main():
ntl = logging.handlers.NTEventLogHandler("Python Logging Test")
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
logger.addHandler(ntl)
logger.error("This is a '%s' message", "Error")
if __name__ == "__main__":
main()
The Python (2.7.x) script above writes "This is a 'Error' message" to the Windows Event Viewer. When I run it as a script, I get the expected output. If I convert the script to an executable via PyInstaller, I get an entry in the event log but it says something completely different.
The description for Event ID ( 1 ) in Source ( Python Logging Test ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details. The following information is part of the event: This is a 'Error' message.
This is the command I use to convert the script into an executable: pyinstaller.py --onefile --noconsole my_script.py though the command line parameters do not appear to have any impact on this behaviour and it will suffice to just call pyinstaller.py my_script.py.
I would appreciate any help in understanding what is going on and how I go about fixing this.
Final solution
I didn't want to go down the resource hacker route, as that is going to be a difficult step to automate. Instead, the approach I took was to grab the win32service.pyd file from c:\Python27\Lib\site-packages\win32 and place it next to my executable. The script was then modified pass the full path to the copy of the win32service.pyd file and this works in both script and exe form. The final script is included below:
import logging, logging.handlers
import os
import sys
def main():
base_dir = os.path.dirname(sys.argv[0])
dllname = os.path.join(base_dir, "win32service.pyd")
ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
logger.addHandler(ntl)
logger.error("This is a '%s' message", "Error")
if __name__ == "__main__":
main()
Usually, the Windows Event Log doesn't store error messages in plain text, but rather message ID references and insertion strings.
Instead of storing a message like Service foo crashed unexpectedly, it stores a message ID which points to a resource string stored in a DLL. In this case, the resource would be something like Service %s crashed unexpectedly and foo would be stored as insertion string. The program which writes the message registers the resource DLL.
The reason for this is localization. DLLs can store lots of different resources (dialog layout, strings, icons…), and one DLL can contain the same resource in many different languages. The operating system automatically chooses the right resources depending on the system locale. Resource DLLs are used by virtually all Microsoft utilities and core utilities.
Side note: Nowadays, the preferred (and cross-platform) way for localization is gettext.
This is used for the message log as well – ideally, you could open a log from an German Windows installation on an English one with all messages in English.
I suspect that the pywin32 implementation skips that mechanism by only having one single message ID (1) which is just something like "%s". It is stored in win32service.pyd and registered by pywin32. This works fine as long as this file exists on the file system, but breaks as soon as it's hidden inside a PyInstaller executable. I guess you have to embed the message ID into your executable directly.
Edit: suspicion confirmed, the message table is indeed stored inside win32service.pyd
Resource Hacker showing the message table http://media.leoluk.de/evlog_rh.png
Try to copy the message table resource from win32service.pyd to your PyInstaller executable (for example using Resource Hacker).
Looking at the logging handler implementation, this might work:
def __init__(self, appname, dllname=None, logtype="Application"):
logging.Handler.__init__(self)
try:
import win32evtlogutil, win32evtlog
self.appname = appname
self._welu = win32evtlogutil
if not dllname:
dllname = os.path.split(self._welu.__file__)
dllname = os.path.split(dllname[0])
dllname = os.path.join(dllname[0], r'win32service.pyd')
You'd have to set dllname to os.path.dirname(__file__). Use something like this if you want it to continue working for the unfrozen script:
if getattr(sys, 'frozen', False):
dllname = None
elif __file__:
dllname = os.path.dirname(__file__)
ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)

Launch default image viewer from pygtk program

I'm writing a PyGTK GUI application in Ubuntu to browse some images, and I'd like to open an image in the default image viewer application when it is double-clicked (like when it is opened in Nautilus).
How can I do it?
I don't know specifically using PyGTK but: xdg-open opens the default app for a file so running something like this should work:
import os
os.system('xdg-open ./img.jpg')
EDIT: I'd suggest using the subprocess module as in the comments. I'm not sure exactly how to use it yet so I just used os.system in the example to show xdg-open.
In GNU/Linux use xdg-open, in Mac use open, in Windows use start. Also, use subprocess, if not you risk to block your application when you call the external app.
This is my implementation, hope it helps: http://goo.gl/xebnV
import sys
import subprocess
import webbrowser
def default_open(something_to_open):
"""
Open given file with default user program.
"""
# Check if URL
if something_to_open.startswith('http') or something_to_open.endswith('.html'):
webbrowser.open(something_to_open)
return 0
ret_code = 0
if sys.platform.startswith('linux'):
ret_code = subprocess.call(['xdg-open', something_to_open])
elif sys.platform.startswith('darwin'):
ret_code = subprocess.call(['open', something_to_open])
elif sys.platform.startswith('win'):
ret_code = subprocess.call(['start', something_to_open], shell=True)
return ret_code
GTK (>= 2.14) has gtk_show_uri:
gtk.show_uri(screen, uri, timestamp)
Example usage:
gtk.show_uri(None, "file:///etc/passwd", gtk.gdk.CURRENT_TIME)
Related
How to open a file with the standard application?

retrieving current URL from FireFox with python

I want to know what is the current url of active tab in running firefox instance from python module. Does FireFox have any API for this and does python know to work with it?
The most convenient way maybe insatll a firefox extension to open up a tcp service, then you can exchange info with firefox.
mozrepl can set up a telnet service, you can call js-like command to get info.
With telnetscript (http: //code.activestate.com/recipes/152043/), you can write:
import telnetscript
script = """rve
w content.location.href;
ru repl>
w repl.quit()
cl
"""
conn = telnetscript.telnetscript( '127.0.0.1', {}, 4242 )
ret = conn.RunScript( script.split( '\n' )).split( '\n' )
print ret[-2][6:]
If on windows you can use win32com
import win32clipboard
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate('Some Application Title')
Then use shell.SendKeys to do a ctrl+l and a ctrl+c
Then read the string in the clipboard.
It's horkey though it will work, alternatly you can use something like AutoIt an compile the code to an exe that you can work with.
Hope this helps.

Categories