Python cpu temp in system tray Linux - python

I'm using tint2 for a panel and want to show the cpu temp as a system tray icon since there aren't any plugins for tint2 that do that and I'd just like to know how to do this anyway whether there was one or not. The script I have so far is:
#! /usr/bin/python
import pygtk,os
pygtk.require("2.0")
import gtk
import egg.trayicon
t = egg.trayicon.TrayIcon("CPUTemp")
cpu_temp=os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()
t.add(gtk.Label(cpu_temp))
t.show_all()
gtk.main()
Basically, it works the first time around but I'd also like it to update every 5 seconds or so. Any help greatly appreciated.

you can define a timer via timeout_add_seconds and update your tray icon in the callback. See if an example below would work for you:
import gtk, gobject, os
class CPUTimer:
def __init__(self, timeout):
self.window = gtk.Window()
vbox = gtk.VBox()
self.window.add(vbox)
self.label = gtk.Label('CPU')
self.label.set_size_request(200, 40)
vbox.pack_start(self.label)
# register a timer
gobject.timeout_add_seconds(timeout, self.timer_callback)
self.window.connect("destroy", lambda w: gtk.main_quit())
self.window.connect("delete_event", lambda w, e: gtk.main_quit())
self.window.show_all()
self.timer_callback()
def timer_callback(self):
cpu_temp = os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()
print 'update CPU: ' + cpu_temp
self.label.set_text('CPU: ' + cpu_temp)
return True
if __name__ == '__main__':
timer = CPUTimer(1) # sets 1 second update interval
gtk.main()
hope this helps, regards

Look at Python's "threading" module. Create a function which runs the command an update gtk.Label's text with new output(t.set_text(str)). And run this function on a thread.
http://docs.python.org/library/threading.html

Related

Identifying why a Python Qt program pauses when not visible on macOS

I have a Qt Python program that logs data over a serial port. I'd like this program to always log data while running even when the application is not visible. Currently, when the application is not visible, the logging will pause after about ~45 seconds. Once the application window becomes visible again, logging resumes. The logging portion of the code is in a second thread using QRunnable and QThreadPool.
I've tried searching for the cause (or solution), but have not had much luck. Part of my problem is that I'm not sure if this issue is related to the OS, IDE, language, etc.
High-level details:
OS: macOS 12.4
IDE: vscode
Language/frameworks: Python3 / Qt (pyside6)
Does anyone have any ideas on why this application/thread might be pausing? Is it possible to have the application to continue to log data even when it is not visible? My hope is that once I'm pointed in the right direction I'll be able to address the issue.
UPDATE
Example code
class LogSignals(QObject):
result = Signal(dict)
class LogWorker(QRunnable):
def __init__():
super().__init__()
self.signals = LogSignals()
def run(self):
try:
for i in range(N_LOG_SAMPLES):
result = self.getSerialData()
self.signals.result.emit(result)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.threadpool = QThreadPool()
def startLog(self):
log_worker = LogWorker()
log_worker.signals.result.connect(self.updateLogData)
self.threadpool.start(log_worker)
#Slot()
def updateLogData(self, result: dict):
#save data
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
You would need to check if your application is active, and start or drop the logging accordingly. You can get this by using:
QWidget *QApplication::activeWindow()
This would return a nullptr if your application does not have an active window. So, you would write something like this:
if (application->activeWindow())
; // Start / Keep logging
else
; // Stop logging

How to detect when a foreign window embedded with QWidget.createWindowContainer closes itself?

I'm embedding another window into a Qt widget using PySide2.QtGui.QWindow.fromWinId(windowId). It works well, but it does not fire an event when the the original X11 window destroys it.
If I run the file below with mousepad & python3 embed.py and press Ctrl+Q, no event fires and I'm left with an empty widget.
How can I detect when the X11 window imported by QWindow.fromWinId is destroyed by its creator?
#!/usr/bin/env python
# sudo apt install python3-pip
# pip3 install PySide2
import sys, subprocess, PySide2
from PySide2 import QtGui, QtWidgets, QtCore
class MyApp(QtCore.QObject):
def __init__(self):
super(MyApp, self).__init__()
# Get some external window's windowID
print("Click on a window to embed it")
windowIdStr = subprocess.check_output(['sh', '-c', """xwininfo -int | sed -ne 's/^.*Window id: \\([0-9]\\+\\).*$/\\1/p'"""]).decode('utf-8')
windowId = int(windowIdStr)
print("Embedding window with windowId=" + repr(windowId))
# Create a simple window frame
self.app = QtWidgets.QApplication(sys.argv)
self.mainWindow = QtWidgets.QMainWindow()
self.mainWindow.show()
# Grab the external window and put it inside our window frame
self.externalWindow = QtGui.QWindow.fromWinId(windowId)
self.externalWindow.setFlags(QtGui.Qt.FramelessWindowHint)
self.container = QtWidgets.QWidget.createWindowContainer(self.externalWindow)
self.mainWindow.setCentralWidget(self.container)
# Install event filters on all Qt objects
self.externalWindow.installEventFilter(self)
self.container.installEventFilter(self)
self.mainWindow.installEventFilter(self)
self.app.installEventFilter(self)
self.app.exec_()
def eventFilter(self, obj, event):
# Lots of events fire, but no the Close one
print(str(event.type()))
if event.type() == QtCore.QEvent.Close:
mainWindow.close()
return False
prevent_garbage_collection = MyApp()
Below is a simple demo script that shows how to detect when an embedded external window closes. The script is only intended to work on Linux/X11. To run it, you must have wmctrl installed. The solution itself doesn't rely on wmctrl at all: it's merely used to get the window ID from the process ID; I only used it in my demo script because its output is very easy to parse.
The actual solution relies on QProcess. This is used to start the external program, and its finished signal then notifies the main window that the program has closed. The intention is that this mechanism should replace your current approach of using subprocess and polling. The main limitation of both these approaches is they will not work with programs that run themselves as background tasks. However, I tested my script with a number applications on my Arch Linux system - including Inkscape, GIMP, GPicView, SciTE, Konsole and SMPlayer - and they all behaved as expected (i.e. they closed the container window when exiting).
NB: for the demo script to work properly, it may be necessary to disable splash-screens and such like in some programs so they can embed themselves correctly. For example, GIMP must be run like this:
$ python demo_script.py gimp -s
If the script complains that it can't find the program ID, that probably means the program launched itself as a background task, so you will have to try to find some way to force it into the foreground.
Disclaimer: The above solution may work on other platforms, but I have not tested it there, and so cannot offer any guarantees. I also cannot guarantee that it will work with all programs on Linux/X11.
I should also point out that embedding external, third-party windows is not officially supported by Qt. The createWindowContainer function is only intended to work with Qt window IDs, so the behaviour with foreign window IDs is strictly undefined (see: QTBUG-44404). The various issues are documentented in this wiki article: Qt and foreign windows. In particular, it states:
A larger issue with our current APIs, that hasn't been discussed yet,
is the fact that QWindow::fromWinId() returns a QWindow pointer, which
from an API contract point of view should support any operation that
any other QWindow supports, including using setters to manipulate the
window, and connecting to signals to observe changes to the window.
This contract is not adhered to in practice by any of our platforms,
and the documentation for QWindow::fromWinId() doesn't mention
anything about the situation.
The reasons for this undefined/platform specific behaviour largely
boils down to our platforms relying on having full control of the
native window handle, and the native window handle often being a
subclass of the native window handle type, where we implement
callbacks and other logic. When replacing the native window handle
with an instance we don't control, and which doesn't implement our
callback logic, the behaviour becomes undefined and full of holes
compared to a regular QWindow.
So, please bear all that in mind when designing an application that relies on this functionality, and adjust your expectations accordingly...
The Demo script:
import sys, os, shutil
from PySide2.QtCore import (
Qt, QProcess, QTimer,
)
from PySide2.QtGui import (
QWindow,
)
from PySide2.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QMessageBox,
)
class Window(QWidget):
def __init__(self, program, arguments):
super().__init__()
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
self.external = QProcess(self)
self.external.start(program, arguments)
self.wmctrl = QProcess()
self.wmctrl.setProgram('wmctrl')
self.wmctrl.setArguments(['-lpx'])
self.wmctrl.readyReadStandardOutput.connect(self.handleReadStdOut)
self.timer = QTimer(self)
self.timer.setSingleShot(True)
self.timer.setInterval(25)
self.timer.timeout.connect(self.wmctrl.start)
self.timer.start()
self._tries = 0
def closeEvent(self, event):
for process in self.external, self.wmctrl:
process.terminate()
process.waitForFinished(1000)
def embedWindow(self, wid):
window = QWindow.fromWinId(wid)
widget = QWidget.createWindowContainer(
window, self, Qt.FramelessWindowHint)
self.layout().addWidget(widget)
def handleReadStdOut(self):
pid = self.external.processId()
if pid > 0:
windows = {}
for line in bytes(self.wmctrl.readAll()).decode().splitlines():
columns = line.split(maxsplit=5)
# print(columns)
# wid, desktop, pid, wmclass, client, title
windows[int(columns[2])] = int(columns[0], 16)
if pid in windows:
self.embedWindow(windows[pid])
# this is where the magic happens...
self.external.finished.connect(self.close)
elif self._tries < 100:
self._tries += 1
self.timer.start()
else:
QMessageBox.warning(self, 'Error',
'Could not find WID for PID: %s' % pid)
else:
QMessageBox.warning(self, 'Error',
'Could not find PID for: %r' % self.external.program())
if __name__ == '__main__':
if len(sys.argv) > 1:
if shutil.which(sys.argv[1]):
app = QApplication(sys.argv)
window = Window(sys.argv[1], sys.argv[2:])
window.setGeometry(100, 100, 800, 600)
window.show()
sys.exit(app.exec_())
else:
print('could not find program: %r' % sys.argv[1])
else:
print('usage: python %s <external-program-name> [args]' %
os.path.basename(__file__))

How to attach and detach an external app with PyQT5 or dock an external application?

I'm developing an GUI for multi-robot system using ROS, but i'm freezing in the last thing i want in my interface: embedding the RVIZ, GMAPPING or another screen in my application. I already put an terminal in the interface, but i can't get around of how to add an external application window to my app. I know that PyQt5 have the createWindowContainer, with uses the window ID to dock an external application, but i didn't find any example to help me with that.
If possible, i would like to drag and drop an external window inside of a tabbed frame in my application. But, if this is not possible or is too hard, i'm good with only opening the window inside a tabbed frame after the click of a button.
I already tried to open the window similar to the terminal approach (see the code bellow), but the RVIZ window opens outside of my app.
Already tried to translate the attaching/detaching code code to linux using the wmctrl command, but didn't work wither. See my code here.
Also already tried the rviz Python Tutorial but i'm receveing the error:
Traceback (most recent call last):
File "rvizTutorial.py", line 23, in
import rviz
File "/opt/ros/indigo/lib/python2.7/dist-packages/rviz/init.py", line 19, in
import librviz_shiboken
ImportError: No module named librviz_shiboken
# Frame where i want to open the external Window embedded
self.Simulation = QtWidgets.QTabWidget(self.Base)
self.Simulation.setGeometry(QtCore.QRect(121, 95, 940, 367))
self.Simulation.setTabPosition(QtWidgets.QTabWidget.North)
self.Simulation.setObjectName("Simulation")
self.SimulationFrame = QtWidgets.QWidget()
self.SimulationFrame.setObjectName("SimulationFrame")
self.Simulation.addTab(rviz(), "rViz")
# Simulation Approach like Terminal
class rviz(QtWidgets.QWidget):
def __init__(self, parent=None):
super(rviz, self).__init__(parent)
self.process = QtCore.QProcess(self)
self.rvizProcess = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.rvizProcess)
# Works also with urxvt:
self.process.start('rViz', [str(int(self.winId()))])
self.setGeometry(121, 95, 940, 367)
I've not tested this specifically, as I've an old version of Qt5 I can't upgrade right now, while from Qt5 5.10 startDetached also returns the pid along with the bool result from the started process.
In my tests I manually set the procId (through a static QInputBox.getInt()) before starting the while cycle that waits for the window to be created.
Obviously there are other ways to do this (and to get the xid of the window).
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck, Gdk
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.embed('xterm')
def embed(self, command, *args):
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
started, procId = proc.startDetached()
if not started:
QtWidgets.QMessageBox.critical(self, 'Command "{}" not started!')
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
if w.get_pid() == procId:
window = QtGui.QWindow.fromWinId(w.get_xid())
container = QtWidgets.QWidget.createWindowContainer(window, self)
self.addTab(container, command)
return
attempts += 1
QtWidgets.QMessageBox.critical(self, 'Window not found', 'Process started but window not found')
app = QtWidgets.QApplication(sys.argv)
w = Container()
w.show()
sys.exit(app.exec_())
I couldn't get the code in the accepted answer to work on Ubuntu 18.04.3 LTS; even when I got rid of the exceptions preventing the code to run, I'd still get a separate PyQt5 window, and separate xterm window.
Finally after some tries, I got the xterm window to open inside the tab; here is my code working in Ubuntu 18.04.3 LTS (with all the misses commented):
#!/usr/bin/env python3
# (same code seems to run both with python3 and python2 with PyQt5 in Ubuntu 18.04.3 LTS)
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck, Gdk
import time
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.embed('xterm')
def embed(self, command, *args):
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
#started, procId = proc.startDetached()
#pid = None
#started = proc.startDetached(pid)
# https://stackoverflow.com/q/31519215 : "overload" startDetached : give three arguments, get a tuple(boolean,PID)
# NB: we will get a failure `xterm: No absolute path found for shell: .` even if we give it an empty string as second argument; must be a proper abs path to a shell
started, procId = proc.startDetached(command, ["/bin/bash"], ".")
if not started:
QtWidgets.QMessageBox.critical(self, 'Command "{}" not started!'.format(command), "Eh")
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# do a bit of sleep, else window is not really found
time.sleep(0.1)
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
print(attempts, w.get_pid(), procId, w.get_pid() == procId)
if w.get_pid() == procId:
self.window = QtGui.QWindow.fromWinId(w.get_xid())
#container = QtWidgets.QWidget.createWindowContainer(window, self)
proc.setParent(self)
#self.scrollarea = QtWidgets.QScrollArea()
#self.container = QtWidgets.QWidget.createWindowContainer(self.window)
# via https://vimsky.com/zh-tw/examples/detail/python-method-PyQt5.QtCore.QProcess.html
#pid = proc.pid()
#win32w = QtGui.QWindow.fromWinId(pid) # nope, broken window
win32w = QtGui.QWindow.fromWinId(w.get_xid()) # this finally works
win32w.setFlags(QtCore.Qt.FramelessWindowHint)
widg = QtWidgets.QWidget.createWindowContainer(win32w)
#self.container.layout = QtWidgets.QVBoxLayout(self)
#self.addTab(self.container, command)
self.addTab(widg, command)
#self.scrollarea.setWidget(self.container)
#self.container.setParent(self.scrollarea)
#self.scrollarea.setWidgetResizable(True)
#self.scrollarea.setFixedHeight(400)
#self.addTab(self.scrollarea, command)
self.resize(500, 400) # set initial size of window
return
attempts += 1
QtWidgets.QMessageBox.critical(self, 'Window not found', 'Process started but window not found')
app = QtWidgets.QApplication(sys.argv)
w = Container()
w.show()
sys.exit(app.exec_())

Gtk3 with Python, TextView rising multiple 'mark-set' signals

Consider the following example code, which puts a TextView inside a window and registers the mark-set event:
#!/usr/bin/env python3
from gi.repository import Gtk
win = Gtk.Window(title='test')
text_view = Gtk.TextView()
def test (*args):
print('test!')
win.add(text_view)
text_view.get_buffer().connect('mark-set', test)
win.connect('delete-event', Gtk.main_quit)
win.show_all()
Gtk.main()
If I launch it, and I click on the visualized TextView once I get the debug output multiple times:
$ ./test.py 2>/dev/null
test!
test!
test!
test!
Do you know why this is happening? Is there a way of avoiding it?
I don't know why exactly it's happening, but I found something to help you understand, or use it to prevent duplicate actions in handler, using mark.get_name(). Here I changed your code, type some text and play with it (select some text, click somewhere, ...)
#!/usr/bin/env python3
import time
from gi.repository import Gtk
win = Gtk.Window(title='test')
text_view = Gtk.TextView()
def mark_set(buf, itr, mark):
### mark.get_name() ==> 'selection_bound' | 'insert' | None
print('Time: %.2f, Mark Name: %s'%(time.time()%100, mark.get_name()))
win.add(text_view)
text_view.get_buffer().connect('mark-set', mark_set)
win.connect('delete-event', Gtk.main_quit)
win.show_all()
Gtk.main()

Activating and Disabling button after process in python and pyGTK

Essentially, I am trying to make a button "active" first, run a process, and then after that process has finished running, disable the button again.
Using pyGTK and Python, the code in question looks like this...
self.MEDIA_PLAYER_STOP_BUTTON.set_sensitive(True) #Set button to be "active"
playProcess = Popen("aplay " + str(pathToWAV) + " >/dev/null 2>&1",shell=True) #Run Process
playProcess.wait() #Wait for process to complete
self.MEDIA_PLAYER_STOP_BUTTON.set_sensitive(False) #After process is complete, disable the button again
However, this does not work at all.
Any help would be greatly appreciated.
All is working normally (python 2.7.3). But if you call playProcess.wait() in gui thread - you freeze gui thread without redrawing (sorry, my english isn`t very well). And are you sure that you try to use subprocess.popen()? Maybe os.popen()?
My small test:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygtk, gtk, gtk.glade
import subprocess
def aplay_func(btn):
btn.set_sensitive(True)
print "init"
playProcess = subprocess.Popen("aplay tara.wav>/dev/null 2>&1", shell=True)
print "aaa"
playProcess.wait()
print "bbb"
btn.set_sensitive(False)
wTree = gtk.glade.XML("localize.glade")
window = wTree.get_widget("window1")
btn1 = wTree.get_widget("button1")
window.connect("delete_event", lambda wid, we: gtk.main_quit())
btn1.connect("clicked", aplay_func)
window.show_all()
gtk.main()
Result:
init
aaa
bbb
And yes, button is working correctly. Sound too.

Categories