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.
Related
I do a remove action through RxJava2 that causes a refresh on my local cache like this:
override fun removeExperience(experienceId: String, placeId: String): Completable {
return from(placesApi.deleteExperience(experienceId, placeId))
.andThen(from(refreshPlace(placeId))
.flatMapCompletable { Completable.complete() }
)
}
so whenever the remove action is done (Completable is complete), a refresh is triggered. The problem is, sometimes this remove action takes long enough for users to just leave the screen, and then the andThen action is never executed cause there is no subscribers anymore, and thus the information on the screen is not up to date anymore.
Is there a way to enforce this action to take place?
Does this logic continue working when user open the same screen again? If so, then you only need to finish subscription from(placesApi.deleteExperience(experienceId, placeId)) on lifecycle events. The easiest way is to add the whole subscription removeExperience() to Disposable or CompositeDisposable and then trigger its .dispose() or .clear() on view stop or destroy events.
.dispose() - doesn't allow to use the same subscription stored.
.clear() - allows re-subscription without creating the new
subscription instance
I have a program like this
...
$self->{bn1}=Wx::Button->new( $tb1, -1, 'Start');
EVT_BUTTON( $self, $self->{bn1}, \&on_click_start );
...
...
sub on_click_start
{
my( $this, $event ) = #_;
$this->{bn1}->SetLabel("Cancel");
$event->Skip;
for (...) {
long_time_operation();
last if ( Cancel_clicked );
}
}
...
My problem is when I click the Start button, on_click_start() will be called, and I want change the label of Start button to Cancel, that allow I to click the button to break out the long_time_operation() loop.
How do I make right code for it?
The only real solution is to use multiple threads and perform the long operation in a background thread. If you want to keep your code simple, you can use wxYield() to handle the events from inside this event handler, but be aware that this may (and will) result in difficult to debug problems due to reentrancy, so at the very least you need to disable the rest of your UI if you're doing it like this.
Try with a wxTimer and start it in your on_click_start function. Put the long time operation code under the event of the timer (ensure that it dont trigger repeteadly, just stop the timer there too).
Inside your long operation function use some global var to know if you want to cancel. In the event of your button now change the value of your global var so your long term code knows about that and cancels/break from the loop.
I am not sure if a loop under the event of the timer can hang your UI, but it appears to use threads, so that may not happen. Try it anyway, i always use wxTimer when I need something like that (dont hanging the UI and not using threads directly).
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
I am using Galasoft MVVMLight. I have a button bound to a command which sends a message to the view to display a messagebox asking for confirmation. If I click either the Yes or No on the messagebox it performs the necessary actions then shows up again. However if I step through the program instead I only get the messagebox once. Is this a bug or is something else going on?
EDIT: I modified the messagebox.show line by adding an Icon and default result and now I can't reproduce this behavior... I'm stumped... if it happens again I'll try a counter like airplaneman19 suggested.
Try tracking the amount of times the MessageBox shows up with an integer, like so:
int counter = 0;
if(counter == 0){
MessageBox.Show();
counter++;
}
else if (counter == 1)
/*Do something that won't alter the program just to escape the if....else statement
like "x++";
I had a similar problem once, I mean, with MessageBox firing twice. It was due to focus changes, and ListView in WinForms fired another selection changed event when running the app; but when debugging - some focus change was missing, and there was no bug :)
I hope this atleast gives you some ideas...
I have a virtual trackpad on my iPhone and to move my mouse I'm using :
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, CGPointMake(((float)aD.msg)+location.x, ((float)aD.msg2)+location.y));
It's working well but this not a real mouse because when I put my mouse on my hidden dock, this one doesn't display it self. I don't understand why.
More over I tried to simulate mouse click with :
case MOUSECLICK:
[self postMouseEventWithButton:0 withType:kCGEventLeftMouseDown andPoint:CGEventGetLocation(CGEventCreate(NULL))];
[self postMouseEventWithButton:0 withType:kCGEventLeftMouseUp andPoint:CGEventGetLocation(CGEventCreate(NULL))];
// *********************
-(void)postMouseEventWithButton:(CGMouseButton)b withType:(CGEventType)t andPoint:(CGPoint)p
{
CGEventRef theEvent = CGEventCreateMouseEvent(NULL, t, p, b);
CGEventSetType(theEvent, t);
CGEventPost(kCGHIDEventTap, theEvent);
CFRelease(theEvent);
}
Is it the good method? Thanks for your help !
CGDisplayMoveCursorToPoint() only moves the image of the cursor, it does not generate any events. You should create and post mouse events of type kCGEventMouseMoved to simulate moving the mouse. Your own method would do it:
[self postMouseEventWithButton:0 withType:kCGEventMouseMoved andPoint:point];
For clicks, you are already doing it the right way, I think. One thing you should also do is set the click count properly on both the mouse down and mouse up events, like so:
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 1);
... because some applications need it.
(See also Simulating mouse clicks on Mac OS X does not work for some applications)
If your code doesn't work, I'm not sure why; it looks OK to me. Try posting to kCGSessionEventTap instead of kCGHIDEventTap and see if it helps. Also, you don't need the CGEventSetType() call since the type is already set in the creation call.