2 # -*- coding: utf-8 -*-
6 # Copyright © 2013-2018 Reborn
8 # This file is part of Cnchi.
10 # Cnchi is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
15 # Cnchi is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # The following additional terms are in effect as per Section 7 of the license:
22 # The preservation of all legal notices and author attributions in
23 # the material or in the Appropriate Legal Notices displayed
24 # by works containing it is required.
26 # You should have received a copy of the GNU General Public License
27 # along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
30 """ Shows slides while installing. Also manages installing messages and progress bars """
40 gi.require_version('Gtk', '3.0')
41 from gi.repository import Gtk, GLib, GdkPixbuf
43 import show_message as show
44 import misc.extra as misc
46 from pages.gtkbasebox import GtkBaseBox
48 from logging_utils import ContextFilter
50 # When testing, no _() is available
53 except NameError as err:
57 class Slides(GtkBaseBox):
60 # Check events queue every second
61 MANAGE_EVENTS_TIMER = 1000
63 # Change image slide every half minute
64 SLIDESHOW_TIMER = 35000
66 def __init__(self, params, prev_page=None, next_page=None):
67 """ Initialize class and its vars """
68 super().__init__(self, params, 'slides', prev_page, next_page)
70 self.progress_bar = self.gui.get_object('progress_bar')
71 self.progress_bar.set_show_text(True)
72 self.progress_bar.set_name('i_progressbar')
74 self.downloads_progress_bar = self.gui.get_object('downloads_progress_bar')
75 self.downloads_progress_bar.set_show_text(True)
76 self.downloads_progress_bar.set_name('a_progressbar')
78 self.info_label = self.gui.get_object('info_label')
80 self.fatal_error = False
81 self.should_pulse = False
83 self.revealer = self.gui.get_object('revealer1')
84 self.revealer.connect('notify::child-revealed', self.image_revealed)
86 self.stop_slideshow = False
88 GLib.timeout_add(Slides.MANAGE_EVENTS_TIMER, self.manage_events_from_cb_queue)
90 def translate_ui(self):
91 """ Translates all ui elements """
92 if not self.info_label.get_label():
93 self.info_label.set_markup(_("Please wait..."))
95 self.header.set_subtitle(_("Installing RebornOS..."))
97 def prepare(self, direction):
98 """ Prepare slides screen """
102 # Last screen reached, hide main progress bar (the one at the top).
103 self.main_progressbar.hide()
105 # Also hide total downloads progress bar
106 self.downloads_progress_bar.hide()
108 # Hide backwards and forwards buttons
109 self.backwards_button.hide()
110 self.forward_button.hide()
112 # Hide close button (we've reached the point of no return)
113 self.header.set_show_close_button(False)
115 # Set slide image (and show it)
116 self.reveal_next_slide()
118 def reveal_next_slide(self):
119 """ Loads slide and reveals it """
120 if not self.stop_slideshow:
121 self.slide = ((self.slide + 1) % 3) + 1
122 if 0 < self.slide <= 3:
124 data_dir = self.settings.get('data')
125 path = os.path.join(data_dir, 'images/slides',
126 '{}.png'.format(self.slide))
127 img = self.gui.get_object('slide1')
128 # img.set_from_file(path)
130 pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
131 path, 820, 334, False)
132 # set the content of the image as the pixbuf
133 img.set_from_pixbuf(pixbuf)
135 self.revealer.set_reveal_child(True)
136 except FileNotFoundError:
137 # FIXME: Installation process finishes before we can read these values ?¿
138 logging.warning("Can't get configuration values.")
139 self.stop_slideshow = True
141 def image_revealed(self, revealer, _revealed):
142 """ Called when a image slide is shown
143 revealer: Gtk.Revealer
144 revealed: GParamBoolean """
145 if not self.stop_slideshow:
146 if revealer.get_child_revealed() and not self.stop_slideshow:
147 GLib.timeout_add(Slides.SLIDESHOW_TIMER, self.hide_slide)
149 self.reveal_next_slide()
151 def hide_slide(self):
152 """ Hide image shown in slideshow, this will trigger image_revealed()
153 so the next slide image will be revealed """
154 self.revealer.set_reveal_child(False)
159 """ Nothing to be done here """
162 def stop_pulse(self):
163 """ Stop pulsing progressbar """
164 self.should_pulse = False
165 # self.progress_bar.hide()
166 self.info_label.show_all()
168 def start_pulse(self):
169 """ Start pulsing progressbar """
172 """ Pulse progressbar """
173 if self.should_pulse:
174 self.progress_bar.pulse()
175 return self.should_pulse
177 if not self.should_pulse:
178 # Hide any text that might be in info area
179 self.info_label.set_markup("")
180 self.info_label.hide()
181 # Show progress bar (just in case)
182 self.progress_bar.show_all()
183 self.progress_bar.set_show_text(True)
184 self.should_pulse = True
185 GLib.timeout_add(100, pbar_pulse)
187 def manage_events_from_cb_queue(self):
188 """ We should be quick here and do as less as possible """
193 if self.callback_queue is None:
196 while not self.callback_queue.empty():
198 event = self.callback_queue.get_nowait()
199 except ValueError as queue_error:
200 # Calling get_nowait so many times can issue a ValueError
201 # exception with this error: semaphore or lock released too
202 # many times. Log it anyways to keep an eye on this error
203 logging.error(queue_error)
206 # Queue is empty, just quit.
209 if event[0] == 'percent':
210 self.progress_bar.set_fraction(float(event[1]))
211 elif event[0] == 'downloads_percent':
212 self.downloads_progress_bar.set_fraction(float(event[1]))
213 elif event[0] == 'progress_bar_show_text':
215 self.progress_bar.set_text(event[1])
217 self.progress_bar.set_text("")
218 elif event[0] == 'progress_bar':
219 if event[1] == 'hide':
220 self.progress_bar.hide()
221 elif event[1] == 'show':
222 self.progress_bar.show()
223 elif event[0] == 'downloads_progress_bar':
224 if event[1] == 'hide':
225 self.downloads_progress_bar.hide()
226 elif event[1] == 'show':
227 self.downloads_progress_bar.show()
228 elif event[0] == 'pulse':
229 if event[1] == 'stop':
231 elif event[1] == 'start':
233 elif event[0] == 'finished':
234 logging.info(event[1])
235 self.installation_finished()
236 elif event[0] == 'error':
237 self.callback_queue.task_done()
238 self.install_error(event[1])
239 elif event[0] == 'info':
240 logging.info(event[1])
241 if self.should_pulse:
242 self.progress_bar.set_text(event[1])
244 self.info_label.set_markup(event[1])
245 elif event[0] == 'cache_pkgs_md5_check_failed':
247 'Adding %s to cache_pkgs_md5_check_failed list', event[1])
248 self.settings.set('cache_pkgs_md5_check_failed', event[1])
250 logging.warning("Event %s not recognised. Ignoring.", event[0])
252 self.callback_queue.task_done()
256 def empty_queue(self):
257 """ Empties messages queue """
258 while not self.callback_queue.empty():
260 self.callback_queue.get_nowait()
261 self.callback_queue.task_done()
267 """ Reboots the system, used when installation is finished """
268 with misc.raised_privileges():
272 cmd = ["/usr/bin/systemctl", "reboot", "--force", "--no-wall"]
274 except subprocess.CalledProcessError as error:
277 def check_bootloader(self):
278 """ Check that bootloader has been successfuly installed
279 Shows a dialog to the user in case something has failed """
280 bootloader_install = self.settings.get('bootloader_install')
281 bootloader_install_ok = self.settings.get('bootloader_installation_successful')
283 if bootloader_install and not bootloader_install_ok:
284 # Warn user about GRUB and ask if we should open wiki page.
286 "IMPORTANT: There may have been a problem with the bootloader installation "
287 "which could prevent your system from booting properly. Before rebooting, "
288 "you may want to verify whether or not the bootloader is installed and "
290 "The Arch Linux Wiki contains troubleshooting information:\n"
291 "\thttps://wiki.archlinux.org/index.php/GRUB\n\n"
292 "Would you like to view the wiki page now?")
293 response = show.question(self.get_main_window(), boot_warn)
294 if response == Gtk.ResponseType.YES:
296 misc.drop_privileges()
297 wiki_url = 'https://wiki.archlinux.org/index.php/GRUB'
298 webbrowser.open(wiki_url)
300 def installation_finished(self):
301 """ Installation finished """
302 log_util = ContextFilter()
303 log_util.send_install_result("True")
305 self.stop_slideshow = True
308 "Installation Complete!\n"
309 "Do you want to restart your system now?")
310 response = show.question(self.get_main_window(), install_ok)
313 misc.remove_temp_files(self.settings.get('temp'))
314 except FileNotFoundError:
315 # FIXME: Installation process finishes before we can read these values ?¿
316 logging.warning("Can't get configuration values.")
320 if response == Gtk.ResponseType.YES:
325 def install_error(self, error):
326 """ A fatal error has been issued """
328 self.stop_slideshow = True
330 # Empty the events queue
333 log_util = ContextFilter()
334 log_util.send_install_result("False")
335 if log_util.have_install_id:
336 # Add install id to error message
337 # (we can lookup logs on bugsnag by the install id)
339 'Please reference the following number when reporting this error: ')
340 error_message = '{0}\n{1}{2}'.format(
341 error, tpl, log_util.install_id)
343 error_message = error
345 show.fatal_error(self.get_main_window(), error_message)