I'm using GtkHScale (which is a slider) to perform seeking in a media player. I connected the "value_changed" signal of the widget to a callback so that the user can seek. I also set a timer, that will update the widget every 250ms so that it will show the current position in track. Unfortunately GTK fires the "value_changed" signal regardless of whether the slider was clicked by the user or updated by the timer. I tried seeking in the "clicked" signal, and the callback was fired, but it always sought to the beginning
You can keep track of whether the user is moving the slider by connecting to the button-press-event and button-release-event signals. When the user presses the mouse button you want to block the updates from the player so they don't move the slider when the user is trying to seek. In the button release handler you can unblock the updates.
One technique is to surround calls to gtk_range_set_value() for playback updates with g_signal_handler_block/unblock [1] calls. This allows the value and UI of the slider to be updated without triggering specific value changed handlers.
Python GTK+ 3 example showing on_value_changed is only called with interactive scrubbing:
from gi.repository import Gtk, GLib
def on_value_changed(hscale):
print('value-changed: %s' % hscale.get_value())
def on_timeout(hscale, handler_id):
# Block value-changed handler during timed playback updates.
with hscale.handler_block(handler_id):
hscale.set_value(hscale.get_value() + 1)
return True
hscale = Gtk.HScale()
hscale.set_range(0, 100)
handler_id = hscale.connect('value-changed', on_value_changed)
window = Gtk.Window()
window.set_size_request(500, 50)
window.add(hscale)
window.connect('destroy', Gtk.main_quit)
window.show_all()
GLib.timeout_add(100, on_timeout, hscale, handler_id)
Gtk.main()
[1] https://developer.gnome.org/gobject/stable/gobject-Signals.html#g-signal-handler-block
Related
GTK+ version: 3.18.9
I created a drawing area (GtkWidget) with following code
content = gtk_drawing_area_new();
gtk_widget_set_can_focus(content, TRUE);
gtk_widget_add_events(content, GDK_ALL_EVENTS_MASK);
g_signal_connect(content, "draw", G_CALLBACK(&drawCallback), ctx);
gtk_widget_realize(content);
// Add a filter for interception
gdk_window_add_filter(content, gtk_widget_get_window(content),
OnXEvent, NULL);
Problem is that when I clicked the wideget, I got sequence of XEvents as follows:
LeaveNotify
FocusIn
EnterNotify
ButtonPress
FocusOut // Lost focus!
ButtonRelease
Above FocusOut indicated we lost focus right away after clicking.
It implies we can't receive any keyboard events since they are available only within focus.
Is this limitation of GTK? If not, is there any tools/methodlogy to find out which widget/widow trigger the FocusOut?
References:
gdk_window_add_filter
As of GTK+ version 3.18.9, it uses an extra X window for toplevel window and any keyboard events are captured by the window. Thus a filter need to be added as a global one, i.e.
gdk_window_add_filter(NULL, OnXEvent, NULL)
Note that the API was removed in this commit in GTK+ 4.x.
Is there any way to detect a click and hold event in PyGTK? I'm in a touch environment, and I want to pop up a context menu from a button if I hold the button down longer than a second or so, since I can't do a right-click or ctrl-click.
If there isn't an easy way, I suppose I could make the mouse press event start a GTK timeout that would activate the menu, and cancel it in the mouse release handler. But if the timeout fires, how would I cancel the mouse press so that the eventual mouse release wouldn't trigger the button click event?
bohrax: After trying that, and beating on it for a while, it mutated into something pretty clean. Maybe someone else can make it even cleaner.
class HoldButton(gtk.Button):
__gsignals__ = { 'held' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) }
def __init__(self, label=None, stock=None, use_underline=True):
gtk.Button.__init__(self, label, stock, use_underline)
self.connect('pressed', HoldButton.h_pressed)
self.connect('clicked', HoldButton.h_clicked)
self.timeout_id = None
def h_clicked(self):
if self.timeout_id:
gobject.source_remove(self.timeout_id)
self.timeout_id = None
else:
self.stop_emission('clicked')
def h_pressed(self):
self.timeout_id = gobject.timeout_add(750, HoldButton.h_timeout, self)
def h_timeout(self):
self.timeout_id = None
self.emit('held')
return False
This starts a timeout in the "pressed" handler. If it is still pending in the "clicked" handler, the timeout is canceled, otherwise the "clicked" signal is terminated with stop_emission. Then I had to figure out how to define a new signal, "held", which is emitted in the timeout handler.
I think the simplest and most portable way would be to use your approach together with a state flag that informs if a "long-click" is in progress. Instead of connecting to the 'clicked' signal of the button you can use 'released' instead and check the value of the flag in that handler.
I'm using Python 3.3 with Pygame and PygButton (low rep don't allow me more than two links)
My files at the moment are [int.py http://pastebin.com/sDRhieCG ] and [scene.py http://pastebin.com/Y2Mgsgmr ].
The idea is making a mainloop in int.py the smaller as possible. The code has a commented-out example of the start_screen buttons at the mainloop. It works, but with every new screen the mainloop would be bloated.
So I created a Scene class to apply background, texts and buttons. It works, but I can make the buttons work. E.g. the bquit button doesn't quit the screen (as it did previously when inserted into the mainloop).
I'm trying to create a scene_loop() inside the Scene() to run everything the specific scene has to offer. With a button click it would change scene and such, start a new scene_loop.
I can't seem to add specific methods after the Scene class is instanced, so I created a Scene_Start class to deal with specific methods like the scene_loop and its buttons (since the background is easily placed through the Scene class).
I'm just stuck and can't see a way to resolve this without scrapping everything and starting again.
Help?
tl;dr:
1. PygButton isn't working outside mainloop
2. How can I create a scene_loop that replaces the mainloop for that scene, "unbloating" mainloop (it would only take care of starting the app and changing scenes).
Thank You.
Problem solved. Answering my own question so it can be helpful to others.
Each class have a sceneloop method that runs and "replace" the mainloop. This allows for unbloating and isolating any problem into the scene without affecting the other components.
The mainloop:
while True:
for event in pg.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pg.quit()
sys.exit()
scene.startscreen.draw()
scene.startscreen.sceneloop()
pg.display.update()
A sceneloop example. This is a method inside the Scene_Start(Scene) class, instanced as startscreen as show above in the main loop.
def sceneloop(self):
while self.scene_loop == True:
for event in pg.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pg.quit()
sys.exit()
if 'click' in self.bcreate.handleEvent(event): #CREATE
startcreate.draw()
startcreate.sceneloop()
if 'click' in self.bstart.handleEvent(event): #START
pg.quit()
sys.exit()
if 'click' in self.bload.handleEvent(event): #LOAD
startload.draw()
startload.sceneloop()
if 'click' in self.boptions.handleEvent(event): #OPTIONS
startoptions.draw()
startoptions.sceneloop()
if 'click' in self.bquit.handleEvent(event): #QUIT
pg.quit()
sys.exit()
self.update()
In Windows, I can blit a bitmap to a DC picked up by GetDC at any time without calling InvalidateRect issuing a WM_PAINT message. How do I port this behavior to GTK/cairo?
In a background thread, I want to
Scale a given bitmap to "best-fit" inside a window (Need to know the width and height of in pixels)
Blit the scaled image to the screen
It should not be done through the expose event (the render loop is fast so the window will be drawn in time anyway). Full logic is
Create a window
Get context of the window
Start message loop
User presses a "Start" button
A new program thread is started that checks for a stop flag. If it is false, it issues commands that update the contents of the window regularly without user interaction. Meanwhile, the message loop checks for user input.
When user presses a "Stop" button the stop flag raises causing the rendering thread to exit
When rendering thread is not running, the expose event should call the same code as the render thread.
I have tried to catch the cairo_t* cairo_obj after creating a GtkDrawingArea (I get one)
the animation thread then calls image draw repeatedly issuing
cairo_set_source_rgb(cairo_obj, 1, 0, 0);
cairo_select_font_face(cairo_obj, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cairo_obj, 40.0);
cairo_move_to(cairo_obj, 10.0, 50.0);
cairo_show_text(cairo_obj, "Disziplin ist Macht.");
This works when doing event passing but not otherwise. Are there any other calls that has to be done at the end of the drawing code swapping buffers?
If this cannot be done with GTK/cairo, are there any other toolkit that lets me do this? Is it possible under X at all?
I am using numeric updowncontrol. For min and max values changed listening for these events
this.numDownMinLimit.ValueChanged += new System.EventHandler(this.numDownMinLimit_ValueChanged);
this.numDownMaxLimit.ValueChanged += new System.EventHandler(this.numDownMaxLimit_ValueChanged);
step size set is 0.1
The eventhandler takes some time to complete.
If you keep the mouse pressed for a period of time and then release it, the eventhandler still executes with the previous values which were fired.
How do we prevent this scenario from occurring? I would like to discard the remaining events which are still in the queue, waiting to be handled, when the user releases the mouse button.
You need to have a look at the NumericUpDownAcceleration Class which handles this behaviour:
From: http://msdn.microsoft.com/en-us/library/system.windows.forms.numericupdownacceleration.aspx
The NumericUpDownAcceleration object
is used by the NumericUpDown control
to optionally provide acceleration
when incrementing or decrementing the
control through a large quantity of
numbers.
Let us know how you get on.