4

So I have the following code (python 2.7) that checks for a combination of keypresses and shows/hides a gtk window with a few gtk entry boxes. The show/hide works until the window locks up and stops responding, and the inner part of the window turns black. I have to kill the process and start it up again once that window turns black. I've tried all different combinations of show_all() and returning True after hiding the window to no avail. I'm not sure what I'm doing wrong here.enter image description here

In the end I'd like to be able to press this key combination and show/hide this window without the contents going away.

#!/usr/bin/python

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import pyxhook

class MyWindow(Gtk.Window):

    def __init__(self):
    Gtk.Window.__init__(self, title="Configurator")

    self.box = Gtk.Box(spacing=6)
    self.add(self.box)

    self.ipEntry = Gtk.Entry()
    self.ipEntry.set_text("IP Address")

    self.maskEntry = Gtk.Entry()
    self.maskEntry.set_text("NetMask")

    self.gatewayEntry = Gtk.Entry()
    self.gatewayEntry.set_text("gatewayEntry")

    self.button1 = Gtk.Button(label="Save")
    self.button1.connect("clicked", self.on_button1_clicked)
    self.box.pack_start(self.ipEntry, True, True, 0)
    self.box.pack_start(self.maskEntry, True, True, 0)
    self.box.pack_start(self.gatewayEntry, True, True, 0)
    self.box.pack_start(self.button1, True, True, 0)

    #catch window destroy event and stop it from happening
    self.connect('delete-event', self.on_destroy)

    def on_button1_clicked(self, widget):
    print("Hello")
    def on_destroy(self, widget=None, *data):
    print("tried to destroy")
    self.hide()
    return True

#list of ascii keypresses to test if a certain combination of keys is pressed
keypresses = []

win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.set_keep_above(True)

def OnKeyboardEvent(event):
#ascii 227 is l_control, ascii 225 is l_shift, ascii 120 is x
#bring the following external variables into the scope of this function
    global keypresses
    global win
#check if gtk window is hidden or visible
    isVisible = win.get_property("visible")

    #store our keypress if it is worthy of being stored (we started with a control character)
    if event.Ascii == 227 or ( len(keypresses) >= 1 and keypresses[0] == 227 ):
            print("here" + str(len(keypresses)))
            keypresses.append(event.Ascii)
    #check if we have three items in keypresses to compare, then see if it's the right combination
    if len(keypresses) == 3 and keypresses[0] == 227 and keypresses[1] == 225 and keypresses[2] == 120:
        #show/hide our window
            if isVisible:
            win.hide()
            del keypresses[:]
            keypresses = []
        else:
            win.show_all()
            #clear out our list to compare again
            del keypresses[:]
            keypresses = []
    elif len(keypresses) >= 3:
            del keypresses[:]
            keypresses = []

#create instance of hook manager
HookManager = pyxhook.HookManager()
#define our callback to fire when a key is pressed
HookManager.KeyDown = OnKeyboardEvent
#hook the keyboard
HookManager.HookKeyboard()
#start our listener
HookManager.start()

Gtk.main()
#close the hook listener when gtk main loop exits
HookManager.cancel()
Corey Manshack
  • 147
  • 2
  • 3
  • 10
  • Check your code indentation, it doesnt seems right. – José Fonte May 09 '17 at 21:37
  • Stackoverflow messed it up when I pasted in. Here is a pastebin of it. https://pastebin.com/yrWYiBYw – Corey Manshack May 09 '17 at 21:38
  • Althought I'm not into Python I'll have a look. First thing that came into my eyes was the return True on delete-event signal callback. If you return True the signal will cease propagation and won't call Gtk.main_quit. Change that to False to exit properly. – José Fonte May 09 '17 at 21:54

1 Answers1

0

Here is a small hack that tests the window show/hide when pressing F5. When you press it a second window will show or hide and i've tested for sometime without problems. Maybe it's the HookManager that somehow is messing with the Gtk/Glib mainloop. Nonetheless my method only works if there is a window to grab the key presses, so you will need, indeed, some form of grabbing key presses if your goal is to have no GUI:

#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
#import pyxhook

class MyWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Configurator")

        self.box = Gtk.Box(spacing=6)
        self.add(self.box)

        self.ipEntry = Gtk.Entry()
        self.ipEntry.set_text("IP Address")

        self.maskEntry = Gtk.Entry()
        self.maskEntry.set_text("NetMask")

        self.gatewayEntry = Gtk.Entry()
        self.gatewayEntry.set_text("gatewayEntry")

        self.button1 = Gtk.Button(label="Save")
        self.button1.connect("clicked", self.on_button1_clicked)
        self.box.pack_start(self.ipEntry, True, True, 0)
        self.box.pack_start(self.maskEntry, True, True, 0)
        self.box.pack_start(self.gatewayEntry, True, True, 0)
        self.box.pack_start(self.button1, True, True, 0)

        #catch window destroy event and stop it from happening
        self.connect('delete-event', self.on_destroy)
        self.connect('key-press-event', self.on_keypress)

    def on_keypress(self, widget, event):
        global w
        if event.keyval == Gdk.KEY_F5:
            isVisible = w.get_property("visible")
            if (isVisible):
                w.hide()
            else:
                w.show()
        print("Keypress")

    def on_button1_clicked(self, widget):
        print("Hello")

    def on_destroy(self, widget=None, *data):
        print("tried to destroy")
        self.hide()
        return False

w = MyWindow()

win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.set_keep_above(True)


Gtk.main() 
José Fonte
  • 4,016
  • 2
  • 16
  • 28
  • So if it's the hookmanager(pyxhook) that is maybe messing with the mainloop should I use threading and create a new thread for hookmanager? Also with `return False` in the `on_destroy` method the window actually gets destroyed. – Corey Manshack May 10 '17 at 13:29
  • Yes it get's destroyed because it was programmed accordingly. The callback inside the class hides the window but lets the signal propagate, then on the program, you added, again, the delete-event signal handler (callback) which will call Gtk.main_quit which will exit the program and the window will get destroyed. Anyway, you must exit gracefully otherwise the application will not exit to the console. About threading, I would not recommend unless you know what your are doing, UI Mainloops don't like threads, at least those that update the UI "from outside" themselfs (loop wise). – José Fonte May 10 '17 at 13:57
  • I'm looking into all sorts of threaded options now as it is clear hookmanager is interfering with the gtk main thread. http://stackoverflow.com/questions/21150914/python-gtk-3-safe-threading – Corey Manshack May 10 '17 at 16:09
  • Check out the Keybinder library if you require global bindings. – TingPing May 11 '17 at 00:05
  • Thanks tingping! I already abandoned doing it in python and did it with nodejs instead, that probably would have solved my problem 100%. – Corey Manshack May 17 '17 at 20:42