PyQt RichText Formatting in system tray tooltip - python

This is an example of code for simple system tray PyQt application.
import sys
from PyQt4 import QtGui
def main():
app = QtGui.QApplication(sys.argv)
trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon('test.png'), app)
menu = QtGui.QMenu()
exitAction = menu.addAction("Exit")
trayIcon.setContextMenu(menu)
# I'd like to show picture in tooltip, BUT IT'S NOT WORK IN WINDOWS
trayIcon.setTooltip('<img src="SomePicture.png" width="48" height="48"/>')
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
In this code I'd like to show balloon tooltip with a some picture and some kind of text formating. For this purpose I use RichText tags formatting. As the result for Ubuntu Linux system (Gnome desktop) everything is Ok. But when I try use RichText formatting for tooltip in Windows XP system, nothing works. Tooltip text equals source string: ''.
Python version on Windows 2.7, on Linux 2.6 but I think that problem is not in different versions.
If in Windows OS RichText isn't parsing how can I make same kind of GUI (Crossplatform is prefered)?

On Windows Qt uses the os' tooltip system, which only supports text.
If you want something more advanced, you could QSystemTrayIcon.showMessage() use as described here. You'll probably have to install an eventfilter or override the QTrayIcons event method to get the help event.

If someone also interested in create of balloon widget. This my code:
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, parent = None):
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
traySignal = "activated(QSystemTrayIcon::ActivationReason)"
self.connect(self, QtCore.SIGNAL(traySignal), self._activateRoutine)
self.balloon = balloonWidget(name)
def _activateRoutine(self, reason):
if reason == QtGui.QSystemTrayIcon.Trigger:
self.balloon.show(self.geometry())
class balloonWidget(QtGui.QWidget):
def __init__(self,name):
QtGui.QWidget.__init__(self, parent = None, flags = QtCore.Qt.Popup)
self.name = name
self.offsetX = 10
self.offsetY = 10
self.outInfo = QtGui.QLabel(self)
self.setStyleSheet("QWidget {border:5px solid rgb(170, 170, 255);}")
def show(self,coord):
richText = tr('Any text with Rich Format')
self.outInfo.setText(richText)
self.outInfo.show()
self.adjustSize()
origin = QtGui.QDesktopWidget().availableGeometry().bottomRight()
if coord.y() < origin.y()/2:
moveY = coord.bottomLeft().y() + self.offsetY
else:
moveY = coord.topLeft().y() - (self.height() + self.offsetY)
if coord.x() + self.width() + self.offsetX >= origin.x():
moveX = origin.x() - (self.width() + self.offsetX)
else:
moveX = coord.x()
self.move(moveX,moveY)
self.setVisible(True)
def closeEvent(self, event):
event.ignore()
self.hide()
def mousePressEvent(self, event):
self.close()

Related

Why while loop freeze api gui [duplicate]

This question already has answers here:
Equivalent to time.sleep for a PyQt application
(5 answers)
Closed 1 year ago.
I trying create GUI Api. First i build python script with only print information in console.
So I wanted to rebuild applications into applications with an interface. I decided to use PyQt5
Like this:
To(first look):
I ran into a problem with the loop While. Aplication just freeze when while is runing
I prepared a short script simulating the problem. The main program looks different
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets
from termcolor import colored
import time
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'API NORD'
self.left = 0
self.top = 0
self.width = 300
self.height = 200
self.setWindowTitle(self.title)
self.resize(800, 600)
self.center()
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.show()
def center(self):
# geometry of the main window
qr = self.frameGeometry()
# center point of screen
cp = QDesktopWidget().availableGeometry().center()
# move rectangle's center point to screen's center point
qr.moveCenter(cp)
# top left of rectangle becomes top left of window centering it
self.move(qr.topLeft())
class MyTableWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.pushButton1 = QPushButton("Run")
self.layout.addWidget(self.pushButton1)
self.pushButton1.clicked.connect(self.button2_clicked)
self.textedit = QtWidgets.QTextEdit(readOnly=True)
self.layout.addWidget(self.textedit)
self.textedit.setText("STATUS")
def onClicked(self):
radioButton = self.sender()
if radioButton.isChecked():
x=0
# print("Shop is %s" % (radioButton.shop))
self.Sklep=radioButton.shop
self.l1.setText(self.Sklep)
return
def checkBulkStatus(self):
Status = "Start"
x=0
self.textedit.setText("Start")
while x < 5:
print("Aktualny Status:", colored(Status,"yellow"))
Status="Running"
self.textedit.append(Status)
if Status=="FAILED":
print("Error")
break
time.sleep(2.5)
x+=1
print("Aktualny Status: ", colored("COMPLETED", "green"))
self.textedit.setText("COMPLETED")
def button2_clicked(self):
self.checkBulkStatus()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
In main program I ussing while to check status of BULK request in GraphQL:
def checkBulkStatus(self):
self.url = self.auth(self.Sklep)["url_auth"]
print(self.url)
Status = "Start"
self.textedit.setText("Start")
while Status != "COMPLETED":
print("Aktualny Status:", colored(Status,"yellow"))
checking = self.Core.callShopifyGraphQL(self.Core.CheckQuery,self.url)
result = checking.json()
Status=result["data"]["currentBulkOperation"]["status"]
self.textedit.append(Status)
if Status=="FAILED":
print(result["data"]["currentBulkOperation"])
break
time.sleep(2.5)
print("Aktualny Status: ", colored("COMPLETED", "green"))
URL_bulk=result["data"]["currentBulkOperation"]["url"]
The problem is that the gui runs in the same thread as the script, so when you run the script it freezes the interface. To prevent this from happening, you need to run the script in a thread, as this way you can share variables with the main thread.
I hope it helps you, greetings.

How can I detect when one window occludes another in PyQt5?

I'm using PyQt5 to create an app with multiple main windows. I want to be able to allow the user to save and load window sizes and window positions. That's easy with, e.g., QMainWindow.saveGeometry() and QMainWindow.loadGeometry() or the corresponding .saveState() and .loadState() variants. These work great for position and size, but if the user moves or resizes one window so that it occludes another, I want to also restore this positioning. I don't mind writing my own code to save the info for each window, but I can't see any way to detect the relative Z order of windows. Am I missing it in the docs, or is this not possible?
To see what I mean, try this:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit
from PyQt5.QtCore import QSettings
from PyQt5.QtGui import QCloseEvent
'''
context: Linux Mint 19.3 Tricia x86_64
Python 3.9
PyQt5 5.15.1
'''
class RememberWin(QMainWindow):
def __init__(self, win_name: str):
super(RememberWin, self).__init__()
self.win_name = win_name
self.setWindowTitle(win_name)
self.can_close = False
def restore_window(self) -> bool:
try:
settings = QSettings("PyQtExamples", "RememberWinTest")
self.restoreGeometry(settings.value(f'{self.win_name} Geometry'))
self.restoreState(settings.value(f'{self.win_name} State'))
return True
except:
return False
def closeEvent(self, event: QCloseEvent):
if not self.can_close:
event.ignore()
else:
settings = QSettings("PyQtExamples", "RememberWinTest")
settings.setValue(f'{self.win_name} Geometry', self.saveGeometry())
settings.setValue(f'{self.win_name} State', self.saveState())
QMainWindow.closeEvent(self, event)
class ControlWindow(RememberWin):
def __init__(self, win_name: str = "ControlWindow"):
super(ControlWindow, self).__init__(win_name=win_name)
self.can_close = True
self.window1 = RememberWin(win_name='WindowOne')
self.window2 = RememberWin(win_name='WindowTwo')
self.text = QPlainTextEdit(self)
s = "Try making Window1 wide enough to cover Window2.\n" \
"Then close this window (auto closes others).\n" \
"Re-run the app and you'll notice that Window2\n" \
"is not on top of Window1 which means that this\n" \
"info isn't getting saved."
self.text.setPlainText(s)
self.setCentralWidget(self.text)
if not self.restore_window():
self.setGeometry(100, 390, 512, 100)
if not self.window1.restore_window():
self.window1.setGeometry(100, 100, 512, 384)
if not self.window2.restore_window():
self.window2.setGeometry(622, 100, 512, 384)
self.window1.show()
self.window2.show()
def closeEvent(self, event: QCloseEvent):
for win in (self.window1, self.window2):
win.can_close = True
win.close()
super(ControlWindow, self).closeEvent(event)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = ControlWindow(win_name='ControlWindow (You can only close this one)')
window.show()
sys.exit(app.exec_())
The simplest way to do what you want to achieve is to keep track of the current focused widget, or, to be precise, the top level window of the last focused widget.
You can store the focused windows in the settings as a list, using a unique objectName for each window (you are already doing this, so you just need to use setObjectName()), then restore the window by showing them in the correct order as long as the object name matches.
class RememberWin(QMainWindow):
def __init__(self, win_name: str):
super(RememberWin, self).__init__()
self.win_name = win_name
self.setObjectName(win_name)
self.setWindowTitle(win_name)
self.can_close = False
# ...
class ControlWindow(RememberWin):
def __init__(self, win_name: str = "ControlWindow"):
# ...
self.settings = QSettings("PyQtExamples", "RememberWinTest")
self.zOrder = []
QApplication.instance().focusObjectChanged.connect(self.focusChanged)
windowOrder = self.settings.value('windowOrder', type='QStringList')
topLevelWindows = QApplication.topLevelWidgets()
if windowOrder:
for objName in windowOrder:
for win in topLevelWindows:
if win.objectName() == objName:
win.show()
else:
self.window1.show()
self.window2.show()
def focusChanged(self, obj):
if not obj or obj.window() == self.window():
return
if obj.window() in self.zOrder[:-1]:
self.zOrder.remove(obj.window())
self.zOrder.append(obj.window())
def closeEvent(self, event: QCloseEvent):
for win in (self.window1, self.window2):
win.can_close = True
win.close()
self.settings.setValue('windowOrder',
[w.window().objectName() for w in self.zOrder])
super(ControlWindow, self).closeEvent(event)

How to play a song from the internet using python

I am trying to use PyQt4 and Phonon (on windows 8 64 bits) to play a song from the internet (streaming, ex: http://dr5huvbk6x9di.cloudfront.net/cloudfront_songs/file4.ogg)
To play a song from the filesystem is working, but when I try to play from the internet it doesn't. I read the documentation and it seems everything is fine. The error is a FatalError, so is hard to understand what is going on. Phonon can't play the song from the internet?
Another questions is that I read that phonon has been deprecated and we have PyQt5. So, which is the best way to do what I am trying to do.
Here is my code. It is a little bit messy because I just wanted to work, so I could understand and then make it better. Thank you
#!/usr/bin/env python
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.phonon import Phonon
class MainWindow(QtGui.QMainWindow):
def __init__(self, win_parent=None):
QtGui.QMainWindow.__init__(self, win_parent)
self.create_widgets()
def create_widgets(self):
# Widgets
self.label = QtGui.QLabel("ply music player")
self.fs_button = QtGui.QPushButton("FileSystem", self)
self.ws_button = QtGui.QPushButton("WebStream", self)
# Phonon actions
self.mediaObject = Phonon.MediaObject(self)
self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
Phonon.createPath(self.mediaObject, self.audioOutput)
# Connect signal
self.fs_button.clicked.connect(self.on_fs_clicked)
self.mediaObject.stateChanged.connect(self.handleStateChanged)
self.ws_button.clicked.connect(self.on_ws_clicked)
# Vertical layout (manages the layout automatically)
v_box = QtGui.QVBoxLayout()
v_box.addWidget(self.fs_button)
v_box.addWidget(self.ws_button)
# Create central widget, add layout and set
central_widget = QtGui.QWidget()
central_widget.setLayout(v_box)
self.setCentralWidget(central_widget)
def on_fs_clicked(self):
if self.mediaObject.state() == Phonon.PlayingState:
self.mediaObject.stop()
else:
files = QtGui.QFileDialog.getOpenFileNames(self, self.fs_button.text())
if files:
songs = []
for file in files:
songs.append(Phonon.MediaSource(file))
self.mediaObject.setQueue(songs)
self.mediaObject.play()
self.fs_button.setText("FileSystem")
def handleStateChanged(self, newstate, oldstate):
if newstate == Phonon.PlayingState:
self.fs_button.setText("Stop")
elif newstate == Phonon.StoppedState:
self.fs_button.setText("FileSystem")
elif newstate == Phonon.ErrorState:
source = self.mediaObject.currentSource().fileName()
print "ERROR: ", self.mediaObject.errorType()
print "ERROR: could not play:", source.toLocal8Bit().data()
def on_ws_clicked(self):
if self.mediaObject.state() == Phonon.PlayingState:
self.mediaObject.stop()
else:
song = "http://dr5huvbk6x9di.cloudfront.net/cloudfront_songs/file4.ogg"
self.mediaObject.setCurrentSource(Phonon.MediaSource(song))
print self.mediaObject.currentSource()
self.mediaObject.play()
self.ws_button.setText("WebStream")
if __name__ == "__main__":
ply = QtGui.QApplication(sys.argv)
ply.setApplicationName("Ply")
ply.setQuitOnLastWindowClosed(True)
main_window = MainWindow()
main_window.show()
sys.exit(ply.exec_())
The answer was installing codecs to play .ogg files. Thanks to #ekhumoro.

python gi.repository.gtk menu separators are missing

I want to show simple popup menu with right click, menu works right but menu separators are missing, I searched similar examples but they all using pygtk but I'm using gi.repository I don't know is my code wrong or this problem is all about libraries.
my code is
#!/usr/bin/python
from gi.repository import Gtk
class Win(Gtk.Window):
def __init__(self):
super(Win, self).__init__()
self.resize(400,280)
self.button = Gtk.Button()
self.add(self.button)
self.button.connect("button-press-event", self.button_press)
self.connect("destroy", Gtk.main_quit)
self.show_all()
def button_press(self,widget,event):
if event.button == 3:
self.menu = Gtk.Menu()
self.menu_copy = Gtk.MenuItem("Copy")
self.menu_paste = Gtk.MenuItem("Paste")
self.menu_select_all = Gtk.MenuItem("Select All")
self.menu.append(self.menu_copy)
self.menu.append(self.menu_paste)
# separator 1
self.menu.append(Gtk.SeparatorMenuItem())
# separator 2
self.menu.append(Gtk.MenuItem())
self.menu.append(self.menu_select_all)
self.menu.show_all()
self.menu.popup(None, None, None, None, event.button, event.get_time())
pass
def main():
app = Win()
Gtk.main()
if __name__ == "__main__":
main()
and here is the screenshot of my menu Example 1
What version of pygobject do you have? I submitted a patch for that back in June and it was fixed in 3.3.4: https://bugzilla.gnome.org/show_bug.cgi?id=670575
If the separator works properly when you use Gtk.SeparatorMenuItem.new() instead of the constructor than you know it's the aforementioned bug.

Class to position a window on the screen

I am new to Python and this is my fist Python class. I am using PyQt4 framework on Windows 7.
I don't know whether the few lines of code below is correctly written or not. I want to modify it further as:
In the arguments, I want to pass the name of another opened Window (.py) on the screen.
I will be passing the x-coord., y-coord. and the name of the window to position on the screen.
How to modify the code to fulfill these requirements?
Edited Further
class PositionWindow:
def __init__(self, xCoord, yCoord, windowName, parent = None):
self.x = xCoord
self.y = yCoord
self.wName = windowName;
def center(self):
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
Can't you just use window.setGeometry(x_pos, y_pos, width, height)? A class seem overkill in this case.
See here for documentation.
You can also use
def main():
app = QtGui.QApplication(sys.argv)
gui = Program()
gui.move(380, 170)
gui.show()
app.exec_()
the gui.move() will position your application to the stated coordinates in the parenthesis

Categories

Resources