The problem is that clicking on spoke/lightbox pulls the spoke/lightbox above nm-c-e window. I tried these approaches to cope with it:
1) Embedding nm-c-e window in anaconda using Gtk.Socket, Gtk.Plug. This requires patch in nm-connection-editor (adding plug). I made simple POC patch adding --socket XID option to nm-c-e (XID is id of socket window created in anaconda). Embedding the window in our UIObject we can e.g make screenshots and control other things. For main connection editor window it works nice even with lightbox. The problem is that other windows created by nm-c-e (e.g. routes dialog) stay owned by nm-c-e process and therefore are stacked below spoke after cliking on it. Also this can't probably be used easily for secret agent's dialogs (wifi authentication) which are run by dbus service.
2) Setting keep_below hints of windows. With hub, spoke, and lighbox we'd need to set it in this order: 1. lightbox 2. spoke 3. hub which means we need access hub from spoke (in the moment when lightbox is created). Without lighbox we could set the parameters already in hub before running a spoke. Or we can hide hub before displaying spoke and show it after it is destroyed, but it makes transition between hub and spoke not smooth (kind of flashing). I am sending raw patch for this last approach (with hiding of hub window) - "Keep nm-c-e-windows...". NMCEEmbedder class is what remained of UIobject using socket/plug (approach 1) - see the second patch "Embed nm-c-e...". It does not make much sense to use UIObject in this case, but strangely when I try to use just function (the third patch) I get this error when accessing lightbox object in the third patch (applied on top of the first one) "Use function...":
Fatal Python error: PyEval_SaveThread: NULL tstate
Any ideas, comments, hints? Can we hide the hub (perhaps there is finer way to do it, i.e. in callback after spoke is realized...)? Or perhaps we could try to tame our window manager somehow?
Radek
--- pyanaconda/ui/gui/hubs/__init__.py | 2 + pyanaconda/ui/gui/spokes/network.py | 41 ++++++++++++++++++++++++++++++---- pyanaconda/ui/gui/spokes/network.ui | 9 +++++++ 3 files changed, 47 insertions(+), 5 deletions(-)
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py index 40cc6ef..d18b2b6 100644 --- a/pyanaconda/ui/gui/hubs/__init__.py +++ b/pyanaconda/ui/gui/hubs/__init__.py @@ -100,7 +100,9 @@ class Hub(UIObject): # signals from going to the underlying (but still displayed) Hub and # prevent the user from switching away. It's up to the spoke's back # button handler to kill its own layer of main loop. + self.window.hide() Gtk.main() + self.window.show() action.apply()
def _createBox(self): diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index a7eb3b6..9394037 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -38,6 +38,7 @@ from pyanaconda.ui.gui import UIObject from pyanaconda.ui.gui.spokes import NormalSpoke, StandaloneSpoke from pyanaconda.ui.gui.categories.software import SoftwareCategory from pyanaconda.ui.gui.hubs.summary import SummaryHub +from pyanaconda.ui.gui.utils import enlightbox
from pyanaconda.network import NetworkDevice, netscriptsDir, kickstartNetworkData, getActiveNetDevs
@@ -46,7 +47,6 @@ import dbus import socket import subprocess import struct -import time from dbus.mainloop.glib import DBusGMainLoop dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
@@ -224,8 +224,13 @@ class NetworkControlBox(): NetworkManager.DeviceType.WIFI, ]
- def __init__(self, builder): + @staticmethod + def run_nmce(uuid): + subprocess.Popen(["nm-connection-editor", "--edit", "%s" % uuid]) + + def __init__(self, builder, configure_callback=run_nmce):
+ self.run_config_tool = configure_callback self.builder = builder
# these buttons are only for vpn and proxy @@ -436,7 +441,7 @@ class NetworkControlBox(): print "DBG: no connection to be edited found" return
- subprocess.Popen(["nm-connection-editor", "--edit", "%s" % uuid]) + self.run_config_tool(uuid)
def on_wireless_enabled(self, *args): switch = self.builder.get_object("device_wireless_off_switch") @@ -927,7 +932,11 @@ class NetworkSpoke(NormalSpoke):
def __init__(self, *args, **kwargs): NormalSpoke.__init__(self, *args, **kwargs) - self.network_control_box = NetworkControlBox(self.builder) + def run_nmce_cb(uuid): + embedder = NMCEEmbedder(self.data) + embedder.run(self.window, uuid) + self.network_control_box = NetworkControlBox(self.builder, + configure_callback=run_nmce_cb)
def apply(self): self.data.network.network = [] @@ -985,7 +994,11 @@ class NetworkStandaloneSpoke(StandaloneSpoke):
def __init__(self, *args, **kwargs): StandaloneSpoke.__init__(self, *args, **kwargs) - self.network_control_box = NetworkControlBox(self.builder) + def run_nmce_cb(uuid): + embedder = NMCEEmbedder(self.data) + embedder.run(self.window, uuid) + self.network_control_box = NetworkControlBox(self.builder, + configure_callback=run_nmce_cb) parent = self.builder.get_object("AnacondaStandaloneWindow-action_area5") parent.add(self.network_control_box.vbox)
@@ -1049,6 +1062,24 @@ def getKSNetworkData(device):
return retval
+class NMCEEmbedder(UIObject): + builderObjects = ["nmceEmbedder"] + mainWidgetName = "nmceEmbedder" + uiFile = "spokes/network.ui" + def run(self, spoke, uuid): + from gi.repository import AnacondaWidgets + self.lb = AnacondaWidgets.lb_show_over(spoke) + + self.lb.set_keep_below(True) + spoke.set_keep_below(True) + + proc = subprocess.Popen(["nm-connection-editor", "--edit", "%s" % + uuid]) + GLib.child_watch_add(proc.pid, self.on_nmce_exited) + + def on_nmce_exited(self, pid, condition): + self.lb.destroy() + if __name__ == "__main__":
win = Gtk.Window() diff --git a/pyanaconda/ui/gui/spokes/network.ui b/pyanaconda/ui/gui/spokes/network.ui index 49c75c2..7394d08 100644 --- a/pyanaconda/ui/gui/spokes/network.ui +++ b/pyanaconda/ui/gui/spokes/network.ui @@ -2216,4 +2216,13 @@ updates available for you.</property> </object> </child> </object> + <object class="GtkWindow" id="nmceEmbedder"> + <property name="can_focus">False</property> + <property name="modal">True</property> + <property name="window_position">center</property> + <property name="type_hint">dialog</property> + <child> + <placeholder/> + </child> + </object> </interface>
Requires patch in nm-c-e: --socket XID option that would place nm-c-e window in plug connected to XID. --- pyanaconda/ui/gui/hubs/__init__.py | 2 -- pyanaconda/ui/gui/spokes/network.py | 22 ++++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py index d18b2b6..40cc6ef 100644 --- a/pyanaconda/ui/gui/hubs/__init__.py +++ b/pyanaconda/ui/gui/hubs/__init__.py @@ -100,9 +100,7 @@ class Hub(UIObject): # signals from going to the underlying (but still displayed) Hub and # prevent the user from switching away. It's up to the spoke's back # button handler to kill its own layer of main loop. - self.window.hide() Gtk.main() - self.window.show() action.apply()
def _createBox(self): diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index 9394037..e0ed369 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -1066,18 +1066,28 @@ class NMCEEmbedder(UIObject): builderObjects = ["nmceEmbedder"] mainWidgetName = "nmceEmbedder" uiFile = "spokes/network.ui" + def run(self, spoke, uuid): + socket = Gtk.Socket() + self.window.add(socket) + from gi.repository import AnacondaWidgets self.lb = AnacondaWidgets.lb_show_over(spoke) + self.window.set_transient_for(self.lb) + + socket.connect("plug-removed", self.on_plug_removed) + #socket.connect("plug-added", self.display) + + socket_id = socket.get_id()
- self.lb.set_keep_below(True) - spoke.set_keep_below(True) + proc = subprocess.Popen(["/home/rvykydal/work/newui/anaconda/nm-connection-editor", "--edit", "%s" % uuid, "--socket", "%d" % socket_id]) + GLib.timeout_add(2000, self.display)
- proc = subprocess.Popen(["nm-connection-editor", "--edit", "%s" % - uuid]) - GLib.child_watch_add(proc.pid, self.on_nmce_exited) + def display(self, *args): + self.window.show_all()
- def on_nmce_exited(self, pid, condition): + def on_plug_removed(self, *args): + self.window.destroy() self.lb.destroy()
if __name__ == "__main__":
--- pyanaconda/ui/gui/spokes/network.py | 41 ++++++++++++++++------------------ 1 files changed, 19 insertions(+), 22 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py index 9394037..4e45e38 100644 --- a/pyanaconda/ui/gui/spokes/network.py +++ b/pyanaconda/ui/gui/spokes/network.py @@ -920,6 +920,23 @@ class NetworkControlBox(): return [row[DEVICES_COLUMN_OBJECT] for row in self.builder.get_object("liststore_devices")]
+def run_nmce_with_lightbox(spoke, uuid): + from gi.repository import AnacondaWidgets + lb = AnacondaWidgets.lb_show_over(spoke) + + lb.set_keep_below(True) + spoke.set_keep_below(True) + + proc = subprocess.Popen(["nm-connection-editor", "--edit", "%s" % + uuid]) + #GLib.child_watch_add(proc.pid, lambda pid, condition: lb.destroy()) + + def destroy_lb(*args): + lb.destroy() + + GLib.child_watch_add(proc.pid, destroy_lb) + + class NetworkSpoke(NormalSpoke): builderObjects = ["networkWindow", "liststore_wireless_network", "liststore_devices"] mainWidgetName = "networkWindow" @@ -933,8 +950,7 @@ class NetworkSpoke(NormalSpoke): def __init__(self, *args, **kwargs): NormalSpoke.__init__(self, *args, **kwargs) def run_nmce_cb(uuid): - embedder = NMCEEmbedder(self.data) - embedder.run(self.window, uuid) + run_nmce_with_lightbox(self.window, uuid) self.network_control_box = NetworkControlBox(self.builder, configure_callback=run_nmce_cb)
@@ -995,8 +1011,7 @@ class NetworkStandaloneSpoke(StandaloneSpoke): def __init__(self, *args, **kwargs): StandaloneSpoke.__init__(self, *args, **kwargs) def run_nmce_cb(uuid): - embedder = NMCEEmbedder(self.data) - embedder.run(self.window, uuid) + run_nmce_with_lightbox(self.window, uuid) self.network_control_box = NetworkControlBox(self.builder, configure_callback=run_nmce_cb) parent = self.builder.get_object("AnacondaStandaloneWindow-action_area5") @@ -1062,24 +1077,6 @@ def getKSNetworkData(device):
return retval
-class NMCEEmbedder(UIObject): - builderObjects = ["nmceEmbedder"] - mainWidgetName = "nmceEmbedder" - uiFile = "spokes/network.ui" - def run(self, spoke, uuid): - from gi.repository import AnacondaWidgets - self.lb = AnacondaWidgets.lb_show_over(spoke) - - self.lb.set_keep_below(True) - spoke.set_keep_below(True) - - proc = subprocess.Popen(["nm-connection-editor", "--edit", "%s" % - uuid]) - GLib.child_watch_add(proc.pid, self.on_nmce_exited) - - def on_nmce_exited(self, pid, condition): - self.lb.destroy() - if __name__ == "__main__":
win = Gtk.Window()
Fatal Python error: PyEval_SaveThread: NULL tstate
I've never seen this before.
Any ideas, comments, hints? Can we hide the hub (perhaps there is finer way to do it, i.e. in callback after spoke is realized...)? Or perhaps we could try to tame our window manager somehow?
I've been trying to think of a good idea all this week, but I haven't come up with anything yet.
We could certainly hide the hub if it'll make things easier. I've noticed the flashing too, but I've noticed that in several other places and don't really have a good idea how to go about fixing it. So I would be willing to go with that approach here too.
Honestly, I have always thought that leaving the hub hidden below was kind of gross.
For the window manager, we've already disabled alt-tab with a patch to lorax so that might help a little bit here.
I'll take a look at your third patch in more detail.
- Chris
anaconda-devel@lists.stg.fedoraproject.org