i am writing an Indicator in Python which should update every 5 minutes. Unfortunately the label is updated only 3 times, but then the label stays the same, although the thread still runs.
For demonstration purpose, I replaced the data with the current time.
#!/usr/bin/env python3
import signal
import gi
import threading
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject
import time
class Indicator():
def __init__(self):
self.app = 'turtle-mining-indicator'
self.menu = {}
self.indicator = AppIndicator3.Indicator.new(
self.app, ICON,
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.indicator.set_label("Starting ...", self.app)
self.scheduler()
def update(self):
print("update " + time.asctime())
self.indicator.set_label(time.asctime(), self.app)
self.menu["first"].set_label("First: " + time.asctime())
def scheduler(self):
self.update()
self.timer = threading.Timer(3, self.scheduler).start()
def create_menu(self):
menu = Gtk.Menu()
self.menu["first"] = Gtk.MenuItem("First")
menu.append(self.menu["first"])
menu.show_all()
return menu
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
I don't understand, why this happens. The method update() gets executed but the set_label method only works 3 times. What am I doing wrong?
Mixing threads and Gtk is just plain wrong. GLib.timeout_add_seconds() is made specifically for this reason. Here is the proper way to do timely updates:
#!/usr/bin/env python3
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GLib
import time
class Indicator():
def __init__(self):
self.app = 'turtle-mining-indicator'
self.menu = {}
self.indicator = AppIndicator3.Indicator.new(
self.app, "my indicator",
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.indicator.set_label("Starting ...", self.app)
GLib.timeout_add_seconds (1, self.update)
def update(self):
print("update " + time.asctime())
self.indicator.set_label(time.asctime(), self.app)
self.menu["first"].set_label("First: " + time.asctime())
return True
def create_menu(self):
menu = Gtk.Menu()
self.menu["first"] = Gtk.MenuItem("First")
menu.append(self.menu["first"])
menu.show_all()
return menu
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
Related
I am very new to GTK and Gnome app development, so apologies for my naiveté. (My development language is Python). I would like to use a ListBox to display some data, and the individual row views will be quite complicated (i.e. composed of multiple different widgets). As a result I would prefer not to use a TreeView, because that will require a bunch of custom drawing/event handling. I noticed that ListBox has a bind_model method, but it appears I can't use it to bind a ListStore model, even thought ListStore implements the ListModel interface. Does anybody know how to accomplish this?
A simple exampe:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, GObject
import sys
class Item(GObject.GObject):
text = GObject.property(type = str)
def __init__(self):
GObject.GObject.__init__(self)
class GUI:
def __init__(self):
item1 = Item()
item1.text = "Hello"
item2 = Item()
item2.text = "World"
liststore = Gio.ListStore()
liststore.append(item1)
liststore.append(item2)
listbox=Gtk.ListBox()
listbox.bind_model(liststore, self.create_widget_func)
window = Gtk.Window()
window.add(listbox)
window.connect("destroy", self.on_window_destroy)
window.show_all()
def create_widget_func(self,item):
label=Gtk.Label(item.text)
return label
def on_window_destroy(self, window):
Gtk.main_quit()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
This is condensed code from my open source accounting program.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys
class GUI :
def __init__(self):
listbox = Gtk.ListBox()
employee_name_label = Gtk.Label("Henry", xalign=1)
combo = Gtk.ComboBoxText()
combo.set_property("can-focus", True)
for name in ["bar", "foo", "python"]:
combo.append('0', name)
list_box_row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
list_box_row.add(hbox)
switch = Gtk.Switch()
switch.props.valign = Gtk.Align.CENTER
project_time_label = Gtk.Label("0:00:00", xalign=1 )
project_time_label.set_property('width-chars', 8)
hbox.pack_start(employee_name_label, True, False, 5)
hbox.pack_end(project_time_label, False, False, 5)
hbox.pack_end(switch, False, False, 5)
hbox.pack_end(combo, False, False, 5)
listbox.add(list_box_row)
window = Gtk.Window()
window.add(listbox)
window.connect("destroy", self.on_window_destroy)
window.show_all()
def on_window_destroy(self, window):
Gtk.main_quit()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
It may not answer your question exactly, but it does work and it shows a way to use ListBox. ListBox is a very good choice for complicated setups. In my case I was doing so much operations every second that it crashed Treeviews.
I'm trying to get dynamic cyclic (every half a second) label updates from a Webservice in Python where I parse a JSON string and return its contents to the GUI (made with Glade 3.8.1).
I have started from a basic example and the code I've written so far looks like this:
import sys
import json
import urllib2
import time
try:
import pygtk
pygtk.require("2.0")
except:
pass
try:
import gtk.glade
import gtk
except:
sys.exit(1)
class cRioHMI():
def on_MainWindow_destroy(self, data = None):
print "quit with cancel"
gtk.main_quit()
def on_gtk_quit_activate(self, data = None):
print "quit from menu"
gtk.main_quit()
def on_btnTest_clicked(self, widget):
print "Button Pressed"
def on_gtk_about_activate(self, data = None):
print "About Page Accessed"
self.response = self.about.run()
self.about.hide()
def __init__(self):
self.gladefile = "Assets/HMI.glade"
self.builder = gtk.Builder()
self.builder.add_from_file(self.gladefile)
self.builder.connect_signals(self)
self.window = self.builder.get_object("MainWindow")
self.about = self.builder.get_object("AboutDialogue")
self.templable = self.builder.get_object("lbl_Temperature")
self.window.show()
def update_Values(self, data = None):
response = urllib2.urlopen('http://10.10.10.11:8001/WebUI/Temperatures/GetTemperatures')
data = json.load(response)
temperature = data['Temperature2'][1]
self.templable.set_text(str(temperature))
time.sleep(.5)
if __name__ == "__main__":
HMI = cRioHMI()
gtk.main()
When I use the code from the update_Values method on a click event, the code performs as expected
def on_btnTest_clicked(self, widget):
response = urllib2.urlopen('http://10.10.10.11:8001/WebUI/Temperatures/GetTemperatures')
data = json.load(response)
temperature = data['Temperature2'][1]
self.templable.set_text(str(temperature))
time.sleep(.5)
print "Button Pressed"
but I would like to update multiple labels in a cyclic manner and still have event driven actions.
What is the best way to do that? Please note, that I'm new to python.
You can use gobject.timeout_add (see the documentation here).
So in your __init__ you would have something like gobject.timeout_add(1000, self.updateValues). If you return False the timeout will not be called again.
You should also not use time.sleep. This is a blocking call. That means your GUI will freeze as it cannot handle incoming events. The same thing will happen with the urllib2.urlopen call, if it takes too much time. To prevent this you can run updateValues in a separate Thread. Then you would have to use gobject.idle_add to set the text of the label (see documentation).
Here is a small example. It is just a counter (and would not need threading) but I marked the place where your urllib2.urlopen would go with a comment:
#!/usr/bin/env python2
from threading import Thread
from pygtk import gtk, gobject
class Window(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.connect('delete-event', gtk.main_quit)
self.label = gtk.Label('1')
self.add(self.label)
gobject.timeout_add_seconds(1, self.threaded)
def threaded(self):
thread = Thread(target=self.updateValues)
thread.start()
return True
def updateValues(self):
# urllib.urlopen calls
n = int(self.label.get_text())
gobject.idle_add(self.label.set_text, str(n + 1))
win = Window()
win.show_all()
gobject.threads_init()
gtk.main()
I want to create a player class to play music with gst. This ist the code:
from gi.repository import GObject
GObject.threads_init()
import gst
class PlayerControl:
def __init__(self, main_window):
self.player = gst.element_factory_make("playbin2", "player")
self.sink = gst.element_factory_make("autoaudiosink", "player Output")
self.videosink= gst.element_factory_make("fakesink", "player fake Output")
self.player.set_property("video-sink", self.videosink)
self.player.set_property("audio-sink", self.sink)
self.player_bus = self.player.get_bus()
self.player_bus.add_signal_watch()
self.player_bus.connect("message", self.on_messages)
self.player.connect("about-to-finish", self.on_finish)
self.main_window = main_window
self.main_window.seek_bar.connect("value-changed", self.on_slider_changed)
self.main_window.seek_bar.set_range(0, 100)
self.playing = False
def on_messages(self, bus, message):
print(message)
def play(self, file):
self.playing = True
self.player.set_state(gst.STATE_READY)
self.player.set_property("uri", "file:///"+str(file)) # örnek olsun diye
self.player.set_state(gst.STATE_PLAYING)
GObject.timeout_add(100, self.update_slider)
[...]
I'm calling this from another class like this to play sound:
from file import PlayerControl
class PlayerExample():
def __init__( self ):
self.player_gst = PlayerControl(self)
[...]
def play(self, file):
self.playing = True
self.player.set_state(gst.STATE_READY)
self.player.set_property("uri", "file:///"+str(file))
self.player.set_state(gst.STATE_PLAYING)
GObject.timeout_add(100, self.update_slider)
[...]
I've connected a gtk button to play function. When i clicked the button i take segmentation fault without any information.
Also if i remove self.player_bus.add_signal_watch() and self.player.connect("about-to-finish", self.on_finish) lines from my PlayerControl class, it works and play sound but i need to call on_finished function with "about-to-finish" signal.
Why it doesn't work?
GObject.threads_init() from gi.repository doesn't work with pygst "0.10"
you need to add:
import gobject
gobject.threads_init()
import gst
top of the player class.
Or use Gst-1.0 https://wiki.ubuntu.com/Novacut/GStreamer1.0
Having read the documentation for VPython and GTK threading, it seems to me that it would be possible to embed VPython graphics within a gtk GUI. I know that it is possible with wx on Windows but I am on Linux and using PyGTK. Now, I have managed to get part of the way. I can embed a VPython window provided that it is spawned a separate process. What I would like is to embed it as a thread. The latter would make GUI events that control the OpenGL easier to implement -- via a thread instead of a socket and network calls.
Edit: Apparently nobody knows anything about this... Meh.
Here is the code I have. Uncomment the two commented out lines and comment a few obvious others and you can get to the process spawning code.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from visual import *
import threading
import Queue
import gtk
import pygtk
import re
import subprocess
class OPenGLThreadClass (threading.Thread):
"""Thread running the VPython code."""
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
self.name = 'OpenGLThread'
def run (self):
gtk.threads_enter()
self.scene = display.get_selected()
self.scene.title = 'OpenGL test'
s = sphere()
gtk.threads_leave()
#P = subprocess.Popen(['python', 'opengl.py'])
time.sleep(2)
self.queue.put(self.find_window_id())
self.queue.task_done()
def find_window_id (self):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
P = subprocess.Popen(['xwininfo', '-name', self.scene.title],
#P = subprocess.Popen(['xwininfo', '-name', 'Visual WeldHead'],
stdout=subprocess.PIPE)
for line in P.stdout.readlines():
match = pattern.findall(line)
if len(match):
ret = long(match[0], 16)
print("OpenGL window id is %d (%s)" % (ret, hex(ret)))
return ret
class GTKWindowThreadClass (threading.Thread):
"""Thread running the GTK code."""
def __init__ (self, winID):
threading.Thread.__init__(self)
self.OpenGLWindowID = winID
self.name = 'GTKThread'
def run (self):
"""Draw the GTK GUI."""
gtk.threads_enter()
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
print("Got winID as %d (%s)" % (self.OpenGLWindowID, hex(self.OpenGLWindowID)))
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
gtk.threads_leave()
def main ():
thread = {}
print("Embedding OpenGL/VPython into GTK GUI")
queue = Queue.Queue()
thread['OpenGL'] = OPenGLThreadClass(queue)
thread['OpenGL'].start()
winID = queue.get()
print("Got winID as %d (%s)" % (winID, hex(winID)))
gtk.gdk.threads_init()
thread['GTK'] = GTKWindowThreadClass(winID)
thread['GTK'].start()
if __name__ == "__main__":
main()
This is the code that works in case anyone cares.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess
import sys
import os
import re
import time
from visual import *
def find_window_id (title):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
proc = subprocess.Popen(['xwininfo', '-name', title],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errors = proc.stderr.readlines()
if errors:
return None
for line in proc.stdout.readlines():
match = pattern.findall(line)
if len(match):
return long(match[0], 16)
return None
class Setting ():
"""VPython/OpenGL class."""
def __init__ (self, w=256, h=256, title='OpenGL via VPython'):
"""Initiator."""
self.width = w
self.height = h
self.title = title
self.scene = display.get_selected()
self.scene.title = self.title
self.scene.width = self.width
self.scene.height = self.height
self.sphere = sphere()
class GTKDisplay ():
def __init__ (self, winID):
"""Initiator: Draws the GTK GUI."""
import gtk
import pygtk
self.OpenGLWindowID = winID
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
def main ():
"""Main entry point."""
name = 'sphere OpenGL window'
child_pid = os.fork()
if 0 == child_pid:
sut = Setting(title=name)
else:
winID = None
while not winID:
time.sleep(.1)
winID = find_window_id(name)
try:
gui = GTKDisplay(winID)
except KeyboardInterrupt, err:
print '\nAdieu monde cruel!'
if __name__ == "__main__":
main()
Note: This does not work under Gnome but works under fvwm2. Go figure...
I have the following Python 2.7/PyGObject 3.0/PyGST 0.10 module:
from gi.repository import Gtk, Gdk, GdkPixbuf
import pango
import pygst
pygst.require('0.10')
import gst
import Trailcrest
import os, sys
import cairo
from math import pi
class Video:
def __init__(self):
def on_message(bus, message):
if message.type == gst.MESSAGE_EOS:
# End of Stream
player.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, 5000000000, gst.SEEK_TYPE_NONE, 6000000000)
elif message.type == gst.MESSAGE_ERROR:
player.set_state(gst.STATE_NULL)
(err, debug) = message.parse_error()
print "Error: %s" % err, debug
def on_sync_message(bus, message):
if message.structure is None:
return False
if message.structure.get_name() == "prepare-xwindow-id":
Gdk.threads_enter()
print "Run before"
Gdk.Display.get_default().sync()
print "Run after"
win_id = videowidget.window.xid
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(win_id)
Gtk.gdk.threads_leave()
def click_me(event, data=None):
player.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, 5000000000, gst.SEEK_TYPE_NONE, 6000000000)
win = Gtk.Window()
win.set_resizable(False)
win.set_decorated(False)
win.set_position(Gtk.WindowPosition.CENTER)
fixed = Gtk.Fixed()
win.add(fixed)
fixed.show()
videowidget = Gtk.DrawingArea()
fixed.put(videowidget, 0, 0)
videowidget.set_size_request(640, 480)
videowidget.show()
# Setup GStreamer
player = gst.element_factory_make("playbin", "MultimediaPlayer")
bus = player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
#used to get messages that GStreamer emits
bus.connect("message", on_message)
#used for connecting video to your application
bus.connect("sync-message::element", on_sync_message)
player.set_property("uri", "file://" + os.getcwd() + "/VID/BGA-HABT-001.ogv")
player.set_state(gst.STATE_PLAYING)
win.show()
def main():
Gdk.threads_enter()
Gtk.main()
return 0
if __name__ == "__main__":
Video()
main()
I am always getting this error, along with the video opening in a new window, instead of the existing window.
Traceback (most recent call last): File "video.py", line 32, in
on_sync_message
win_id = videowidget.window.xid AttributeError: 'DrawingArea' object has no attribute 'window'
How do I fix this, so the video displays in the window I created, instead of a new one?
By the by, this problem only started occuring after I switched to PyGObject 3.0 from PyGTK 2.24.
(Reprinted from GNOME PyGObject 3 Bug Report 663360. Answer credit goes to Timo Vanwynsberghe).
There are a couple of things to note:
- the drawingarea has to be realised before you can get its GdkWindow
- apparently, you can't get the window property directly
- you need to import GdkX11 for the xid method
With this in mind, here is a minimal working example:
from gi.repository import GdkX11, Gtk
class App:
def __init__(self):
win = Gtk.Window()
win.resize(400, 400)
win.connect('delete-event', Gtk.main_quit)
da = Gtk.DrawingArea()
win.add(da)
win.show_all()
print da.get_property('window').get_xid()
if __name__ == "__main__":
App()
Gtk.main()