gfxdetails: Update UI a bit

Refactor the internals to cleanly separate the pieces that fill in
the UI, and the pieces that react to UI state to dynamically show/hide
fields.

Improve spice GL warnings while he are here, and several other minor
fixes

Signed-off-by: Cole Robinson <crobinso@redhat.com>
This commit is contained in:
Cole Robinson 2020-09-01 18:04:38 -04:00
parent 525afd1aa6
commit a10d746c41
4 changed files with 191 additions and 155 deletions

View File

@ -472,6 +472,7 @@ class Details(uiutils.UITestCase):
tab.combo_select("Type:", "VNC")
tab.combo_select("Listen type:", "Address")
tab.find("graphics-port-auto", "check").click()
tab.find("graphics-port-auto", "check").click()
tab.find("graphics-port", "spin button").set_text("6001")
tab.find("Password:", "check").click()
passwd = tab.find_fuzzy("graphics-password", "text")

View File

@ -53,6 +53,7 @@
<object class="GtkEntry" id="graphics-password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">start</property>
<property name="visibility">False</property>
<property name="invisible_char">●</property>
<signal name="changed" handler="on_graphics_password_changed" swapped="no"/>
@ -297,45 +298,34 @@
</packing>
</child>
<child>
<object class="GtkGrid" id="graphics-opengl-box">
<object class="GtkGrid" id="graphics-opengl-subopts-box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="row_spacing">3</property>
<property name="halign">start</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkBox" id="box77">
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">3</property>
<child>
<object class="GtkCheckButton" id="graphics-opengl">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_graphics_opengl_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="graphics-opengl-warn">
<property name="can_focus">False</property>
<property name="stock">gtk-dialog-warning</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<property name="valign">start</property>
<property name="stock">gtk-dialog-warning</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="stock">gtk-dialog-warning</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
@ -360,14 +350,63 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="graphics-warn-virtio">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">OpenGL only works with 'virtio' graphics with '3D acceleration' enabled</property>
<property name="wrap">True</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="graphics-warn-listen">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">OpenGL only works with 'Listen type' value 'none'</property>
<property name="wrap">True</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="graphics-opengl">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_graphics_opengl_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</interface>

View File

@ -2096,7 +2096,9 @@ class vmmDetails(vmmGObjectUI):
_("Hypervisor does not support removing this device"))
def _refresh_graphics_page(self, gfx):
title = self.gfxdetails.set_dev(gfx)
pretty_type = vmmGraphicsDetails.graphics_pretty_type_simple(gfx.type)
title = (_("%(graphicstype)s Server") % {"graphicstype": pretty_type})
self.gfxdetails.set_dev(gfx)
self.widget("graphics-title").set_markup("<b>%s</b>" % title)
def _refresh_sound_page(self, sound):

View File

@ -33,8 +33,8 @@ class vmmGraphicsDetails(vmmGObjectUI):
self.builder.connect_signals({
"on_graphics_type_changed": self._change_graphics_type,
"on_graphics_port_auto_toggled": self._change_port_auto,
"on_graphics_use_password": self._change_password_chk,
"on_graphics_show_password": self._show_password_chk,
"on_graphics_use_password": self._change_password_cb,
"on_graphics_show_password": self._show_password_cb,
"on_graphics_listen_type_changed": self._change_graphics_listen,
"on_graphics_password_changed": lambda ignore: self.emit("changed-password"),
@ -114,6 +114,69 @@ class vmmGraphicsDetails(vmmGObjectUI):
port = -1
return port
##############
# UI syncing #
##############
def _sync_ui(self):
gtype = uiutil.get_list_selection(self.widget("graphics-type"))
is_vnc = gtype == "vnc"
is_spice = gtype == "spice"
listen = uiutil.get_list_selection(self.widget("graphics-listen-type"))
has_listen_none = (listen in ["none", "socket"])
has_virtio_3d = bool([
v for v in self.vm.xmlobj.devices.video if
(v.model == "virtio" and v.accel3d)])
uiutil.set_grid_row_visible(
self.widget("graphics-warn-virtio"),
not has_virtio_3d)
uiutil.set_grid_row_visible(
self.widget("graphics-warn-listen"),
not has_listen_none)
passwd_enabled = self.widget("graphics-password-chk").get_active()
self.widget("graphics-password").set_sensitive(passwd_enabled)
if not passwd_enabled:
self.widget("graphics-password").set_text("")
passwd_visible = self.widget("graphics-visiblity-chk").get_active()
self.widget("graphics-password").set_visibility(passwd_visible)
glval = self.widget("graphics-opengl").get_active()
uiutil.set_grid_row_visible(
self.widget("graphics-opengl-subopts-box"),
glval and is_spice)
all_rows = [
"graphics-listen-type",
"graphics-address",
"graphics-password-box",
"graphics-port-box",
"graphics-opengl",
"graphics-opengl-subopts-box",
]
is_auto = (self.widget("graphics-port-auto").get_active() or
self.widget("graphics-port-auto").get_inconsistent())
self.widget("graphics-port").set_visible(not is_auto)
rows = ["graphics-password-box", "graphics-listen-type"]
if listen == 'address':
rows.extend(["graphics-port-box", "graphics-address"])
if is_spice:
rows.append("graphics-opengl")
if glval:
rows.append("graphics-opengl-subopts-box")
if not is_vnc and not is_spice:
rows = []
for row in all_rows:
uiutil.set_grid_row_visible(self.widget(row), row in rows)
##############
# Public API #
@ -128,13 +191,12 @@ class vmmGraphicsDetails(vmmGObjectUI):
rendermodel = self.widget("graphics-rendernode").get_model()
self.widget("graphics-rendernode").set_active_iter(rendermodel[-1].iter)
self._change_ports()
self.widget("graphics-port-auto").set_active(True)
self.widget("graphics-password").set_text("")
self.widget("graphics-password").set_sensitive(False)
self.widget("graphics-password-chk").set_active(False)
self.widget("graphics-opengl").set_active(False)
self._sync_opengl_ui()
self._sync_ui()
def get_values(self):
gtype = uiutil.get_list_selection(self.widget("graphics-type"))
@ -146,156 +208,88 @@ class vmmGraphicsDetails(vmmGObjectUI):
if not self.widget("graphics-password-chk").get_active():
passwd = None
gl = self.widget("graphics-opengl").get_active()
glval = self.widget("graphics-opengl").get_active()
if not self.widget("graphics-opengl").is_visible():
glval = None
rendernode = uiutil.get_list_selection(self.widget("graphics-rendernode"))
if not self.widget("graphics-rendernode").get_visible():
if not self.widget("graphics-rendernode").is_visible():
rendernode = None
return gtype, port, listen, addr, passwd, gl, rendernode
return gtype, port, listen, addr, passwd, glval, rendernode
def set_dev(self, gfx):
self.reset_state()
def set_port(basename, val):
auto = self.widget(basename + "-auto")
widget = self.widget(basename)
auto.set_inconsistent(False)
label = _("A_uto")
portval = gfx.port
portautolabel = _("A_uto")
if val == -1 or gfx.autoport:
auto.set_active(True)
if val and val != -1: # pragma: no cover
# Triggering this with the test driver is tough
# because it doesn't fill in runtime port values
label = _("A_uto (Port %(port)d)") % {"port": val}
elif val is None:
auto.set_inconsistent(True)
else:
auto.set_active(False)
widget.set_value(val)
if portval == -1 or gfx.autoport:
portauto = True
if portval and portval != -1: # pragma: no cover
# Triggering this with the test driver is tough
# because it doesn't fill in runtime port values
portautolabel = _("A_uto (Port %(port)d)") % {"port": portval}
elif portval is None:
portauto = None
else:
portauto = False
auto.set_label(label)
self.widget("graphics-port").set_value(portval or 0)
self.widget("graphics-port-auto").set_label(portautolabel)
self.widget("graphics-port-auto").set_active(bool(portauto))
self.widget("graphics-port-auto").set_inconsistent(portauto is None)
gtype = gfx.type
is_vnc = (gtype == "vnc")
is_spice = (gtype == "spice")
pretty_type = vmmGraphicsDetails.graphics_pretty_type_simple(gtype)
title = (_("%(graphicstype)s Server") % {"graphicstype": pretty_type})
if is_vnc or is_spice:
use_passwd = gfx.passwd is not None
set_port("graphics-port", gfx.port)
listentype = gfx.get_first_listen_type()
if listentype and listentype == 'none':
uiutil.set_list_selection(self.widget("graphics-listen-type"), 'none')
else:
uiutil.set_list_selection(self.widget("graphics-listen-type"), 'address')
uiutil.set_list_selection(
self.widget("graphics-address"), gfx.listen)
self.widget("graphics-password").set_text(gfx.passwd or "")
self.widget("graphics-password-chk").set_active(use_passwd)
self.widget("graphics-password").set_sensitive(use_passwd)
if is_spice:
opengl_warning = ""
glval = bool(gfx.gl)
renderval = gfx.rendernode or None
# If the config doesn't support spice GL, show a warning
# but still let the user set the value in case we are wrong
if not [v for v in self.vm.xmlobj.devices.video if
(v.model == "virtio" and v.accel3d)]:
opengl_warning = _("Spice GL requires "
"VirtIO graphics configured with accel3d.")
elif gfx.get_first_listen_type() not in ["none", "socket"]:
opengl_warning = _("Graphics listen type does not support "
"spice GL.")
self.widget("graphics-opengl").set_active(glval)
if glval:
# Only sync rendernode UI with XML, if gl=on, otherwise
# we want to preserve the suggested rendernode already
# selected in the UI
uiutil.set_list_selection(
self.widget("graphics-rendernode"), renderval)
self.widget("graphics-opengl-warn").set_tooltip_text(
opengl_warning or None)
self.widget("graphics-opengl-warn").set_visible(
bool(opengl_warning))
uiutil.set_list_selection(self.widget("graphics-type"), gtype)
return title
use_passwd = gfx.passwd is not None
self.widget("graphics-password").set_text(gfx.passwd or "")
self.widget("graphics-password-chk").set_active(use_passwd)
listentype = gfx.get_first_listen_type()
uiutil.set_list_selection(
self.widget("graphics-listen-type"), listentype)
uiutil.set_list_selection(
self.widget("graphics-address"), gfx.listen)
glval = bool(gfx.gl)
renderval = gfx.rendernode or None
self.widget("graphics-opengl").set_active(glval)
if glval:
# Only sync rendernode UI with XML, if gl=on, otherwise
# we want to preserve the suggested rendernode already
# selected in the UI
uiutil.set_list_selection(
self.widget("graphics-rendernode"), renderval)
#############
# Listeners #
#############
def _show_rows_from_type(self):
hide_all = ["graphics-address",
"graphics-password-box", "graphics-port-box",
"graphics-opengl-box"]
gtype = uiutil.get_list_selection(self.widget("graphics-type"))
listen = uiutil.get_list_selection(self.widget("graphics-listen-type"))
vnc_rows = ["graphics-password-box"]
if listen == 'address':
vnc_rows.extend(["graphics-port-box", "graphics-address"])
spice_rows = vnc_rows[:]
spice_rows.extend(["graphics-opengl-box"])
rows = []
if gtype == "vnc":
rows = vnc_rows
elif gtype == "spice":
rows = spice_rows
for row in hide_all:
uiutil.set_grid_row_visible(self.widget(row), row in rows)
def _change_graphics_type(self, ignore):
self._show_rows_from_type()
self._sync_ui()
self.emit("changed-type")
def _change_graphics_listen(self, ignore):
self._show_rows_from_type()
self._sync_ui()
self.emit("changed-listen")
def _sync_opengl_ui(self):
uiutil.set_grid_row_visible(
self.widget("graphics-rendernode"),
self.widget("graphics-opengl").get_active())
def _change_opengl(self, ignore):
self._sync_opengl_ui()
self._sync_ui()
self.emit("changed-opengl")
self.emit("changed-rendernode")
def _change_port_auto(self, ignore):
self.widget("graphics-port-auto").set_inconsistent(False)
self._change_ports()
self._sync_ui()
self.emit("changed-port")
def _change_ports(self):
is_auto = (self.widget("graphics-port-auto").get_active() or
self.widget("graphics-port-auto").get_inconsistent())
self.widget("graphics-port").set_visible(not is_auto)
def _change_password_chk(self, ignore=None):
if self.widget("graphics-password-chk").get_active():
self.widget("graphics-password").set_sensitive(True)
else:
self.widget("graphics-password").set_text("")
self.widget("graphics-password").set_sensitive(False)
def _change_password_cb(self, src):
self._sync_ui()
self.emit("changed-password")
def _show_password_chk(self, ignore=None):
if self.widget("graphics-visiblity-chk").get_active():
self.widget("graphics-password").set_visibility(True)
else:
self.widget("graphics-password").set_visibility(False)
def _show_password_cb(self, src):
self._sync_ui()