Find and fix glade issues
by David Shea
Having a comprehensive glade file check probably isn't worth our time, since
it'd be a moving target and the XML schema is mostly undocumented and glade is
supposed to check for most problems anyway, but I figured we could have a check
for the things we actually mess up.
I remember an issue a while ago where a file had duplicate IDs, and we have an
issue where mnemonic_widget properties reference objects that don't exist, so
check for those things. Feel free to add your own!
10 years, 4 months
[blivet] Use blivet's Size class for all size quantities
by David Lehman
This has been a long time coming. It resolves a whole array of issues
related to conversion between various units. Under the covers, using
Size means we are storing sizes as Decimals representing the number of
bytes. It also switches the size module's default from decimal to binary
since binary is what is actually used at the lower levels (sector size,
lvm extent size, md chunk size, &c). Using decimal makes it impossible
for things to have round-numbered sizes unless you intentionally ignore
or discard some precision, both of which have serious consequences.
This patch set also sigificantly reduces our usage of float. The
function blivet.devicelibs.lvm.clampSize was rewritten to not use any
float math.
10 years, 4 months
[blive:rhel7/master 0/3] Supplement storage log ids (take 2)
by Anne Mulhern
A couple of classes already define the kind of id that ObjectID implements,
but they call it id.
So, I call it id like they do and I use ObjectID class for them.
Otherwise, much the same as the last version.
mulhern (3):
Make a class that creates a unique-per-class id for objects (#1043763)
Refactor to use ObjectID class (#1043763)
Give DeviceFormat objects an id (#1043763)
blivet/deviceaction.py | 11 +++--------
blivet/devices.py | 10 ++--------
blivet/formats/__init__.py | 11 +++++++----
blivet/util.py | 18 ++++++++++++++++++
tests/formats_test/init_test.py | 11 +++++++++++
5 files changed, 41 insertions(+), 20 deletions(-)
--
1.8.3.1
10 years, 4 months
[master/rhel7-branch] Additional completion checks in network spoke. (#1044571)
by Samantha N. Bueno
If our install source is local media, we should not require a
network connection to be configured.
Resolves: rhbz#1044571
---
pyanaconda/ui/gui/spokes/network.py | 3 ++-
pyanaconda/ui/tui/spokes/network.py | 7 ++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py
index 1dbec82..bdfdb93 100644
--- a/pyanaconda/ui/gui/spokes/network.py
+++ b/pyanaconda/ui/gui/spokes/network.py
@@ -1318,8 +1318,9 @@ class NetworkSpoke(FirstbootSpokeMixIn, NormalSpoke):
@property
def completed(self):
# TODO: check also if source requires updates when implemented
+ localinst = bool(self.data.method.method == "cdrom")
return (not flags.can_touch_runtime_system("require network connection")
- or nm.nm_activated_devices())
+ or nm.nm_activated_devices() or localinst)
@property
def mandatory(self):
diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py
index a599a40..b8b8d13 100644
--- a/pyanaconda/ui/tui/spokes/network.py
+++ b/pyanaconda/ui/tui/spokes/network.py
@@ -64,8 +64,13 @@ class NetworkSpoke(EditTUISpoke):
@property
def completed(self):
+ """ Check whether this spoke is complete or not. Do an additional
+ check if we're installing from CD/DVD, since a network connection
+ should not be required in this case.
+ """
+ localinst = bool(self.data.method.method == "cdrom")
return (not can_touch_runtime_system("require network connection")
- or nm_activated_devices())
+ or nm_activated_devices() or localinst)
@property
def status(self):
--
1.8.3.1
10 years, 4 months
[blivet:rhel7/master 0/2] Supplement storage log ids
by Anne Mulhern
These two both address a problem where the ids recorded in the storage log
may not turn out to be so useful for debugging.
While it does not resolve the issue I expect it will make subsequent similar
issues easier to address.
mulhern (2):
Make a class that creates a unique-per-class id for objects (#1043763)
Give DeviceFormat objects an object_id (#1043763)
blivet/formats/__init__.py | 11 +++++++----
blivet/util.py | 18 ++++++++++++++++++
tests/formats_test/init_test.py | 5 +++++
3 files changed, 30 insertions(+), 4 deletions(-)
--
1.8.3.1
10 years, 4 months
[PATCH] Use the new Gtk.ListBox for displaying environments and addons (#1039683).
by Chris Lumens
This clears up the problems of funny spacing in the views by getting rid of
the stores and views, which were causing the problems. Instead I'm just using
a ListBox of boxes, each of which contains a button and a label describing the
environment or addon.
Note that for now, this reverts proper keyboard scrolling behavior.
---
pyanaconda/ui/gui/spokes/software.glade | 125 +++----------------
pyanaconda/ui/gui/spokes/software.py | 208 +++++++++++++++++---------------
2 files changed, 133 insertions(+), 200 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/software.glade b/pyanaconda/ui/gui/spokes/software.glade
index 3ea3fbb..e5b22d7 100644
--- a/pyanaconda/ui/gui/spokes/software.glade
+++ b/pyanaconda/ui/gui/spokes/software.glade
@@ -1,34 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.16.0 on Thu Oct 17 10:58:51 2013 -->
+<!-- Generated with glade 3.16.0 on Fri Jan 3 13:29:45 2014 -->
<interface>
- <!-- interface-requires gtk+ 3.0 -->
+ <!-- interface-requires gtk+ 3.10 -->
<!-- interface-requires AnacondaWidgets 1.0 -->
- <object class="GtkListStore" id="addonStore">
- <columns>
- <!-- column-name addonSelected -->
- <column type="gboolean"/>
- <!-- column-name addonMarkup -->
- <column type="gchararray"/>
- <!-- column-name addonName -->
- <column type="gchararray"/>
- <!-- column-name addonSeparator -->
- <column type="gboolean"/>
- </columns>
- </object>
- <object class="GtkListStore" id="environmentStore">
- <columns>
- <!-- column-name envSelected -->
- <column type="gboolean"/>
- <!-- column-name envMarkup -->
- <column type="gchararray"/>
- <!-- column-name envName -->
- <column type="gchararray"/>
- </columns>
- </object>
<object class="AnacondaSpokeWindow" id="softwareWindow">
+ <property name="startup_id">filler</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
+ <property name="startup_id">filler</property>
<property name="window_name" translatable="yes">SOFTWARE SELECTION</property>
<signal name="button-clicked" handler="on_back_clicked" swapped="no"/>
<signal name="info-bar-clicked" handler="on_info_bar_clicked" swapped="no"/>
@@ -127,49 +107,15 @@
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
- <object class="GtkTreeView" id="addonView">
+ <object class="GtkViewport" id="addonViewport">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="border_width">12</property>
- <property name="model">addonStore</property>
- <property name="headers_visible">False</property>
- <property name="headers_clickable">False</property>
- <property name="search_column">0</property>
- <signal name="button-release-event" handler="on_addon_view_clicked" swapped="no"/>
- <signal name="key-release-event" handler="on_addon_view_clicked" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="addonSelector"/>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="addonSelectedCol">
- <signal name="clicked" handler="on_addon_toggled" swapped="no"/>
- <child>
- <object class="GtkCellRendererToggle" id="addonSelectedRenderer">
- <property name="yalign">0</property>
- <signal name="toggled" handler="on_addon_toggled" swapped="no"/>
- </object>
- <attributes>
- <attribute name="active">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkTreeViewColumn" id="addonDescCol">
- <property name="expand">True</property>
- <child>
- <object class="GtkCellRendererText" id="addonDescRenderer">
- <property name="width">250</property>
- <property name="yalign">0</property>
- <property name="wrap_mode">word-char</property>
- <property name="wrap_width">200</property>
- </object>
- <attributes>
- <attribute name="markup">1</attribute>
- </attributes>
- </child>
+ <object class="GtkListBox" id="addonListBox">
+ <property name="width_request">250</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <signal name="row-activated" handler="on_addon_activated" swapped="no"/>
</object>
</child>
</object>
@@ -186,54 +132,21 @@
<object class="GtkScrolledWindow" id="environmentScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="events">GDK_STRUCTURE_MASK | GDK_SCROLL_MASK</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
- <object class="GtkTreeView" id="environmentView">
+ <object class="GtkViewport" id="environmentViewport">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="border_width">12</property>
- <property name="model">environmentStore</property>
- <property name="headers_visible">False</property>
- <property name="headers_clickable">False</property>
- <property name="search_column">0</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="environmentSelector">
- <signal name="changed" handler="on_environment_selection_changed" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="environmentSelectedCol">
- <child>
- <object class="GtkCellRendererToggle" id="environmentSelectedRenderer">
- <property name="yalign">0</property>
- <property name="radio">True</property>
- <signal name="toggled" handler="on_environment_toggled" swapped="no"/>
- </object>
- <attributes>
- <attribute name="active">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkTreeViewColumn" id="environmentDescCol">
- <property name="expand">True</property>
- <child>
- <object class="GtkCellRendererText" id="environmentDescRenderer">
- <property name="width">250</property>
- <property name="yalign">0</property>
- <property name="wrap_mode">word-char</property>
- <property name="wrap_width">200</property>
- </object>
- <attributes>
- <attribute name="markup">1</attribute>
- </attributes>
- </child>
+ <object class="GtkListBox" id="environmentListBox">
+ <property name="width_request">250</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <signal name="row-activated" handler="on_environment_activated" swapped="no"/>
</object>
</child>
</object>
diff --git a/pyanaconda/ui/gui/spokes/software.py b/pyanaconda/ui/gui/spokes/software.py
index 4fbd16c..021187a 100644
--- a/pyanaconda/ui/gui/spokes/software.py
+++ b/pyanaconda/ui/gui/spokes/software.py
@@ -19,7 +19,7 @@
# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
#
-from gi.repository import Gdk
+from gi.repository import Gtk, Pango
from pyanaconda.flags import flags
from pyanaconda.i18n import _, C_, CN_
@@ -65,8 +65,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
self.excludedGroups = []
self.environment = None
- self._addonStore = self.builder.get_object("addonStore")
- self._environmentStore = self.builder.get_object("environmentStore")
+ self._environmentListBox = self.builder.get_object("environmentListBox")
+ self._addonListBox = self.builder.get_object("addonListBox")
# Used to determine which add-ons to display for each environment.
# The dictionary keys are environment IDs. The dictionary values are two-tuples
@@ -84,12 +84,9 @@ class SoftwareSelectionSpoke(NormalSpoke):
self._origAddons = []
self._origEnvironment = None
- # We need to tell the addon view whether something is a separator or not.
- self.builder.get_object("addonView").set_row_separator_func(self._addon_row_is_separator, None)
-
def _apply(self):
- row = self._get_selected_environment()
- if not row:
+ env = self._get_selected_environment()
+ if not env:
return
addons = self._get_selected_addons()
@@ -99,8 +96,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
self._selectFlag = False
self.payload.data.packages.groupList = []
- self.payload.selectEnvironment(row[2])
- self.environment = row[2]
+ self.payload.selectEnvironment(env)
+ self.environment = env
for group in self.selectedGroups:
self.payload.selectGroup(group)
@@ -145,14 +142,14 @@ class SoftwareSelectionSpoke(NormalSpoke):
@property
def changed(self):
- row = self._get_selected_environment()
- if not row:
+ env = self._get_selected_environment()
+ if not env:
return True
addons = self._get_selected_addons()
# Don't redo dep solving if nothing's changed.
- if row[2] == self._origEnvironment and set(addons) == set(self._origAddons) and \
+ if env == self._origEnvironment and set(addons) == set(self._origAddons) and \
self.txid_valid:
return False
@@ -189,8 +186,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
if not self.txid_valid:
return _("Source changed - please verify")
- row = self._get_selected_environment()
- if not row:
+ env = self._get_selected_environment()
+ if not env:
# Kickstart installs with %packages will have a row selected, unless
# they did an install without a desktop environment. This should
# catch that one case.
@@ -199,7 +196,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
return _("Nothing selected")
- return self.payload.environmentDescription(row[2])[0]
+ return self.payload.environmentDescription(env)[0]
def initialize(self):
NormalSpoke.initialize(self)
@@ -279,40 +276,58 @@ class SoftwareSelectionSpoke(NormalSpoke):
hubQ.send_message(self.__class__.__name__, _("No installation source available"))
return False
+ def _add_row(self, listbox, name, desc, button):
+ row = Gtk.ListBoxRow()
+ box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+ box.set_spacing(6)
+
+ button.set_valign(Gtk.Align.START)
+ button.connect("clicked", self.on_button_toggled, row)
+ box.add(button)
+
+ label = Gtk.Label()
+ label.set_line_wrap(True)
+ label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
+ label.set_markup("<b>%s</b>\n%s" % (escape_markup(name), escape_markup(desc)))
+ label.set_hexpand(True)
+ label.set_alignment(0, 0.5)
+ box.add(label)
+
+ row.add(box)
+ listbox.insert(row, -1)
+
def refresh(self):
NormalSpoke.refresh(self)
threadMgr.wait(constants.THREAD_PAYLOAD_MD)
- self._environmentStore.clear()
if self.environment not in self.payload.environments:
self.environment = None
firstEnvironment = True
+ firstRadio = None
+
+ self._clear_listbox(self._environmentListBox)
+
for environment in self.payload.environments:
(name, desc) = self.payload.environmentDescription(environment)
- itr = self._environmentStore.append([environment == self.environment, "<b>%s</b>\n%s" % \
- (escape_markup(name), escape_markup(desc)), environment])
- # Either:
- # (1) Select the environment given by kickstart or selected last
- # time this spoke was displayed; or
- # (2) Select the first environment given by display order as the
- # default if nothing is selected.
- if (environment == self.environment) or \
- (not self.environment and firstEnvironment):
+ radio = Gtk.RadioButton(group=firstRadio)
+
+ active = environment == self.environment or \
+ not self.environment and firstEnvironment
+ radio.set_active(active)
+ if active:
self.environment = environment
- sel = self.builder.get_object("environmentSelector")
- sel.select_iter(itr)
+
+ self._add_row(self._environmentListBox, name, desc, radio)
+ if not firstRadio:
+ firstRadio = radio
firstEnvironment = False
self.refreshAddons()
- def _addon_row_is_separator(self, model, itr, *args):
- # The last column of the model tells us if this row is a separator or not.
- return model[itr][3]
-
def _addAddon(self, grp):
(name, desc) = self.payload.groupDescription(grp)
@@ -326,17 +341,18 @@ class SoftwareSelectionSpoke(NormalSpoke):
else:
selected = self.payload.environmentOptionIsDefault(self.environment, grp)
- self._addonStore.append([selected, "<b>%s</b>\n%s" % \
- (escape_markup(name), escape_markup(desc)), grp, False])
+ check = Gtk.CheckButton()
+ check.set_active(selected)
+ self._add_row(self._addonListBox, name, desc, check)
def refreshAddons(self):
- self._addonStore.clear()
-
# The source was changed, make sure the list is current
if not self.txid_valid:
self._parseEnvironments()
if self.environment and (self.environment in self._environmentAddons):
+ self._clear_listbox(self._addonListBox)
+
# We have two lists: One of addons specific to this environment,
# and one of all the others. The environment-specific ones will be displayed
# first and then a separator, and then the generic ones. This is to make it
@@ -356,7 +372,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
# This marks a separator in the view - only add it if there's both environment
# specific and generic addons.
if addSep:
- self._addonStore.append([False, "", "", True])
+ self._addonListBox.insert(Gtk.Separator(), -1)
for grp in self._environmentAddons[self.environment][1]:
self._addAddon(grp)
@@ -368,95 +384,99 @@ class SoftwareSelectionSpoke(NormalSpoke):
else:
self.clear_info()
+ def _allAddons(self):
+ return self._environmentAddons[self.environment][0] + \
+ [""] + \
+ self._environmentAddons[self.environment][1]
+
def _get_selected_addons(self):
- return [row[2] for row in self._addonStore if row[0]]
+ retval = []
+
+ addons = self._allAddons()
+
+ for (ndx, row) in enumerate(self._addonListBox.get_children()):
+ box = row.get_children()[0]
+
+ if isinstance(box, Gtk.Separator):
+ continue
+
+ button = box.get_children()[0]
+ if button.get_active():
+ retval.append(addons[ndx])
+
+ return retval
# Returns the row in the store corresponding to what's selected on the
# left hand panel, or None if nothing's selected.
def _get_selected_environment(self):
- environmentView = self.builder.get_object("environmentView")
- itr = environmentView.get_selection().get_selected()[1]
- if not itr:
- return None
+ for (ndx, row) in enumerate(self._environmentListBox.get_children()):
+ box = row.get_children()[0]
+ button = box.get_children()[0]
+ if button.get_active():
+ return self.payload.environments[ndx]
- return self._environmentStore[itr]
+ return None
+
+ def _clear_listbox(self, listbox):
+ for child in listbox.get_children():
+ listbox.remove(child)
+ del(child)
@property
def txid_valid(self):
return self._tx_id == self.payload.txID
# Signal handlers
- def on_environment_toggled(self, renderer, path):
+ def on_button_toggled(self, radio, row):
+ row.activate()
+
+ def on_environment_activated(self, listbox, row):
if not self._selectFlag:
return
- # First, mark every row as unselected so the radio button on whatever
- # row was previously selected will be cleared out.
- for row in self._environmentStore:
- row[0] = False
+ box = row.get_children()[0]
+ button = box.get_children()[0]
- # Then, remove all the groups that were selected by the previously
+ button.handler_block_by_func(self.on_button_toggled)
+ button.set_active(not button.get_active())
+ button.handler_unblock_by_func(self.on_button_toggled)
+
+ # Remove all the groups that were selected by the previously
# selected environment.
for groupid in self.payload.environmentGroups(self.environment):
if groupid in self.selectedGroups:
self.selectedGroups.remove(groupid)
# Then mark the clicked environment as selected and update the screen.
- self._environmentStore[path][0] = True
- self.environment = self._environmentStore[path][2]
+ self.environment = self.payload.environments[row.get_index()]
self.refreshAddons()
+ self._addonListBox.show_all()
- def on_environment_selection_changed(self, selection):
- (model, itr) = selection.get_selected()
- if not itr:
+ def on_addon_activated(self, listbox, row):
+ box = row.get_children()[0]
+ if isinstance(box, Gtk.Separator):
return
- # Only do something if the row's not previously been selected.
- if not model[itr][0]:
- self.on_environment_toggled(None, model.get_path(itr))
-
- def on_addon_toggled(self, renderer, path):
- selected = not self._addonStore[path][0]
- group = self._addonStore[path][2]
- self._addonStore[path][0] = selected
- if selected:
- if group not in self.selectedGroups:
- self.selectedGroups.append(group)
-
- if group in self.excludedGroups:
- self.excludedGroups.remove(group)
+ button = box.get_children()[0]
+ addons = self._allAddons()
+ group = addons[row.get_index()]
- self._addonStates[group] = self._ADDON_SELECTED
+ wasActive = group in self.selectedGroups
- else:
- if group in self.selectedGroups:
- self.selectedGroups.remove(group)
+ button.handler_block_by_func(self.on_button_toggled)
+ button.set_active(not wasActive)
+ button.handler_unblock_by_func(self.on_button_toggled)
+ if wasActive:
+ self.selectedGroups.remove(group)
self._addonStates[group] = self._ADDON_DESELECTED
+ else:
+ self.selectedGroups.append(group)
- def on_addon_view_clicked(self, view, event, *args):
- if event and not event.type in [Gdk.EventType.BUTTON_RELEASE, Gdk.EventType.KEY_RELEASE]:
- return
-
- if event and event.type == Gdk.EventType.KEY_RELEASE and \
- event.keyval not in [Gdk.KEY_space, Gdk.KEY_Return, Gdk.KEY_ISO_Enter, Gdk.KEY_KP_Enter, Gdk.KEY_KP_Space]:
- return
-
- selection = view.get_selection()
- (model, itr) = selection.get_selected()
- if not itr:
- return
-
- # If the user clicked on the first column, they've clicked on the checkbox which was
- # handled separately from this signal handler. Handling it again here will result in
- # the checkbox being toggled yet again. So, we need to return in that case.
- col = view.get_cursor()[1]
- first_col = view.get_column(0)
- if col == first_col:
- return
+ if group in self.excludedGroups:
+ self.excludedGroups.remove(group)
- # Always do something here, since addons can be toggled.
- self.on_addon_toggled(None, model.get_path(itr))
+ self._addonStates[group] = self._ADDON_SELECTED
def on_info_bar_clicked(self, *args):
if not self._errorMsgs:
--
1.8.3.1
10 years, 4 months
[rhel7-branch] User name validity checking
by Vratislav Podzimek
This is a partial port of user name validity checking we have on the master
branch.
Vratislav Podzimek (1):
Do not allow 'root' as a user name (#1032671)
pyanaconda/ui/gui/spokes/user.py | 11 +++++++++--
pyanaconda/ui/tui/spokes/user.py | 4 ++--
pyanaconda/users.py | 5 +++++
3 files changed, 16 insertions(+), 4 deletions(-)
--
1.8.4.2
10 years, 4 months
[PATCH] Remove scrot dependency for global screenshot support
by Martin Kolman
Looks like you indeed can make full-screen screenshots
with GTK/GDK only!
Thanks goes to Ray Strode (halfline) for the hint! :)
Signed-off-by: Martin Kolman <mkolman(a)redhat.com>
---
anaconda.spec.in | 1 -
pyanaconda/ui/gui/__init__.py | 15 ++++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/anaconda.spec.in b/anaconda.spec.in
index f8576bc..cb648e9 100755
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -166,7 +166,6 @@ Requires: nm-connection-editor
%ifarch %livearches
Requires: zenity
%endif
-Requires: scrot
Requires: keybinder3
%description gui
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index e2e0cfc..a28bc03 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -25,7 +25,6 @@ from gi.repository import Gdk, Gtk, AnacondaWidgets, Keybinder
from pyanaconda.i18n import _
from pyanaconda import product
-from pyanaconda import iutil
from pyanaconda.ui import UserInterface, common
from pyanaconda.ui.gui.utils import enlightbox, gtk_action_wait, busyCursor, unbusyCursor
@@ -243,15 +242,17 @@ class GUIObject(common.UIObject):
fn = os.path.join(self.screenshots_directory,
"screenshot-%04d.png" % _screenshotIndex)
- rc = iutil.execWithRedirect("scrot", [fn])
- if rc == 0:
- log.info("screenshot nr. %d taken", _screenshotIndex)
- _screenshotIndex += 1
- else:
- log.error("taking screenshot failed")
+ root_window = Gdk.get_default_root_window()
+ pixbuf = Gdk.pixbuf_get_from_window(root_window, 0, 0,
+ root_window.get_width(),
+ root_window.get_height())
+ pixbuf.savev(fn, 'png', [], [])
+ log.info("screenshot nr. %d taken" % _screenshotIndex)
+ _screenshotIndex += 1
# start counting from the time the screenshot operation is done
_last_screenshot_timestamp = time.time()
+
@property
def window(self):
"""Return the top-level object out of the GtkBuilder representation
--
1.8.4.2
10 years, 4 months
[anaconda][PATCH] Allow hubs and standalone spokes decide if it is possible to continue
by Vratislav Podzimek
Related: rhbz#965797
Signed-off-by: Vratislav Podzimek <vpodzime(a)redhat.com>
---
pyanaconda/ui/gui/hubs/__init__.py | 29 +++++++++++++++++++++++++++--
pyanaconda/ui/gui/spokes/__init__.py | 18 ++++++++++++++++--
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index 37daf47..bb473b6 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -354,11 +354,36 @@ class Hub(GUIObject, common.Hub):
self._update_spoke_id = GLib.timeout_add_seconds(1, self._update_spokes)
- ### SIGNAL HANDLERS
+ @property
+ def continue_possible(self):
+ """
+ Indicates whether it is possible to continue to the next action (hub,
+ standalone spoke,...). When overridden, IT IS THE HUB'S RESPONSIBILITY
+ to inform user, where the problem is and how to fix it.
+
+ """
+
+ return True
+
+ ### SIGNAL HANDLING
+ def _checked_continue(self, cb):
+ """
+ Checks if continue is possible (from the hub's perspective and if yes,
+ runs the callback.
+
+ """
+
+ if self.continue_possible:
+ cb()
+
+ def _register_continue_event_cb(self, cb):
+ """Registers the callback for the continue event adding the check."""
+
+ self.continueButton.connect("clicked", lambda *args: self._checked_continue(cb))
def register_event_cb(self, event, cb):
if event == "continue" and hasattr(self, "continueButton"):
- self.continueButton.connect("clicked", lambda *args: cb())
+ self._register_continue_event_cb(cb)
elif event == "quit" and hasattr(self, "quitButton"):
self.quitButton.connect("clicked", lambda *args: cb())
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index c1a35b3..bb85e1e 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -63,9 +63,23 @@ class StandaloneSpoke(Spoke, common.StandaloneSpoke):
Spoke.__init__(self, data)
common.StandaloneSpoke.__init__(self, data, storage, payload, instclass)
+ @property
+ def continue_possible(self):
+ """
+ Indicates whether it is possible to continue to the next action (hub,
+ standalone spoke,...). When overridden, IT IS THE SPOKES'S
+ RESPONSIBILITY to inform user, where the problem is and how to fix it.
+
+ """
+
+ return True
+
+
+ ### SIGNAL HANDLING
def _on_continue_clicked(self, cb):
- self.apply()
- cb()
+ if self.continue_possible:
+ self.apply()
+ cb()
def register_event_cb(self, event, cb):
if event == "continue":
--
1.7.11.7
10 years, 4 months
[PATCH] Add a sidebar to the hub windows (#1045250)
by Máirín Duffy
This adds a sidebar to the left of the hub windows. The reason for
this is to visually reinforce the left-to-right linear nature of the
hub windows. There is a variable (sidebar_width_percentage) you can
set to control the percentage width of the screen that the sidebar
occupies - I've set it to 18% which seems reasonable. It auto-centers
the logo on top of the sidebar as well. There are three layers to
the sidebar: the base color, a texture pixbuf, and the logo pixbuf
on top.
---
widgets/src/HubWindow.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 70 insertions(+), 2 deletions(-)
diff --git a/widgets/src/HubWindow.c b/widgets/src/HubWindow.c
index 7f118dd..afba8ff 100644
--- a/widgets/src/HubWindow.c
+++ b/widgets/src/HubWindow.c
@@ -82,9 +82,75 @@ static void anaconda_hub_window_buildable_init(GtkBuildableIface *iface);
G_DEFINE_TYPE_WITH_CODE(AnacondaHubWindow, anaconda_hub_window, ANACONDA_TYPE_BASE_WINDOW,
G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE, anaconda_hub_window_buildable_init))
+static int get_sidebar_width(GtkWidget *window) {
+ GtkAllocation allocation;
+ /* change value below to make sidebar bigger / smaller */
+ float sidebar_width_percentage = .18;
+ gtk_widget_get_allocation(window, &allocation);
+ return allocation.width * sidebar_width_percentage;
+}
+
+static int get_sidebar_height(GtkWidget *window) {
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(window, &allocation);
+ return allocation.height;
+}
+
+/* function to override default drawing to insert sidebar image */
+gboolean anaconda_hub_window_on_draw(GtkWidget *win, cairo_t *cr) {
+
+ /* Create sidebar */
+ GTK_WIDGET_CLASS(anaconda_hub_window_parent_class)->draw(win,cr);
+ cairo_rectangle(cr, 0, 0, get_sidebar_width(win), get_sidebar_height(win));
+
+ /* Configure sidebar base color */
+ /* Dark grey for RHEL: 65/255.0, 65/255.0, 62/255.0, 1 */
+ /* Blue for Fedora: 60/255.0, 110/255.0, 180/255.0, 1 */
+ cairo_set_source_rgba(cr, 65/255.0, 65/255.0, 62/255.0, 1);
+ cairo_fill_preserve(cr);
+
+ /* Configure sidebar texture image */
+ GdkPixbuf *pixbuf_background = gdk_pixbuf_new_from_file("/usr/share/anaconda/pixmaps/noise-texture.png", NULL);
+ cairo_surface_t *surface= gdk_cairo_surface_create_from_pixbuf(pixbuf_background, 0, gtk_widget_get_window(GTK_WIDGET(win)));
+ cairo_set_source_surface(cr, surface, 0, 0);
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
+ cairo_fill(cr);
+
+ /* Configure logo image overlaid on sidebar */
+ GdkPixbuf *pixbuf_logo = gdk_pixbuf_new_from_file("/usr/share/anaconda/pixmaps/redhat-logo.png", NULL);
+ if (pixbuf_logo != NULL) {
+ double x_value = (get_sidebar_width(win) - gdk_pixbuf_get_width(pixbuf_logo))/2;
+ double y_value = 20;
+ cairo_surface_t *surface= gdk_cairo_surface_create_from_pixbuf(pixbuf_logo, 0, gtk_widget_get_window(GTK_WIDGET(win)));
+ cairo_set_source_surface(cr, surface, x_value, y_value);
+ cairo_rectangle(cr, x_value, y_value, gdk_pixbuf_get_width(pixbuf_logo), gdk_pixbuf_get_height(pixbuf_logo));
+ cairo_fill(cr);
+ }
+ return TRUE; /* TRUE to avoid default draw handler */
+}
+
+/* Move base window content appropriate amount of space to the right to make room for sidebar */
+static void anaconda_hub_window_size_allocate (GtkWidget *window, GtkAllocation *allocation) {
+ GtkAllocation child_allocation;
+ GtkWidget *child;
+
+ gtk_widget_set_allocation(window, allocation);
+ int sidebar_width = get_sidebar_width(window);
+ child_allocation.x = allocation->x+sidebar_width;
+ child_allocation.y = allocation->y;
+ child_allocation.width = allocation->width-sidebar_width;
+ child_allocation.height = allocation->height;
+
+ child = gtk_bin_get_child (GTK_BIN (window));
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_size_allocate (child, &child_allocation);
+}
+
static void anaconda_hub_window_class_init(AnacondaHubWindowClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
-
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ widget_class->draw=anaconda_hub_window_on_draw;
+ widget_class->size_allocate=anaconda_hub_window_size_allocate;
g_type_class_add_private(object_class, sizeof(AnacondaHubWindowPrivate));
}
@@ -100,13 +166,15 @@ GtkWidget *anaconda_hub_window_new() {
return g_object_new(ANACONDA_TYPE_HUB_WINDOW, NULL);
}
+
+
static void anaconda_hub_window_init(AnacondaHubWindow *win) {
GtkWidget *action_area = anaconda_base_window_get_action_area(ANACONDA_BASE_WINDOW(win));
win->priv = G_TYPE_INSTANCE_GET_PRIVATE(win,
ANACONDA_TYPE_HUB_WINDOW,
AnacondaHubWindowPrivate);
-
+
win->priv->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win->priv->scrolled_window),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
--
1.8.4.2
10 years, 4 months