Interacting with a gtk.container while gtk.main() is executing? - python

Experimenting with a battery monitor icon at the moment in Python using pygtk and egg.trayicon to create an icon to display a battery icon/tooltip.
I seem to be able to add the icon and the tooltip text, but when it then reaches the gtk.main() stage I need a way to modify these so it can then show the updated values.
I've tried gobject.idle_add() and gobject.timeout_add() without much luck, not sure where to go from this.
Anyone got any ideas?
EDIT: Perhaps not the clearest of questions.
I need to loop, fetching information from acpi while running and apply it to widgets inside the gtk container.
EDIT 2: Ok, it's properly down now. The issue was that I wasn't returning anything inside my callback. I just gave it "return 123" and now it's happily chugging away in my system tray, notifying me of my battery percentage :)

This example works for me:
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import gobject
import gtk
from egg import trayicon
label = gtk.Label("Over here")
def callback(widget, ev):
label.set_text("You found me")
def timeout():
label.set_text("What are you waiting for?")
tray = trayicon.TrayIcon("TrayIcon")
box = gtk.EventBox()
box.add(label)
tray.add(box)
tray.show_all()
box.connect("button-press-event", callback)
gobject.timeout_add(3000L, timeout)
gtk.main()
Without seeing your code, it's hard to tell what doesn't work.

Related

Global hotkey on windows with tkinter

I want a python tkinter application to register a global hotkey (triggered even if the application has not the focus). I've found some pieces, but I can't find a way to put them together...
Basically, I can register the hotkey (with a call to the windows API RegisterHotKey), but it send a "WM_HOTKEY" message to the root windows that is under Tkinter mainloop management, and I cant find a way to bind on it...
tk.protocol() function seems to be there for it, but this message seems not to be understood (I can't find a complete list of the recognized messages)...
Here a non-working code example where I would like to print a message when "WIN-F3" is pressed...
import Tkinter
import ctypes
import win32con
class App(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
user32 = ctypes.windll.user32
if user32.RegisterHotKey (None, 1, win32con.MOD_WIN , win32con.VK_F3):
print("hotkey registered")
else:
print("Cannot register hotkey")
self.protocol("WM_HOTKEY", self.hotkey_received)
def hotkey_received(self):
print("hotkey")
if __name__ == "__main__":
app = App()
app.mainloop()
try:
app.destroy()
except:
pass
Thanks
EDIT --------------------------------
OK, I found a way by pushing the whole windows loop in a separate thread, independent from the tkinter mainloop.
It doesn't feel like a clean solution though as I have actually 2 loops running for basically the same kind of interactions with the OS, and it require a message queue to interact with the application, but it do the trick...
If someone has a better option, I would be happy to see it...
A little bit late but maybe it would be helpful for someone... Read this answer.

Different behaviour between python console and python script

I am experiencing different behaviour on the same code using the python console and a python script.
The code is as follows:
import gtk
import webkit
win = gtk.Window()
win.show()
web = webkit.WebView()
win.add(web)
web.show()
web.open("http://www.google.com")
When running the code in the python console, the output is a new frame that contains the google main page.
When running the code as a script, the result is a void frame. It closes very fast but even if I use a delay function, the webkit is not added to the frame.
How is it possible?
Furthermore, using PyDev IDE it flags: "unresolved import: gtk",
but if i run the project, the program starts without problem of compilation. is it normal?
Add
gtk.main()
to the end of your script. This starts the gtk event loop.
import gtk
import webkit
class App(object):
def __init__(self):
win = gtk.Window()
win.connect("destroy", self.destroy)
web = webkit.WebView()
web.open("http://www.google.com")
win.add(web)
web.show()
win.show()
def destroy(self, widget, data = None):
gtk.main_quit()
app = App()
gtk.main()
My guess is that the console keeps the python session open, while at the end of the script the program closes. When the script closes, it takes everything it created with it.
Something to test this theory: if you type "exit" in the console do you see the interface shut down in the same manner? If so, think of some code (e.g. a pause like a raw_input) that will allow the script to stay open.
Good luck!

Enter-Notify-Event Signal not working on gtk.ToolButton

On a happy (if not irrevelent) note, this is the absolute last obstacle in this particular project. If I fix this, I have my first significant dot release (1.0), and the project will be going public. Thanks to everyone here on SO for helping me through this project, and my other two (the answers help across the board, as they should).
Now, to the actual question...
I have a toolbar in my application (Python 2.7, PyGTK) which has a number of gtk.ToolButton objects on it. These function just fine. I have working "clicked" events tied to them.
However, I need to also connect them to "enter-notify-event" and "leave-notify-event" signals, so I can display the button's functions in the statusbar.
This is the code I have. I am receiving no errors, and yet, the status bar messages are not appearing:
new_tb = gtk.ToolButton(gtk.STOCK_NEW)
toolbar.insert(new_tb, -1)
new_tb.show()
new_tb.connect("clicked", new_event)
new_tb.connect("enter-notify-event", status_push, "Create a new, empty project.")
new_tb.connect("leave-notify-event", status_pop)
I know the issue is not with the "status_push" and "status_pop" events, as I've connected all my gtk.MenuItem objects to them, and they work swimmingly.
I know that gtk.ToolButton objects are in the Widgets class, so "enter-notify-event" and "leave-notify-event" SHOULD technically work. My only guess is that this particular object does not emit any signals other than "clicked", and thus I'd have to put each in a gtk.EventBox.
What am I doing wrong here? How do I fix this?
Thanks in advance!
Your guess was correct, you should wrap your widget in a gtk.EventBox, here is an example that i hope will be hopeful:
import gtk
def callback(widget, event, data):
print event, data
class Win(gtk.Window):
def __init__(self):
super(Win, self).__init__()
self.connect("destroy", gtk.main_quit)
self.set_position(gtk.WIN_POS_CENTER)
self.set_default_size(250, 200)
tb = gtk.ToolButton(gtk.STOCK_NEW)
# Wrap ``gtk.ToolButton`` in an ``gtk.EventBox``.
ev_box = gtk.EventBox()
ev_box.connect("enter-notify-event", callback, "enter")
ev_box.connect("leave-notify-event", callback, "leave")
ev_box.add(tb)
self.add(ev_box)
if __name__ == '__main__':
Win()
gtk.main()
It appears, based on experimentation and evidence, this is impossible in PyGtk 2.24.

Python GTK menu item takes two clicks to activate

Alright, first off I'm not quite sure how to phrase my problem. This could be lack of sleep, or being pretty new to Python and GTK, or a combination. To aid me, I have written a complete bare-bones example with the help of zetcode.com's tutorials.
The problem, as well as I can put it, is a menu item - with no sub-menus - takes two clicks to activate. Unlike a sub-menu item activating on a single click. This is mildly annoying (and likely to confuse future users), but not really causing any problems with my application. I would, however, like to resolve it.
My actual application is being created with the help of Ubuntu Quickly - but the problem exists while using gtkBuilder or straight-gtk.
Here is the bare-bones example:
#!/usr/bin/python
import gtk
class MenuTest(gtk.Window):
def __init__(self):
super(MenuTest, self).__init__()
self.set_title("Menus, how do they work?!")
self.set_size_request(350, 200)
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
self.set_position(gtk.WIN_POS_CENTER)
mb = gtk.MenuBar()
filemenu = gtk.Menu()
filem = gtk.MenuItem("Some Action")
filem.connect("activate", self.on_file_activate)
mb.append(filem)
vbox = gtk.VBox(False, 2)
vbox.pack_start(mb, False, False, 0)
self.add(vbox)
self.connect("destroy", gtk.main_quit)
self.show_all()
def on_file_activate(self, widget):
md = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, "herp derp, took two clicks to show me")
md.run()
md.destroy()
MenuTest()
gtk.main()
Hopefully someone can help, and not completely confuse this noob at the same time.
You can solve your problem by connecting to the 'button-press-event' signal instead of the 'activate' signal, and making your callback like this:
def on_file_activate(self, widget, event):
if event.button != 1:
return False #only intercept left mouse button
md = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, "herp derp, I only needed one click")
md.run()
md.destroy()
return True
However, why would you want to do that? I'm not surprised that your original code didn't work as expected, because that's not really what menus are for. You'd be better off using a toolbar button, or a regular button. I think misusing a menu as a button is more likely to confuse future users.
I know this is a fairly old thread. But, for the sake of anyone else trying to accomplish this task, the simplest solution is to replace the "activate" signal with the "select" signal. That should fix it. At least, it does on my box.
ie. replace
filem.connect("activate", self.on_file_activate)
with
filem.connect("select", self.on_file_activate)
I would also change the function name for the sake of clarity.
I hope that helps someone. =)

Adding a program icon in Python GTK

I know this is very simple, just use the command self.set_icon_from_file("icon.png"), however my program still does not display the icon. I made sure the icon.png is in the same working directory as the Python file. I also tried giving the complete file path, but still it does not display the icon.
I am using Ubuntu 10.10 if that helps and using Python V2.6. I use Glade Interface Designer to design the GUI. However, I tried setting the icon both using Glade and using the command above.
I hope I have provided sufficient information.
EDIT: I got the status icon to work in my program.. However in the question I meant the program icon displayed in the task bar and also on the left side of the application bar.
I made sure the icon.png is in the same working directory of the python file.
This may be your problem — paths are looked up relative to the working directory of the Python interpreter, not the file containing the code. I often find myself defining a function like:
def get_resource_path(rel_path):
dir_of_py_file = os.path.dirname(__file__)
rel_path_to_resource = os.path.join(dir_of_py_file, rel_path)
abs_path_to_resource = os.path.abspath(rel_path_to_resource)
return abs_path_to_resource
Mine isn't actually quite that verbose, but hopefully the variable names make it clear what's going on. Also, getting the absolute path isn't strictly necessary, but might help if you need to debug.
Then you can just do:
self.set_icon_from_file(get_resource_path("icon.png"))
Update: Here is a demo program. "icon.png" is in the same directory as this script, and I run it using ./gtktest.py. I see the icon in the top left corner (standard place for my theme). icon.png is just a shape drawn in Inkscape and exported as a bitmap (it works with the original SVG too, anyway).
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class HelloWorld:
def delete_event(self, widget, event, data=None):
return False
def destroy(self, widget, data=None):
gtk.main_quit()
def __init__(self):
# create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_icon_from_file('icon.png')
self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", self.destroy)
# Creates a new button with the label "Hello World".
self.button = gtk.Button("Hello World")
self.window.add(self.button)
self.button.show()
self.window.show()
def main(self):
gtk.main()
if __name__ == "__main__":
hello = HelloWorld()
hello.main()
I am not sure what icon you are creating, but try this smallest PyGTK icon-showing example of taskbar icon I have thought of:
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
# create icon object
statusIcon = gtk.StatusIcon()
# load it
statusIcon.set_from_file("icon.ico")
# show it
statusIcon.set_visible(True)
# and run main gtk loop
gtk.main()
Maybe you just missed the command statusIcon.set_visible(True)
For standard icons, use stock items, and find icons that suits your needs. that way
You don't have to pack icons whith your program
The icons change
according to the user's theme and will blend nicely in her
environment.
for pyGTK :
gtk.icon_theme_get_default().load_icon("folder-open", 128, 0)

Categories