I'm trying to create application which runs block of code every X seconds, which has system tray icon with only "Quit" option. But the problem is that when it get to the tray function, it doesn't read next lines of code, and as a result, "While" loop can't be launched.
Is there any other approach to do that?
import time
import os
import sys
from PySide2 import QtWidgets, QtGui
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
self.setToolTip(f'Wallpy')
menu = QtWidgets.QMenu(parent)
exit_ = menu.addAction("Exit")
exit_.triggered.connect(lambda: sys.exit())
menu.addSeparator()
self.setContextMenu(menu)
def tray():
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
tray_icon = SystemTrayIcon(QtGui.QIcon("tray.ico"), w)
tray_icon.show()
app.exec_()
def loop_function():
print("Nice") # anything executable
tray() # launch tray icon
while True:
loop_function() # executing every minute
time.sleep(60)
Its because, when you used the tray(), your main application started and the GUI main loop is initiated. It runs till your application exits, after which the while loop is executed.
However, if you want the loop to run simultaneously, you should integrate it with Qt's main loop. In Gtk we do this using GLib.add_main_thread and other methods, I don't know about Qt, but you can use this general solution using Threading.
from threading import Thread
import time
import os
import sys
from PySide2 import QtWidgets, QtGui
def loop_function():
print("Nice") # anything executable
def thread_loop():
while True:
loop_function() # executing every minute
time.sleep(60)
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
self.setToolTip(f'Wallpy')
menu = QtWidgets.QMenu(parent)
exit_ = menu.addAction("Exit")
exit_.triggered.connect(lambda: sys.exit())
menu.addSeparator()
self.setContextMenu(menu)
def tray():
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
tray_icon = SystemTrayIcon(QtGui.QIcon("tray.ico"), w)
tray_icon.show()
app.exec_()
my_loop = Thread(target=thread_loop, args=()) # Start the thread with no args
my_loop.start()
tray() # launch tray icon
Related
Using PyQt5, I want to implement a two windows displaying one after another automatically, without the user interacting with any window. Something like this:
While True:
Show Window1
wait 2 seconds
Close Window1
Show Window2
wait 2 seconds
Close Window2
The problem I am having is that the main UI thread is stuck in app.exec_() function, so it cannot implement the opening and closing logic.
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
class Win1(QMainWindow):
def __init__(self):
super(Win1, self).__init__()
uic.loadUi('win1.ui', self)
self.show()
class Win2(QMainWindow):
def __init__(self):
super(Win2, self).__init__()
uic.loadUi('win2.ui', self)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
while True:
win = Win1()
time.sleep(1)
win.close()
win = Win2()
time.sleep(1)
win.close()
app.exec_() # <--------- Program blocks here
I would appreciate if someone can share a minimal example for this working without blocking. Or please point to the mechanism that should be used.
If you are going to work with Qt then you should forget about sequential logic but you have to implement the logic using events. For example, in your case you want one window to be shown every time T and another to be hidden, so that can be implemented with a QTimer and a flag:
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
class Win1(QMainWindow):
def __init__(self):
super(Win1, self).__init__()
uic.loadUi('win1.ui', self)
self.show()
class Win2(QMainWindow):
def __init__(self):
super(Win2, self).__init__()
uic.loadUi('win2.ui', self)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
timer = QTimer()
timer.setProperty("flag", True)
win1 = Win1()
win2 = Win2()
def on_timeout():
flag = timer.property("flag")
if flag:
win1.show()
win2.close()
else:
win2.show()
win1.close()
timer.setProperty("flag", not flag)
timer.timeout.connect(on_timeout)
timer.start(1 * 1000)
on_timeout()
app.exec_()
You should not use while loop or time.sleep since they block the eventloop in which the GUI lives, that is: they freeze the windows
I need to display adb logs in a Pyqt window, i tried like this. When i click the button log_display function is called, which set the console output to textBrowser. I tried to use subprocess but it didn't help, the window is just freezing and not responding. What is the way to do it? Maybe i need to use new thread for it?
def log_display(self):
result = subprocess.run('adb logcat *:I', stdout=subprocess.PIPE)
self.textBrowser.setText(subprocess.run('result.stdout'))
You have to use QProcess since it launches the application but does not block the GUI since it notifies the log through signals:
from PyQt5 import QtCore, QtGui, QtWidgets
class LogView(QtWidgets.QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent)
self.setReadOnly(True)
self._process = QtCore.QProcess()
self._process.readyReadStandardOutput.connect(self.handle_stdout)
self._process.readyReadStandardError.connect(self.handle_stderr)
def start_log(self, program, arguments=None):
if arguments is None:
arguments = []
self._process.start(program, arguments)
def add_log(self, message):
self.appendPlainText(message.rstrip())
def handle_stdout(self):
message = self._process.readAllStandardOutput().data().decode()
self.add_log(message)
def handle_stderr(self):
message = self._process.readAllStandardError().data().decode()
self.add_log(message)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = LogView()
w.resize(640, 480)
w.show()
w.start_log("adb", ["logcat", "*:I"])
sys.exit(app.exec_())
I want to embed an Ipython terminal in a QT widget so I can access objects in my QT application. I understand that this is possible with examples such as this: https://github.com/gpoulin/python-test/blob/master/embedded_qtconsole.py
however, if the kernel is blocking (in an infinite while loop for example) the whole application is unresponsive and can only be killed with a keyboard interrupt from the terminal where the qt application was run. If I use the QtKernelManager instead of QtInProcessKernelManager I can successfully interrupt the kernel from the qt application, however, I cannot access objects within it as the kernel is embedded in a different process.
Is there a way to catch the keyboard interrupt when embedding the IPython terminal in the same process ? or should I use a different implementation of embedding the kernel ?
my adapted code is below
import os
os.environ['QT_API'] = 'pyqt'
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
import sys
from PyQt4 import QtGui, QtCore
from pkg_resources import require
require('ipython')
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
class IPythonWidget(QtGui.QWidget):
def __init__(self, **kwarg):
super(IPythonWidget, self).__init__()
self.initUI()
def startIpKernel(self):
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel_manager.kernel.gui = 'qt4'
self.kernel_client = kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
def initUI(self):
self.startIpKernel()
self.addWidgets()
def addWidgets(self):
self.button = QtGui.QPushButton()
self.button.setText("test button")
self.console = RichIPythonWidget(self)
self.console.kernel_manager =self.kernel_manager
self.console.kernel_client = self.kernel_client
self.console.kernel_manager.kernel.shell.push({"button": self.button})#this can be wrapped into another method
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.console)
vbox.addWidget(self.button)
self.setLayout(vbox)
def mousePressEvent(self, event):
self.mousepos = event.pos()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = IPythonWidget()
ex.show()
sys.exit(app.exec_())
I have this application that controls a deamon of mine. It basically checks if the deamon is already running, if so it gives the option to close, if not it gives the option to close, also it offers you a log of it, and everything is on a menu that opens from a QSystemTrayIcon. When I run it it works perfectly fine, but when I set it to run automatically after i log in it runs, but the trayicon doesn't show, you can even see the process with "ps aux". I'm running on Ubuntu 12.04.
#!/usr/bin/env python
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from subprocess import call
from subprocess import Popen
import os
import time
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, parent=None):
QtGui.QSystemTrayIcon.__init__(self, parent)
self.setIcon(QtGui.QIcon("./mail_open_process.png"))
global closeAction
global openAction
global startOnLaunch
self.menu = QtGui.QMenu(parent)
openAction = self.menu.addAction("Open")
closeAction = self.menu.addAction("Close")
logAction = self.menu.addAction("Log")
startOnLaunch = self.menu.addAction("Start on Launch")
startOnLaunch.setCheckable(True)
exitAction = self.menu.addAction("Exit")
self.setContextMenu(self.menu)
self.connect(openAction,QtCore.SIGNAL('triggered()'),self.openDaemon)
self.connect(closeAction,QtCore.SIGNAL('triggered()'),self.closeDaemon)
self.connect(logAction,QtCore.SIGNAL('triggered()'),self.openLog)
self.connect(exitAction,QtCore.SIGNAL('triggered()'),self.Exit)
#self.connect(startOnLaunch,QtCore.SIGNAL('triggered()'),self.startLaunch)
if os.path.exists("/dev/repa"):
closeAction.setVisible(True)
openAction.setVisible(False)
else:
closeAction.setVisible(False)
openAction.setVisible(True)
def Exit(self):
os.remove("/tmp/daemonMenu.pid")
sys.exit()
def openDaemon(self):
call(["gksu", os.path.expandvars("$HOME")+"/repad/apps/repad -f"])
time.sleep(1)
if os.path.exists("/dev/repa"):
closeAction.setVisible(True)
openAction.setVisible(False)
def closeDaemon(self):
call(["gksu", os.path.expandvars("$HOME")+"/repad/apps/repad -c"])
time.sleep(1)
if not os.path.exists("/dev/repa"):
closeAction.setVisible(False)
openAction.setVisible(True)
def openLog(self):
Popen(["gedit", "/dev/repad.log"])
#def startLaunch(self):
# Dir = "/etc/init.d/S99scriptDaemon"
# if startOnLaunch.isChecked():
# call(["gksu","cp ./S99scriptDaemon /etc/rc5.d"])
#if not startOnLaunch.isChecked():
# call (["gksu","rm /etc/rc5.d/S99scriptDaemon"])
def main():
pid = str(os.getpid())
pidDir = "/tmp/daemonMenu.pid"
if os.path.isfile(pidDir):
pidFile = open(pidDir,"r")
pidLine = pidFile.readline()
call(["kill", "%s" %(pidLine)])
os.remove(pidDir)
file(pidDir, 'w+').write(pid)
if name == 'main':
main()
app = QtGui.QApplication(sys.argv)
trayIcon = SystemTrayIcon()
trayIcon.show()
sys.exit(app.exec_())
I've got the same issue. I made script start.sh and at startup system starts this script. In script I added 10 second delay before starting application. This script seems like this:
sleep 10
./main.py
I defined a print function using QPrinter and QDialog. However, when I launch the printer dialog and then press cancel, the entire mainwindow goes into not responding mode. I have tried to do QtGui.QPrintDialog.close() but does not work.
Code:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class QButton(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.button = QtGui.QPushButton('Button', self)
self.name='me'
self.button.clicked.connect(self.calluser)
def calluser(self):
Appli=QtGui.QApplication(sys.argv)
printer= QtGui.QPrinter()
doc=QtGui.QTextDocument("Set local variables in this printing slot." )
dialog = QtGui.QPrintDialog(printer)
dialog.setModal(True)
dialog.setWindowTitle("Print Document" )
if dialog.exec_() == True:
doc.print_(printer)
# dialog.addEnabledOption(QAbstractPrintDialog.PrintSelection)
def demo_QButton():
app = QtGui.QApplication(sys.argv)
tb = QButton()
tb.show()
app.exec_()
if __name__=='__main__':
demo_QButton()
You created a new application in the calluser method. Delete or comment the line:
Appli=QtGui.QApplication(sys.argv)
and try again. I think this time your main window will remain responsive.