rumps.notification not working - silently fails to show notification - python-3.x

I have a simple python3.9 rumps app, roughly following the documented example https://rumps.readthedocs.io/en/latest/examples.html.
main.py:
import rumps
class SiMenuBarApp(rumps.App):
def __init__(self):
super(SiMenuBarApp, self).__init__("SiProdHacks")
self.menu = ["Item1"]
#rumps.clicked("Item1")
def item_one(self, _):
print("Hi Si!")
rumps.notification("SiProdHacks", "Keeping Si's Brain Free since 2021", "KAPOWIE!")
if __name__ == '__main__':
app = SiMenuBarApp()
app.icon = "happyicon.png"
app.run()
It runs fine, but when I click the menu bar item1, it prints my console message, but no notification occurs.
I am using python 3.9.0, rumps=0.3.0, iTerm and Mac OS 10.15.7 (Catalina).
Console output is:
❯ pipenv run python main.py
Hi Si!

Ok, I did some more digging on this, and discovered the debug mode of rumps:
import rumps
rumps.debug_mode(True)
which added the following to the output:
In this case there is no file at "/Users/simonrowland/.local/share/virtualenvs/si-menu-productivity-mlyLc7OG/bin/Info.plist"
Running the following command should fix the issue:
/usr/libexec/PlistBuddy -c 'Add :CFBundleIdentifier string "rumps"' /Users/simonrowland/.local/share/virtualenvs/si-menu-productivity-mlyLc7OG/bin/Info.plist
Running the suggested command:
/usr/libexec/PlistBuddy -c 'Add :CFBundleIdentifier string "rumps"' ${PATH_TO_VENV_BIN_DIR}/bin/Info.plist
Made it work!

Related

Python fdpexpect with multiple expects

Testing the simple example that should execute the external application and catch both inputs there.
# external_application.py
print('First_line:')
x = input()
print('Second_line:')
y = input()
print(f'Done: {x} and {y}')
And the runner:
# runner.py
import subprocess
import pexpect.fdpexpect
p = subprocess.Popen(("python", "external_application.py"), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
session = pexpect.fdpexpect.fdspawn(p.stdout.fileno())
session.expect("First_line:")
p.stdin.write(b'1')
print('first_done')
session.expect("Second_line:")
p.stdin.write(b'2')
print('second_done')
p.stdin.close()
print(f'Result: {p.stdout.read()}')
I can see that output of the print('first_done') and then it locks on the second expect. If we remove the second input and a second expect then everything works correctly till the end.
Running on the windows, python 3.7.9, pexpect 4.8.0
Am I missing some timeout or a flush?
When I check the dump, session matches b'First_line:\r\n', but p.stdin.write doesn't move the pexpect cursor forward to look for "Second_line:", as what happens with send() or sendline().
May I suggest a simpler way, using PopenSpawn? The docs state that it "provides an interface like pexpect.spawn interface (sic) using subprocess.Popen":
NOTE - Tested in Windows 11, using Python 3.9.
import pexpect
from pexpect import popen_spawn
session = pexpect.popen_spawn.PopenSpawn("python external_application.py")
session.expect("First_line:")
session.sendline("1")
print("first_done")
session.expect("Second_line:")
session.sendline("2")
print("second_done")
print(session.read().decode("utf-8"))
Output:
first_done
second_done
Done: 1 and 2

Pycomm3 write_tag(tag,value) issue with Micro Logix 1400 PLC

code
from pycomm.ab_comm.slc import Driver as MLDriver
ML = MLDriver()
while 1:
try:
if ML.open('192.168.2.150'):
#print (ML.read_tag('F8:0',10))
print (ML.write_tag('N7:0',10))
ML.close()
except Exception as e:
print (e)
#continue
Error
Error(can only concatenate str (not "bytes") to str) packing the values to write to the SLC write_tag(N7:0,10)
Read tag works fine but the write module is not working in pycomm3
Package Link :-
pip install git+https://github.com/bpaterni/pycomm.git#pycomm3
This was a bug that should have been fixed recently

logging disabled when used with pytest

I am having a problem when using pytest and logging together. When I run a program on its own, I can see its messages printed on screen as well as in the file test.log.
python3 main.py -> prints on terminal, and also in test.log
However, when I am running the same program with pytest, I am seeing the messages only on screen, but the file test.log is not being created.
pytest -vs test -> prints only on terminal, but not in test.log
Why is pytest interfering with the logging utility, and what should I do to create these log files when using pytest?
My versions are the following:
platform linux -- Python 3.6.7, pytest-4.0.2, py-1.7.0, pluggy-0.8.0 -- /usr/bin/python3
The directory structure is the following:
├── logger.py
├── main.py
└── test
├── __init__.py
└── test_module01.py
The code for these files are given below:
# logger.py ===================================
import logging
def logconfig(logfile, loglevel):
print('logconfig: logfile={} loglevel={}..'.format(logfile,loglevel))
logging.basicConfig(filename=logfile, level=logging.INFO, format='%(asctime)s :: %(message)s')
def logmsg(log_level, msg):
print(log_level,': ',msg)
logging.info('INFO: ' + msg)
# main.py =====================================
from datetime import datetime
from logger import *
def main(BASE_DIR):
LOG_FILE = BASE_DIR + 'test.log'
logconfig(LOG_FILE,'INFO')
logmsg('INFO',"Starting PROGRAM#[{}] at {}=".format(BASE_DIR,datetime.now()))
logmsg('INFO',"Ending PROGRAM at {}=".format(datetime.now()))
if __name__ == "__main__":
main('./')
# __init__.py =================================
all = ["test_module01"]
# test_module01.py ============================
import pytest
import main
class TestClass01:
def test_case01(self):
print("In test_case01()")
main.main('./test/')
By default, pytest captures all log records emitted by your program. This means that all logging handlers defined in your code will be replaced with the custom handler pytest uses internally; if you pass -s, it will print the emitted records to the terminal, otherwise it will print nothing and no further output is being made. The internal handler will capture all records emitted from your code. To access them in the tests, use the caplog fixture. Example: imagine you need to test the following program:
import logging
import time
def spam():
logging.basicConfig(level=logging.CRITICAL)
logging.debug('spam starts')
time.sleep(1)
logging.critical('Oopsie')
logging.debug('spam ends')
if __name__ == '__main__':
spam()
If you run the program, you'll see the output
CRITICAL:root:Oopsie
but there's no obvious way to access the debug messages. No problem when using caplog:
def test_spam(caplog):
with caplog.at_level(logging.DEBUG):
spam()
assert len(caplog.records) == 3
assert caplog.records[0].message == 'spam starts'
assert caplog.records[-1].message == 'spam ends'
If you don't need log capturing (for example, when writing system tests with pytest), you can turn it off by disabling the logging plugin:
$ pytest -p no:logging
Or persist it in the pyproject.toml to not having to enter it each time:
[tool.pytest.ini_options]
addopts = "-p no:logging"
The same configuration with (legacy) pytest.cfg:
[pytest]
addopts = -p no:logging
Of course, once the log capturing is explicitly disabled, you can't rely on caplog anymore.

GTK ToolButton text stops displaying after 10 minutes of pc inactivity, in Python gtk mate applet

For some reason the following code works fine but if computer not used for about 10 minutes (no mouse/keyboard interaction) then the ToolButton text stops being displayed on panel and is just blank. The strange thing is if using PC it will stay working for longer, any ideas why or what could cause this sort of inactivity fault?
Think my next step maybe to install a fresh Linux VM to test code in and rule out my install as running out of ideas of things to try.
will also have another go at trying to get code to start from terminal instead of the mate desktop "add to panel" or mate-panel-test-applets commands as i'm sure there are other errors/warnings not being printed via print statements due to being a panel applet.
#Example panel applet output text: "/ = 6G"
I'm Using:
Linux katerina 4.15.0-36-generic #39-Ubuntu SMP Mon Sep 24 16:19:09 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Ubuntu 18.04.1 LTS
GTK 3.22.30
python sys.version = '3.6.6 (default, Sep 12 2018, 18:26:19) \n[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]]
Know controls working as can get click events
Checked screensaver, Power managment options
Tried forceing redraw events on ToolButton
checked ToolButton lable.text updateing still
Checked my disk free code still working so know scheduler working
checked changing disk space text
dd if=/dev/zero of=test bs=1024 count=1000000
Tried setting box.set_above_child(False)
import sys
import logging
import logging.handlers
import os
import shutil
import gi
gi.require_version('MatePanelApplet', '4.0')
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import MatePanelApplet
from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
from hurry.filesize import size
def configure_logger():
#Create a new logger and connect to syslog as invokes as script
my_logger = logging.getLogger("TestPythonApplet")
my_logger.setLevel(logging.DEBUG)
logging.getLogger('apscheduler').setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)s[%(process)d]: %(levelname)s: %(message)s')
handler = logging.handlers.SysLogHandler(address = '/dev/log')
handler.setFormatter(formatter)
my_logger.addHandler(handler)
return my_logger
<pre>
# create the applet. Basicly all your applet code for buttons, etc here
def applet_fill(applet):
#todo: maybe create a applet class like dock_applet.py example
my_logger.debug('Applet started')
#NOTE: Click events not always registered when click using ToolButton
#Think because it dont own its own X-window for preformance reasons as mostly decrotive
#As such dont normaly have to handle x events
#see: https://developer.gnome.org/gtkmm-tutorial/3.0/chapter-widgets-without-xwindows.html.en
#So we wrap in an Gtk.EventBox()
button = Gtk.ToolButton()
box = Gtk.EventBox() #Used to capture mouse events
box.set_above_child(True) #Fixes left clicks not being detected 100%
box.add(button)
box.connect("button_press_event", button_click, applet)
applet.add(box)
# show_image = False
# if show_image:
# #Follow works for showing image
# theme = Gtk.IconTheme().get_default()
# if theme.has_icon("mate"): #Keep things simple and look for known icon installed
# my_logger.debug('Mate icon found')
#
# image = Gtk.Image()
# #<class 'gi.repository.GdkPixbuf.Pixbuf
# i = theme.load_icon("happy", 32, Gtk.IconLookupFlags.NO_SVG)
# s= image.new_from_pixbuf(i) #<class 'gi.repository.Gtk.Image'>
# button.set_icon_widget(s)
# else: #Use text mode
# button.set_label(get_disk_free())
<pre>
button.set_label(get_disk_free())
#Now finnished creating applet start disk
scheduler.start()
scheduler.add_job(set_disk_free, 'interval', args=[applet], seconds=1, id='set_disk_free_id', replace_existing=True) #Args to pass to function set_disk_free
applet.show_all()
return True
</pre>
def set_disk_free(applet):
#TODO: Is there a way to applet.get_child_by_name("GtkToolButton") type function
#Find toolButton widget and set text
for items in applet.get_children(): # Get list of child objects under applet
#my_logger.debug(items.get_name()) # should be a 'gi.repository.Gtk.EventBox'
if items.get_name() == "GtkEventBox":
for items in items.get_children():
if items.get_name() == "GtkToolButton": #Found main tool button
items.set_label(get_disk_free())
items.queue_draw() #lets try forceing a redraw of widget for some reason
#if dont touch pc for 10 min cant see text in pannel
#yet the debug line below shows text is still set and updating
my_logger.debug('Label: \"' + items.get_label() + '\"')
def get_disk_free():
usage_string = '/ = '+ size(shutil.disk_usage('/').free)
my_logger.debug('Updateing:' + str(datetime.now().strftime('%d-%m-%Y %H:%M:%S')) + usage_string )
return usage_string
def button_click(widget, event, applet):
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 1:
my_logger.debug('Left Button clicked')
my_logger.debug('Updating free space text')
set_disk_free(applet)
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
my_logger.debug('Right Button clicked')
def applet_factory(applet, iid, data):
# return False to tell mate we did not create a applet
if iid != "TestPythonApplet":
return False
applet_fill(applet)
return True
#Code starts here
my_logger = configure_logger()
scheduler = BackgroundScheduler()
MatePanelApplet.Applet.factory_main("TestPythonAppletFactory", True,
MatePanelApplet.Applet.__gtype__,
applet_factory, None)
my_logger.debug('Exiting')
scheduler.shutdown()
<pre>
#
#To test:
##########
##Create File: org.mate.panel.applet.TestPythonApplet.service
#[D-BUS Service]
#Name=org.mate.panel.applet.TestPythonAppletFactory
#Exec=/usr/bin/env python3 /home/chiptoxic/Downloads/mate-panel-applet/test_python_applet/test_python_applet.py
#
##Create File: org.mate.panel.TestPythonApplet.mate-panel-applet
#[Applet Factory]
#Id=TestPythonAppletFactory
#InProcess=false
#Location=/home/chiptoxic/Downloads/mate-panel-applet/test_python_applet/test_python_applet.py
#Name=Test Applet Factory
#Description=A MATE Python Applet example factory
#
#[TestPythonApplet]
#Name=Test Python Applet
#Description=A MATE Python Applet example
#Icon=happy
#MateComponentId=OAFIID:MATE_TestPythonApplet
#Simplified path to save space
#sudo ln -s org.mate.panel.TestPythonApplet.mate-panel-applet /usr/share/mate-panel/applets/org.mate.panel.TestPythonApplet.mate-panel-applet
#sudo ln -s org.mate.panel.applet.TestPythonApplet.service /usr/share/dbus-1/services/org.mate.panel.applet.TestPythonApplet.service
#chmod 755 org.mate.panel.TestPythonApplet.mate-panel-applet
#chmod 644 org.mate.panel.TestPythonApplet.service
#tail -f /var/log/syslog
#
#Following will kill process id of applet (have as bash script binded to key for easy testing / reload
#kill $(ps -aux | grep python3 | grep test_python_applet.py | tr -s ' ' | cut -d ' ' -f 2)
#
################################################################################
</pre>

pyside qdialog box or qaction icon is closing app

The issue is a combination of setting an icon on the action in the toolbar (or qpushbutton) and showing a qmessagebox when triggered. If I remove the icon, the message box displays. And if I remove the message box but keep the icon, the app stays open. The other odd thing is, if I add the icon to the push button but not the action, and click on the action, it still closes the app. The doubly odd thing is if I add main.qpush_button_clicked before qapplication.exec_(), the message box is displayed. However, the next time I click on either, it closes the app.
I have looked at multiple posts and some of the ideas were to use setQuitOnLastWindowClosed, but that did not fix the issue. I also implemented event to see what was happening. When I click on either of the items, it triggers a ChildAdded event, then closes.
Also, this only does not work when I use cx_Freeze on a Mac. I have not tried on Win. It works properly when run using Eclipse or from CLI.
Does anyone have any ideas on what might be causing this, or how to fix it besides not using icons.
The icon I used is from Android icon pack.
I can add the crash log if you need it.
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.qicon = QIcon('../ic_add_black_24dp_1x.png')
self.tool_bar = self.addToolBar('main')
qaction = QAction(self)
self.tool_bar.addAction(qaction)
qaction.setText('add')
qaction.setIcon(self.qicon)
qaction.triggered.connect(self.qpush_button_clicked)
qpush_button = QPushButton('add')
self.setCentralWidget(qpush_button)
qpush_button.setIcon(self.qicon)
qpush_button.clicked.connect(self.qpush_button_clicked)
def qpush_button_clicked(self, *args, **kwargs):
QMessageBox.critical(self, 'test', 'testing')
if __name__ == '__main__':
qapplication = QApplication(sys.argv)
main = Main()
main.show()
main.raise_()
qapplication.exec_()
And here is the setup file
name = 'dialog'
version = '0.1'
description = 'description'
packages = ('os',)
excludes = ('tkinter',)
include_files = ('ic_add_black_24dp_1x.png',)
build_exe = dict(packages=packages,
excludes=excludes,
include_files=include_files)
options = dict(build_exe=build_exe)
base = 'Win32GUI' if sys.platform == 'win32' else None
script = 'dialog.py'
executables = (Executable(script, base=base),)
setup(name=name,
version=version,
description=description,
options=options,
executables=executables)
PySide Version : 1.2.2
PySide Component: : (1, 2, 2, 'final', 0)
PySide Compiled Qt : 4.8.7
PySide Qt Component: (4, 8, 7)
Running Qt Version : 4.8.7

Resources