I'm trying to make a little application with appindicator and Gtk. My goal is to display a list of server with link to url of them.
Here is what I try :
from gi.repository import Gtk as gtk
from gi.repository import AppIndicator3 as appindicator
def main():
indicator = appindicator.Indicator.new(APPINDICATOR_ID, img, appindicator.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
indicator.set_menu(build_menu())
gtk.main()
def build_menu():
menu = gtk.Menu()
value = "label"
item = gtk.MenuItem()
button = gtk.LinkButton("http://url/host/id", label=value)
button.show()
item.add(button)
item.show()
menu.append(item)
menu.show_all()
return menu
if __name__ == "__main__":
main()
That's working and I have no errors. But wen I launch application, I've only menu, with items but no link.
I've seen many exemple with gtk.Window but nothing with a menu for appindicator.
Is there a way to have link in this menu ?
Thanks
I've found a way to do that. I'm not sure it's the best way, but it works.
Instead of create a LinkItem, I've made a function for open url:
def open_url(source):
webbrowser.open("http://url/host/id")
And I call it after, with connect :
item.connect("activate", open_url)
When I run my app and click on my item, it opens url as expected. Here is part of code working:
def build_menu():
menu = gtk.Menu()
value = "label"
item = gtk.MenuItem(value)
item.connect("activate", open_url)
menu.append(item)
menu.show_all()
return menu
As I see in many post on web, appindicator has limited functions compared to normal Gtk application.
Related
OS: W10. This may be significant. If you have different results on a different platform, feedback would be helpful.
Here is an MRE. If you run it and go Ctrl+O, the menu labels become greyed. If you select a file in the QFileDialog by clicking the "Open" button or using its mnemonic (Alt+O), the open-file dialog is dismissed and the "Files" and "Help" menus become un-greyed.
However, if you go Ctrl+O again, and this time enter the name of a file in the "File name" box (QLineEdit), and then press Return, the dialog is dismissed (with a successful selection result) but the "Files" and "Help" menus remain greyed-out. It looks like this:
import sys, os
from PyQt5 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Greying of menus MRE')
self.setGeometry(QtCore.QRect(100, 100, 400, 200))
menubar = QtWidgets.QMenuBar(self)
self.setMenuBar(menubar)
self.files_menu = QtWidgets.QMenu('&Files', self)
menubar.addMenu(self.files_menu)
self.help_menu = QtWidgets.QMenu('&Help', self)
menubar.addMenu(self.help_menu)
self.new_action = QtWidgets.QAction('&New', self)
self.files_menu.addAction(self.new_action)
self.open_action = QtWidgets.QAction('&Open', self)
self.files_menu.addAction(self.open_action)
self.open_action.setShortcut("Ctrl+O")
self.open_action.triggered.connect(self.open_file)
def focusInEvent(self, event ):
print('main_window focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('main_window focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('main_window activateWindow')
super().activateWindow()
def open_file(self):
print('open file')
main_window_self = self
# open_doc_dialog = QtWidgets.QFileDialog(self.get_main_window())
class OpenDocFileDialog(QtWidgets.QFileDialog):
def accepted(self):
print('accepted')
super().accepted()
def accept(self):
print('accept')
super().accept()
def close(self):
print('close')
super().close()
def done(self, r):
print(f'done r {r}')
# neither of these solves the problem:
# main_window_self.activateWindow()
# main_window_self.files_menu.activateWindow()
super().done(r)
def hide(self):
print(f'hide')
super().hide()
def focusInEvent(self, event ):
print('focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('activateWindow')
super().activateWindow()
open_doc_dialog = OpenDocFileDialog(self)
open_doc_dialog.setWindowTitle('Choose file')
open_doc_dialog.setDirectory(os.getcwd())
# we cannot use the native dialog, because we need control over the UI
options = open_doc_dialog.Options(open_doc_dialog.DontUseNativeDialog)
open_doc_dialog.setOptions(options)
open_doc_button = open_doc_dialog.findChild(QtWidgets.QDialogButtonBox).button(QtWidgets.QDialogButtonBox.Open)
lineEdit = open_doc_dialog.findChild(QtWidgets.QLineEdit)
# this does not solve the problem
# lineEdit.returnPressed.disconnect()
# lineEdit.returnPressed.connect(open_doc_button.click)
print(f'open_doc_button {open_doc_button}, lineEdit {lineEdit}')
# show the dialog
dialog_code = open_doc_dialog.exec()
if dialog_code != QtWidgets.QDialog.Accepted: return
sel_files = open_doc_dialog.selectedFiles()
print(f'sel_files: {sel_files}')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())
This problem can be understood, if not solved, with reference to this answer.
Note that this greying-out is not disablement. As explained in the above link, this has to do with "active/inactive states" of the menus (or their labels). The menus remain enabled throughout, although in this case it's impossible to know that while the open-file dialog is showing because it is modal. Clicking on one menu after the dialog has gone, or just hovering over it, is enough to un-grey them both...
The explanation, as I understand it, is that the "File name" box QLineEdit has a signal, returnPressed, which appears to activate something subtley different to the slot which is invoked when you use the "Choose" button. You can see I have experimented with trying to re-wire that signal, to no avail.
The method done of the QFileDialog appears to be called however the dialog closes (unlike close!), so I tried "activating" the main window... and then the individual QMenus... Doesn't work.
I am not clear how to get a handle on this "active state" business or why the slot connected to returnPressed is (seemingly) unable to give the "active state" back to the menus when the other slot manages to do so.
Edit
Searching on Musicamante's "unpolishing" suggestion led me to this:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
lineEdit.returnPressed.connect(return_pressed)
... unfortunately this doesn't work.
This looks like a possible Windows-related bug, since I can't reproduce it on Linux. As a work-around, you could try forcing a repaint after the dialog closes:
# show the dialog
dialog_code = open_doc_dialog.exec()
self.menubar.repaint()
Finally got it, thanks to Musicamante's suggestion:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
main_window_self.menubar.repaint()
lineEdit.returnPressed.connect(return_pressed)
... I actually tried this several times, just to make sure it was doing what was intended. So in fact, fortunately, no single-shot timer was needed in this case.
I'm trying to create an application that contains a web browser within it, but when I add the web browser my menu bar visually disappears but functionally remains in place. The following are two images, one showing the "self.centralWidget(self.web_widget)" commented out, and the other allows that line to run. If you run the example code, you will also see that while visually the entire web page appears as if the menu bar wasn't present, you have to click slightly below each entry field and button in order to activate it, behaving as if the menu bar was in fact present.
Web Widget Commented Out
Web Widget Active
Example Code
import os
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtWebEngineWidgets import *
class WebPage(QWebEngineView):
def __init__(self, parent=None):
QWebEngineView.__init__(self)
self.current_url = ''
self.load(QUrl("https://facebook.com"))
self.loadFinished.connect(self._on_load_finished)
def _on_load_finished(self):
print("Url Loaded")
class MainWindow(QMainWindow):
def __init__(self, parent=None):
# Initialize the Main Window
super(MainWindow, self).__init__(parent)
self.create_menu()
self.add_web_widet()
self.show()
def create_menu(self):
''' Creates the Main Menu '''
self.main_menu = self.menuBar()
self.main_menu_actions = {}
self.file_menu = self.main_menu.addMenu("Example File Menu")
self.file_menu.addAction(QAction("Testing Testing", self))
def add_web_widet(self):
self.web_widget = WebPage(self)
self.setCentralWidget(self.web_widget)
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.showMaximized()
sys.exit(app.exec_()) # only need one app, one running event loop
Development Environment
Windows 10, PyQt5, pyqt5-5.9
EDIT
The problem doesn't seem to be directly related to the menu bar. Even removing the menu bar the issue still occurs. That said, changing from showMaximized() to showFullScreen() does seem to solve the problem.
I no longer believe this is an issue with PyQt5 specifically but rather a problem with the graphics driver. Specifically, if you look at Atlassian's HipChat application it has a similar problem which is documented here:
https://jira.atlassian.com/browse/HCPUB-3177
Some individuals were able to solve the problem by running the application from the command prompt with the addendum "--disable-gpu" but that didn't work for my python application. On the other hand, rolling back the Intel(R) HD Graphics Driver did solve my problem. Version 21.20.16.4627 is the one that seems to be causing problems.
I need to update the existing menu items for a system tray application. At first when the app loads, there will be two menu items. Later when I click a button these menu items need to be replaced with new menu items. How can I achieve that ? Here is my code.
from PySide.QtGui import *
import sys
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.tray = QSystemTrayIcon(QApplication.style().standardIcon(QStyle.SP_DriveDVDIcon), self)
self.m = QMenu()
self.m.addAction('First')
self.m.addAction('Second')
self.tray.setContextMenu(self.m)
self.tray.show()
p = QPushButton("Click Me", self)
self.setCentralWidget(p)
p.clicked.connect(self.onClick)
def onClick(self):
self.m.clear()
self.m.addAction('First')
self.m.addAction('Third')
self.tray.setContextMenu(self.m)
app = QApplication(sys.argv)
w = MainWindow()
w.show();
sys.exit(app.exec_())
However this is not working. If I try removing self.m.clear()the new menu items will append to the existing (Which is the normal behaviour in this case). Isn't menu.clear() clears the current menu & the new menu should be populated here ?
I have seen this similar question Qt QSystemTrayIcon change menu items and the solution doesn't work for me. I am running Ubuntu 14.04.
I figured it out, the problem is due to the self.tray.setContextMenu(self.m). Remove this line from onClick method. This should work fine on Ubuntu.
I'm trying to get this python code to react when the mouse hovers over the tray icon and scrolls the mouse wheel, I can't find any examples online. This is what I have so far, it doesn't react to scrolling the wheel...
#!/usr/bin/python
APPNAME = "My App"
ICON = "/usr/share/pixmaps/firefox.png"
import appindicator as AI
import gtk
def sayhello(item):
print "menu item selected"
def scroll(aai, ind, steps):
print "hello" # doesn't print anything
def makemenu():
' Set up the menu '
menu = gtk.Menu()
check = gtk.MenuItem('Check')
exit = gtk.MenuItem('Quit')
check.connect('activate', sayhello)
exit.connect('activate', gtk.main_quit)
menu.append(check)
menu.append(exit)
return menu
def startapp():
ai = AI.Indicator(APPNAME, ICON, AI.CATEGORY_APPLICATION_STATUS)
ai.set_status(AI.STATUS_ACTIVE)
ai.connect("scroll-event", scroll)
ai.set_menu(makemenu())
gtk.main()
startapp()
How can I detect scroll wheel movements?
That is the correct way to connect to the mouse scroll event and the code does work, tested on two 12.04 systems. There might be a bug however as the first few test runs on one of them did not work either but then was fine.
If you are starting from scratch I would recommend using pygobject (Gtk3) instead of pygtk (Gtk2) as it is no longer developed. As part of testing I did convert your script to pygobject and fixed showing the menu:
#!/usr/bin/env python
APPNAME = "My App"
ICON = "/usr/share/pixmaps/firefox.png"
from gi.repository import AppIndicator3 as AI
from gi.repository import Gtk
def sayhello(item):
print "menu item selected"
def scroll(aai, ind, steps):
print "hello" # doesn't print anything
def makemenu():
' Set up the menu '
menu = Gtk.Menu()
check_item = Gtk.MenuItem('Check')
exit_item = Gtk.MenuItem('Quit')
check_item.connect('activate', sayhello)
check_item.show()
exit_item.connect('activate', Gtk.main_quit)
exit_item.show()
menu.append(check_item)
menu.append(exit_item)
menu.show()
return menu
def startapp():
ai = AI.Indicator.new(APPNAME, ICON, AI.IndicatorCategory.HARDWARE)
ai.set_status(AI.IndicatorStatus.ACTIVE)
ai.set_menu(makemenu())
ai.connect("scroll-event", scroll)
Gtk.main()
startapp()
I'm starting to write a small panel applet for Gnome and I'd like the user to be able to left-click on the status icon to see some options and information e.g. similar to sound icon in Gnome 3, where you can set volume via left-click while set preferences via right-click.
Right-click code is this:
statusicon.connect("popup-menu", right_button_click)
where right_button_click is the name of the function that gets called on right-click event. The important part is "popup-menu". What would be alternative for setting left-click event?
This is the tiny example showing how it works.
#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class TrayIcon(Gtk.StatusIcon):
def __init__(self):
Gtk.StatusIcon.__init__(self)
self.set_from_icon_name('help-about')
self.set_has_tooltip(True)
self.set_visible(True)
self.connect("popup_menu", self.on_secondary_click)
def on_secondary_click(self, widget, button, time):
menu = Gtk.Menu()
menu_item1 = Gtk.MenuItem("First Entry")
menu.append(menu_item1)
menu_item2 = Gtk.MenuItem("Quit")
menu.append(menu_item2)
menu_item2.connect("activate", Gtk.main_quit)
menu.show_all()
menu.popup(None, None, None, self, 3, time)
if __name__ == '__main__':
tray = TrayIcon()
Gtk.main()
First thing is to look into the gnome code for the volume control, and that's is this
Second, you should look into the API documentation for GtkStatusIcon, and that one is here
That should be enough.
This is a late response, but i'm just posting this in case someone else ever looks for left click control over gtkstatusicon.
The direct alternative is
statusicon.connect("activate", left_button_click)
This is a sample for trayicon popup menu working with left click instead of the (common) right click.
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class TrayIcon(gtk.StatusIcon):
def __init__(self):
gtk.StatusIcon.__init__(self)
self.set_from_icon_name('help-about')
self.set_has_tooltip(True)
self.set_visible(True)
self.connect("activate", self.on_click)
def greetme(self,data=None):
msg=gtk.MessageDialog(None, gtk.DIALOG_MODAL,gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Greetings")
msg.run()
msg.destroy()
def on_click(self,data):
event=gtk.get_current_event()
btn=event.button #this gets the button value of gtk event.
time=gtk.get_current_event_time() # required by menu popup.
menu = gtk.Menu()
menu_item1 = gtk.MenuItem("First Entry")
menu.append(menu_item1)
menu_item1.connect("activate", self.greetme)
menu_item2 = gtk.MenuItem("Quit")
menu.append(menu_item2)
menu_item2.connect("activate", gtk.main_quit)
menu.show_all()
menu.popup(None, None, None, btn, time)
#button can be hardcoded (i.e 1) but time must be correct.
if __name__ == '__main__':
tray = TrayIcon()
gtk.main()
Also , there is this alternative :
statusicon.connect("button-press-event", button_click)
Bellow sample code raise the same popup menu in gtktrayicon in BOTH right and left click.
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class TrayIcon(gtk.StatusIcon):
def __init__(self):
gtk.StatusIcon.__init__(self)
self.set_from_icon_name('help-about')
self.set_has_tooltip(True)
self.set_visible(True)
self.connect("button-press-event", self.on_click)
def greetme(self,data=None):
msg=gtk.MessageDialog(None, gtk.DIALOG_MODAL,gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Greetings")
msg.run()
msg.destroy()
def on_click(self,data,event):
#event in this case is sent by the status icon connect.
btn=event.button
#By controlling this event.button value (1-2-3 for left-middle-right click) you can call other functions.
time=gtk.get_current_event_time() # required by the popup.
menu = gtk.Menu()
menu_item1 = gtk.MenuItem("First Entry")
menu.append(menu_item1)
menu_item1.connect("activate", self.greetme)
menu_item2 = gtk.MenuItem("Quit")
menu.append(menu_item2)
menu_item2.connect("activate", gtk.main_quit)
menu.show_all()
menu.popup(None, None, None, btn, time)
if __name__ == '__main__':
tray = TrayIcon()
gtk.main()
Hope above code helps.
George V.