PyGTK Hotkey Toggle Pin/Unpin - toggle

I'm still learning PyGTK, and I'm trying to figure out how I can tell the window to pin/unpin (aka toggle always on top) each time I press the F1 key.
This is what I got so far.
#!/usr/bin/env python
import gtk
class app(gtk.Window):
def pinning(self, widget, event) :
if event.keyval == gtk.keysyms.F1 :
self.set_keep_above(True)
def __init__(self):
super(app, self).__init__()
self.set_position(gtk.WIN_POS_CENTER)
self.set_title("TestApp")
self.set_default_size(320, 200)
self.connect("destroy", gtk.main_quit)
self.connect("key-press-event", self.pinning)
self.show_all()
app()
gtk.main()

Unfortunately, there is no get_keep_above() method. You have to connect to the window's window-state-event signal; in the event parameter, read the new_window_state field to see if the sticky flag is set or not, then keep track of it yourself, for example in self.is_kept_above.
Then you can do
if self.is_kept_above:
self.set_keep_above(False)
else:
self.set_keep_above(True)
in your F1 handler.

In general, you can capture keypresses and then emit whatever signal you want, like I wrote in this answer: PYGTK redirect event to TreeView

Related

GTK: How do I grab keyboard input for a dialog/splash window, so that keyinput works out of window region?

I noticed that when my mouse is out of the dialog area, the keyboard input stops working.
This is detrimental since I want this small app to grab keyboard, so that I could handle it through keyboard without having to move my mouse.
I tried:
windowSetKeepAbove, windowSetSkipPagerHint, windowSetSkipTaskbarHint,
and windowPresentWithTime. I still could not focus in the window. None of these seem to work.
Also tried Seat.grab function, it gave me GDK_GRAB_NOT_VIEWABLE. But I am running this after calling showAll on the main window. Why is it not viewable?
I am so confused now. Any help would be appreciated.
EDIT: It is written in gi-gtk binding of haskell, but I don't think the language would be relevant - it is pretty much 1-1 binding to the gtk library itself. (E.g. windowSetTypeHint corresponds toGtk.Window.set_type_hint)
Here is the close-to-minimal reproducible example. (I guess things like windowSetPosition could have culled out, but it should not affect much. onWidgetKeyPressEvent is to hook into key press event)
{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE LambdaCase #-}
module Main where
import Control.Monad
import Data.Foldable
import Data.Text qualified as T
import GI.Gdk qualified as Gdk
import GI.Gio.Objects qualified as Gio
import GI.Gtk qualified as Gtk
import System.Exit
main :: IO ()
main = do
-- Does not care crashing here
Just app <- Gtk.applicationNew (Just $ T.pack "test.program") []
Gio.onApplicationActivate app (activating app)
status <- Gio.applicationRun app Nothing
when (status /= 0) $ exitWith (ExitFailure $ fromIntegral status)
where
activating :: Gtk.Application -> IO ()
activating app = do
window <- Gtk.applicationWindowNew app >>= Gtk.toWindow
Gtk.windowSetTitle window (T.pack "Test Program")
Gtk.windowSetDefaultSize window 560 140
Gtk.windowSetTypeHint window Gdk.WindowTypeHintDialog
Gtk.windowSetPosition window Gtk.WindowPositionCenterAlways
Gtk.windowSetKeepAbove window True
Gtk.windowSetSkipPagerHint window True
Gtk.windowSetSkipTaskbarHint window True
Gtk.onWidgetKeyPressEvent window $
Gdk.getEventKeyKeyval >=> \case
Gdk.KEY_Escape -> True <$ Gtk.windowClose window
_ -> pure False
Gtk.widgetShowAll window
screen <- Gtk.windowGetScreen window
gdkWins <- Gdk.screenGetToplevelWindows screen
seat <- Gdk.screenGetDisplay screen >>= Gdk.displayGetDefaultSeat
event <- Gtk.getCurrentEvent
putStrLn "Finding window"
filterM (fmap (Gdk.WindowStateAbove `elem`) . Gdk.windowGetState) gdkWins
>>= traverse_
( \win -> do
putStrLn "Window found"
Gdk.windowShow win
stat <- Gdk.seatGrab seat win [Gdk.SeatCapabilitiesAll] True (Nothing #Gdk.Cursor) event Nothing
print stat
)
pure ()
I know, horrible hack, but I don't know other ways to get Gdk.Window. Searched through the gtk library, could not find the way to take Gdk.Window out of Gtk.Window.
Still, it turns out that this hack have found the gdk window.
Running with e.g. cabal run prints:
Finding window
Window found
GrabStatusNotViewable
So I got: GDK_GRAB_NOT_VIEWABLE somehow.
It turns out that later on when e.g. focus event is fired, grab works normally. But I want to grab the mouse/keyboard earlier.
It turns out that the window needs to be mapped to grab the seat. I suspect that showAll does not immediately map the window, so calling seatGrab there leads to an error.
Hence, the better way would be grabbing the focus at Widget's map-event. It conveniently carries the Gdk window field in its event struct, which I could use to grab the seat. In haskell, getEventAnyWindow gets the gdk window from the EventAny struct.
A haskell snippet setting window to grab the focus:
-- | Grab the screen on window map.
windowGrabOnMap :: MonadIO m => Window -> m ()
windowGrabOnMap window = do
afterWidgetMapEvent window $
Gdk.getEventAnyWindow >=> \case
Nothing -> pure False
Just win -> do
event <- getCurrentEvent
seat <- Gdk.windowGetDisplay win >>= Gdk.displayGetDefaultSeat
Gdk.seatGrab seat win [Gdk.SeatCapabilitiesAll] True (Nothing #Gdk.Cursor) event Nothing
pure False
pure ()

Click and hold in PyGTK?

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.

How to handle flow removal event

I want to know when a switch removes any flow rule after hard_timeout is passed. I know ofp_flow_mod has an attribute flags where I can test OFPFF_SEND_FLOW_REM. I made the following in my controller:
def handle_flowRemoval(self, event):
msg = event.parsed
if msg.flags == of.OFPFF_SEND_FLOW_REM:
print ("The switch %s has raised the removal event!" % event.dpid)
In fact it does not trigger this method after hard_timeout is expired. I don't know why. Can anyone suggest how to fix it.
Tank you
If you are sure there are flows installed on the switch best approach would be to add the topology module and listen to the FlowRemoved event mixin
In your main class add something like
core.openflow.addListenerByName("FlowRemoved", self._handle_flow_removal)
and then somewhere to read the event
def _handle_flow_removal (self, event):
"""
handler flow removed event here
"""
print event.__dict__() # to get available info
The FlowRemoved event mixin is in the POX topology.py module at line 172
https://github.com/noxrepo/pox/blob/carp/pox/openflow/topology.py

GTK slider for media seeking

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

Python + Pygame + PygButton detecting

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()