def second_scroll(self): self._clear() ticks = Gtk.Image.new_from_file(self._stage.media_path('ticks.png')) copy = [ "Complete all of the ticks in a", "quest to unlock rewards." ] text_widgets = self._get_text_widgets(copy) chest = Gtk.Image.new_from_file(self._stage.media_path('chest-open.png')) button = KanoButton('NEXT', color='orange') button.connect('clicked', cb_wrapper, self.third_scroll) ticks.set_margin_top(20) self._vbox.pack_start(ticks, False, False, 0) text_widgets[0].set_margin_top(40) for w in text_widgets: self._vbox.pack_start(w, False, False, 0) chest.set_margin_top(35) self._vbox.pack_start(chest, False, False, 0) button.set_margin_top(40) button.set_margin_left(80) button.set_margin_right(80) self._vbox.pack_start(button, False, False, 0) self.show_all()
def third_scroll(self): self._clear() heading = Gtk.Label('New quest') add_class(heading, 'scroll-heading') world = Gtk.Image.new_from_file(self._stage.media_path('world.png')) copy = [ "Journey to Kano World", ] text_widgets = self._get_text_widgets(copy) button = KanoButton('OK', color='orange') button.connect('clicked', cb_wrapper, self._stage._ctl.next_stage) heading.set_margin_top(35) self._vbox.pack_start(heading, False, False, 0) world.set_margin_top(35) self._vbox.pack_start(world, False, False, 0) text_widgets[0].set_margin_top(40) for w in text_widgets: self._vbox.pack_start(w, False, False, 0) button.set_margin_top(40) button.set_margin_left(80) button.set_margin_right(80) self._vbox.pack_start(button, False, False, 0) self.show_all()
def first_scroll(self): self._clear() copy = [ "Quests are a series of tasks that you", "can complete on your Kano to get", "great rewards." ] text_widgets = self._get_text_widgets(copy) img_path = self._stage.media_path('chest-closed.png') chest = Gtk.Image.new_from_file(img_path) button = KanoButton('NEXT', color='orange') button.connect('clicked', cb_wrapper, self.second_scroll) text_widgets[0].set_margin_top(30) for w in text_widgets: self._vbox.pack_start(w, False, False, 0) chest.set_margin_top(60) self._vbox.pack_start(chest, False, False, 0) button.set_margin_top(70) button.set_margin_left(80) button.set_margin_right(80) self._vbox.pack_start(button, False, False, 0) self.show_all()
def __generate_buttons(self): self.buttons = [] kano_button_box = Gtk.Box() for button in self.button_info: for argument, value in button_defaults.iteritems(): # Use default info if not provided if argument not in button: button[argument] = value # Create default return values for OK and CANCEL buttons if argument == "return_value": if hasattr(button, "label"): if button["label"] == _("OK"): button["return_value"] = 0 elif button["label"] == _("CANCEL"): button["return_value"] = 1 if argument == "color": if button["label"] == _("CANCEL"): button["color"] = "red" color = button["color"] return_value = button["return_value"] button_name = button["label"] button = KanoButton(button_name) button.set_color(color) button.connect("button-release-event", self.exit_dialog, return_value) button.connect("key-release-event", self.exit_dialog, return_value) self.buttons.append(button) kano_button_box.pack_start(button, False, False, 6) return kano_button_box
def slide1(self): fixed = Gtk.Fixed() pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self.media_path("apps-slide.png"), 1000, 678 ) image = Gtk.Image.new_from_pixbuf(pixbuf) title1 = Gtk.Label("Get Started") title1.get_style_context().add_class("title") title2 = Gtk.Label("Apps") title2.get_style_context().add_class("title") desc = Gtk.Label( "Create and play with\n" + "Kano's Apps to make and save your\n" + "own code masterpieces, from games\n" + "and minecraft world, to art and music pieces..." ) desc.get_style_context().add_class("description") next_button = KanoButton("NEXT") next_button.connect("clicked", self.slide2_wrapper) fixed.put(image, 0, 0) fixed.put(title1, 460, 40) fixed.put(title2, 60, 400) fixed.put(desc, 60, 450) fixed.put(next_button, 460, 580) self.add(fixed) self.show_all()
def first_scroll(self): self._clear() copy = [ "Quests are a series of tasks that you", "can complete on your Kano to get", "great rewards." ] text_widgets = self._get_text_widgets(copy) img_path = self._stage.media_path('chest-closed.png') chest = Gtk.Image.new_from_file(img_path) button = KanoButton('NEXT', color='orange') button.connect('clicked', cb_wrapper, self.second_scroll) text_widgets[0].set_margin_top(30) for w in text_widgets: self._vbox.pack_start(w, False, False, 0) chest.set_margin_top(60) self._vbox.pack_start(chest, False, False, 0) button.set_margin_top(70) button.set_margin_left(80) button.set_margin_right(80) self._vbox.pack_start(button, False, False, 0) self.show_all()
def __init__(self, stage, overscan_ctl, next_cb): super(Window2, self).__init__() add_class(self, 'overscan-window') head_img = Gtk.Image.new_from_file(stage.media_path('hint2.png')) box = Gtk.VBox() self.add(box) box.pack_start(head_img, False, False, 0) heading = Gtk.Label('Use UP and DOWN keys') heading.set_margin_top(25) heading.set_margin_bottom(25) add_class(heading, 'notebook-heading') box.pack_start(heading, False, False, 0) label_copy = 'Stretch or shrink your screen, until the white lines' text1 = Gtk.Label(label_copy) text1.set_margin_bottom(5) add_class(text1, 'notebook-text') box.pack_start(text1, False, False, 0) text2 = Gtk.Label('are lined up with the edges') text2.set_margin_bottom(25) add_class(text2, 'notebook-text') box.pack_start(text2, False, False, 0) buttons = Gtk.HBox(halign=Gtk.Align.CENTER) buttons.set_margin_bottom(25) done = KanoButton('DONE', color='green') buttons.pack_start(done, False, False, 0) done.connect('clicked', cb_wrapper, next_cb) box.pack_start(buttons, False, False, 0)
def second_scroll(self): self._clear() ticks = Gtk.Image.new_from_file(self._stage.media_path('ticks.png')) copy = ["Complete all of the ticks in a", "quest to unlock rewards."] text_widgets = self._get_text_widgets(copy) chest = Gtk.Image.new_from_file( self._stage.media_path('chest-open.png')) button = KanoButton('NEXT', color='orange') button.connect('clicked', cb_wrapper, self.third_scroll) ticks.set_margin_top(20) self._vbox.pack_start(ticks, False, False, 0) text_widgets[0].set_margin_top(40) for w in text_widgets: self._vbox.pack_start(w, False, False, 0) chest.set_margin_top(35) self._vbox.pack_start(chest, False, False, 0) button.set_margin_top(40) button.set_margin_left(80) button.set_margin_right(80) self._vbox.pack_start(button, False, False, 0) self.show_all()
def __init__(self, win, char_creator): Gtk.EventBox.__init__(self) self._win = win # Should this be inherited, passed as a variable, or global? # Could be a member variable in window. # self.char_creator = self._win.get_char_creator() self.char_creator = char_creator self._win.pack_in_main_content(self.char_creator) self.char_creator.reset_selected_menu_items() save_changes_button = KanoButton(_("Save changes").upper()) save_changes_button.connect("clicked", self.save_changes) discard_changes_button = OrangeButton(_("Discard").upper()) discard_changes_button.connect("clicked", self.discard) discard_changes_button.set_margin_left(100) empty_label = Gtk.Label("") button_box = Gtk.ButtonBox() button_box.pack_start(discard_changes_button, False, False, 0) button_box.pack_start(save_changes_button, False, False, 0) button_box.pack_start(empty_label, False, False, 0) self._win.pack_in_bottom_bar(button_box) self._win.show_all() # Hide all the pop ups self.char_creator._hide_pop_ups()
def __init__(self, stage, troubleshoot_cb, skip_cb): super(TroubleshootOrDisconnect, self).__init__(orientation=Gtk.Orientation.VERTICAL) self._stage = stage self.set_hexpand(False) self.set_vexpand(False) self.set_margin_left(40) self.set_margin_right(40) desc = Gtk.Label('Oops there was a problem connecting to internet.') desc.set_line_wrap(True) add_class(desc, 'console-screen-desc') img_path = self._stage.media_path("troubleshooting.png") image = Gtk.Image.new_from_file(img_path) troubleshoot = KanoButton('FIX IT') troubleshoot.connect('clicked', self._cb_wrapper, troubleshoot_cb) skip = KanoButton('SKIP', color="orange") skip.connect('clicked', self._cb_wrapper, skip_cb) buttons = Gtk.HBox(False, 0) buttons.pack_start(troubleshoot, True, True, 20) buttons.pack_start(skip, True, True, 0) self.pack_start(desc, False, False, 40) self.pack_start(image, False, False, 20) self.pack_start(buttons, False, False, 30)
class KanoButtonBox(Gtk.Box): def __init__(self, kano_button_text, orange_text="", orange_text_2=""): Gtk.Box.__init__(self) self.kano_button = KanoButton(kano_button_text) if not orange_text == "": self.orange_button = OrangeButton(orange_text) self.pack_start(self.orange_button, False, False, 0) self.pack_start(self.kano_button, False, False, 0) if orange_text_2 == "": # The empty label is to centre the kano_button label = Gtk.Label(" ") self.pack_start(label, False, False, 0) else: self.orange_button2 = OrangeButton(orange_text_2) self.pack_start(self.orange_button2, False, False, 0) else: self.pack_start(self.kano_button, False, False, 0) def get_kano_button(self): return self.kano_button def set_kano_button_cb(self, cb, args=[]): self.kano_button.connect('button-release-event', cb, args) def set_orange_button_cb(self, cb, args=[]): self.orange_button.connect('button-release-event', cb, args) def set_orange_button2_cb(self, cb, args=None): self.orange_button2.connect('button-release-event', cb, args)
def __generate_buttons(self): self.buttons = [] kano_button_box = Gtk.Box() for button in self.button_info: for argument, value in button_defaults.iteritems(): # Use default info if not provided if argument not in button: button[argument] = value # Create default return values for OK and CANCEL buttons if argument == 'return_value': if hasattr(button, 'label'): if button['label'] == _("OK"): button['return_value'] = 0 elif button['label'] == _("CANCEL"): button['return_value'] = 1 if argument == 'color': if button['label'] == _("CANCEL"): button['color'] = 'red' color = button['color'] return_value = button['return_value'] button_name = button['label'] button = KanoButton(button_name) button.set_color(color) button.connect('button-release-event', self.exit_dialog, return_value) button.connect('key-release-event', self.exit_dialog, return_value) self.buttons.append(button) kano_button_box.pack_start(button, False, False, 6) return kano_button_box
def third_scroll(self): self._clear() heading = Gtk.Label('New quest') add_class(heading, 'scroll-heading') world = Gtk.Image.new_from_file(self._stage.media_path('world.png')) copy = [ "Journey to Kano World", ] text_widgets = self._get_text_widgets(copy) button = KanoButton('OK', color='orange') button.connect('clicked', cb_wrapper, self._stage._ctl.next_stage) heading.set_margin_top(35) self._vbox.pack_start(heading, False, False, 0) world.set_margin_top(35) self._vbox.pack_start(world, False, False, 0) text_widgets[0].set_margin_top(40) for w in text_widgets: self._vbox.pack_start(w, False, False, 0) button.set_margin_top(40) button.set_margin_left(80) button.set_margin_right(80) self._vbox.pack_start(button, False, False, 0) self.show_all()
def __init__(self, stage, try_again_cb, skip_cb): super(AreYouSure, self).__init__(orientation=Gtk.Orientation.VERTICAL) self._stage = stage self.set_hexpand(False) self.set_vexpand(False) self.set_margin_left(40) self.set_margin_right(40) heading = Gtk.Label('Are You Sure?') heading.set_margin_top(20) add_class(heading, 'console-screen-heading') desc = Gtk.Label("Kano uses WiFi to stay up to date" + "\nwith all new software updates, apps" + "\nand features.") desc.set_justify(Gtk.Justification.CENTER) desc.set_line_wrap(True) add_class(desc, 'console-screen-desc') try_again = KanoButton('TRY AGAIN') try_again.connect('clicked', self._cb_wrapper, try_again_cb) skip = KanoButton('YES I WANT TO SKIP', color="grey") skip.connect('clicked', self._cb_wrapper, skip_cb, 'init-flow-wifi-skipped') buttons = Gtk.HBox(False, 0) buttons.pack_start(try_again, True, True, 20) buttons.pack_start(skip, True, True, 0) self.pack_start(heading, False, False, 30) self.pack_start(desc, False, False, 40) self.pack_start(buttons, False, False, 40)
def slide2(self): self.clear_window() fixed = Gtk.Fixed() pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self.media_path("world-slide.png"), 1000, 678 ) image = Gtk.Image.new_from_pixbuf(pixbuf) title = Gtk.Label("Kano World") title.get_style_context().add_class("title") desc = Gtk.Label( "Show off and share your creations to friends\n" + "family and other Kano users on Kano World.\n\n" + "Why not check it out now by clicking on the\n" + "icon to see what people have made today." ) desc.get_style_context().add_class("description") next_button = KanoButton("LET'S GO") next_button.connect("clicked", Gtk.main_quit) fixed.put(image, 0, 0) fixed.put(title, 415, 370) fixed.put(desc, 310, 420) fixed.put(next_button, 440, 580) self.add(fixed) self.show_all()
def __init__(self, stage, yes_cb, no_cb): super(Window1, self).__init__() add_class(self, 'overscan-window') head_img = Gtk.Image.new_from_file(stage.media_path('hint1.png')) box = Gtk.VBox() self.add(box) box.pack_start(head_img, False, False, 0) heading = Gtk.Label('Let\'s set up your screen') heading.set_margin_top(25) heading.set_margin_bottom(25) add_class(heading, 'notebook-heading') box.pack_start(heading, False, False, 0) heading = Gtk.Label('Are the white lines touching the edges?') heading.set_margin_bottom(35) add_class(heading, 'notebook-text') box.pack_start(heading, False, False, 0) buttons = Gtk.HBox(halign=Gtk.Align.CENTER) buttons.set_margin_bottom(25) yes = KanoButton('YES', color='green') yes.connect('clicked', cb_wrapper, yes_cb) buttons.pack_start(yes, False, False, 0) no = KanoButton('NO', color='red') no.connect('clicked', cb_wrapper, no_cb) buttons.pack_start(no, False, False, 10) box.pack_start(buttons, False, False, 0)
class CharacterEdit(Gtk.EventBox): '''Offer the user the option to modify their avatar ''' def __init__(self, win, char_creator): Gtk.EventBox.__init__(self) self._win = win # Should this be inherited, passed as a variable, or global? # Could be a member variable in window. # self.char_creator = self._win.get_char_creator() self.char_creator = char_creator self._win.pack_in_main_content(self.char_creator) self.char_creator.reset_selected_menu_items() self._save_changes_button = KanoButton(_("Save changes").upper()) self._save_changes_button.connect('clicked', self.save_changes) self._save_changes_button.set_sensitive(False) self.char_creator.connect( 'character_changed', self._make_button_sensitive ) discard_changes_button = OrangeButton(_("Discard").upper()) discard_changes_button.connect('clicked', self.discard) discard_changes_button.set_margin_left(100) empty_label = Gtk.Label("") button_box = Gtk.ButtonBox() button_box.pack_start(discard_changes_button, False, False, 0) button_box.pack_start(self._save_changes_button, False, False, 0) button_box.pack_start(empty_label, False, False, 0) self._win.pack_in_bottom_bar(button_box) self._win.show_all() # Hide all the pop ups self.char_creator._hide_pop_ups() def save_changes(self, widget): self.char_creator.save() self._go_back_to_display_screen() def discard(self, widget): self.char_creator.update_from_saved_image() self._go_back_to_display_screen() def _go_back_to_display_screen(self): '''Don't save, just go back to the edit character screen ''' self._win.empty_main_content() self._win.empty_bottom_bar() self._win.menu_bar.enable_buttons() CharacterDisplay(self._win) def _make_button_sensitive(self, widget=None): self._save_changes_button.set_sensitive(True)
def __init__(self, stage, image_path, image_scale, image_align, title, copy, buttons): super(Notebook, self).__init__() self.add( Gtk.Image.new_from_file(stage.media_path('lonely-notebook.png'))) fixed = Gtk.Fixed() self.add_overlay(fixed) self._eb = Gtk.EventBox() add_class(self._eb, 'notebook-content-area') self._eb.set_size_request(415, 460) fixed.put(self._eb, 20, 70) vbox = Gtk.VBox(False, 0) vbox.set_vexpand(True) vbox.set_hexpand(True) vbox.set_margin_left(10) vbox.set_margin_right(10) img = scale_image(Gtk.Image.new_from_file(image_path), image_scale) img_align = Gtk.Alignment.new(image_align, 0.5, 0, 0) img_align.add(img) img_align.set_vexpand(True) img_align.set_hexpand(True) vbox.pack_start(img_align, False, False, 0) heading = Gtk.Label(title) add_class(heading, 'notebook-heading') copy_widgets = [] for line in copy: l = Gtk.Label(line) l.set_line_wrap(False) l.set_justify(Gtk.Justification.CENTER) add_class(l, 'notebook-text') l.set_halign(Gtk.Align.CENTER) copy_widgets.append(l) # Pack heading vbox.pack_start(heading, False, False, 0) heading.set_margin_top(15) heading.set_margin_bottom(15) for w in copy_widgets: vbox.pack_start(w, False, False, 3) hbox = Gtk.HBox(False, 10) hbox.set_margin_top(15) hbox.set_halign(Gtk.Align.CENTER) for b in buttons: button = KanoButton(b['label'], color=b['color']) button.connect('clicked', cb_wrapper, b['callback']) hbox.pack_start(button, False, False, 0) vbox.pack_start(hbox, False, False, 0) self._eb.add(vbox)
def __init__(self, stage, image_path, image_scale, image_align, title, copy, buttons): super(Notebook, self).__init__() self.add(Gtk.Image.new_from_file(stage.media_path('lonely-notebook.png'))) fixed = Gtk.Fixed() self.add_overlay(fixed) self._eb = Gtk.EventBox() add_class(self._eb, 'notebook-content-area') self._eb.set_size_request(415, 460) fixed.put(self._eb, 20, 70) vbox = Gtk.VBox(False, 0) vbox.set_vexpand(True) vbox.set_hexpand(True) vbox.set_margin_left(10) vbox.set_margin_right(10) img = scale_image(Gtk.Image.new_from_file(image_path), image_scale) img_align = Gtk.Alignment.new(image_align, 0.5, 0, 0) img_align.add(img) img_align.set_vexpand(True) img_align.set_hexpand(True) vbox.pack_start(img_align, False, False, 0) heading = Gtk.Label(title) add_class(heading, 'notebook-heading') copy_widgets = [] for line in copy: l = Gtk.Label(line) l.set_line_wrap(False) l.set_justify(Gtk.Justification.CENTER) add_class(l, 'notebook-text') l.set_halign(Gtk.Align.CENTER) copy_widgets.append(l) # Pack heading vbox.pack_start(heading, False, False, 0) heading.set_margin_top(15) heading.set_margin_bottom(15) for w in copy_widgets: vbox.pack_start(w, False, False, 3) hbox = Gtk.HBox(False, 10) hbox.set_margin_top(15) hbox.set_halign(Gtk.Align.CENTER) for b in buttons: button = KanoButton(b['label'], color=b['color']) button.connect('clicked', cb_wrapper, b['callback']) hbox.pack_start(button, False, False, 0) vbox.pack_start(hbox, False, False, 0) self._eb.add(vbox)
class CharacterEdit(Gtk.EventBox): '''Offer the user the option to modify their avatar ''' def __init__(self, win, char_creator): Gtk.EventBox.__init__(self) self._win = win # Should this be inherited, passed as a variable, or global? # Could be a member variable in window. # self.char_creator = self._win.get_char_creator() self.char_creator = char_creator self._win.pack_in_main_content(self.char_creator) self.char_creator.reset_selected_menu_items() self._save_changes_button = KanoButton(_("Save changes").upper()) self._save_changes_button.connect('clicked', self.save_changes) self._save_changes_button.set_sensitive(False) self.char_creator.connect('character_changed', self._make_button_sensitive) discard_changes_button = OrangeButton(_("Discard").upper()) discard_changes_button.connect('clicked', self.discard) discard_changes_button.set_margin_left(100) empty_label = Gtk.Label("") button_box = Gtk.ButtonBox() button_box.pack_start(discard_changes_button, False, False, 0) button_box.pack_start(self._save_changes_button, False, False, 0) button_box.pack_start(empty_label, False, False, 0) self._win.pack_in_bottom_bar(button_box) self._win.show_all() # Hide all the pop ups self.char_creator._hide_pop_ups() def save_changes(self, widget): self.char_creator.save() self._go_back_to_display_screen() def discard(self, widget): self.char_creator.update_from_saved_image() self._go_back_to_display_screen() def _go_back_to_display_screen(self): '''Don't save, just go back to the edit character screen ''' self._win.empty_main_content() self._win.empty_bottom_bar() self._win.menu_bar.enable_buttons() CharacterDisplay(self._win) def _make_button_sensitive(self, widget=None): self._save_changes_button.set_sensitive(True)
def __init__(self, title, description, buttons, is_plug=False, img_path=None): super(Template, self).__init__(orientation=Gtk.Orientation.VERTICAL) self._focus_widget = None heading = Heading( title, description, is_plug, back_btn=False ) bbox = Gtk.ButtonBox() bbox.set_spacing(20) bbox.set_layout(Gtk.ButtonBoxStyle.CENTER) bbox.set_margin_right(10) bbox.set_margin_left(10) for b in buttons: label = b["label"] if not label: gtk_button = Gtk.Label() else: button_type = b["type"] callback = b["callback"] if button_type == "KanoButton": color = b["color"] gtk_button = KanoButton(label, color=color) elif button_type == "OrangeButton": gtk_button = OrangeButton(label) gtk_button.connect("clicked", callback) bbox.pack_start(gtk_button, False, False, 0) if "focus" in b: self._focus_widget = gtk_button self.pack_start(heading.container, False, False, 0) heading.container.set_margin_right(15) heading.container.set_margin_left(15) if img_path: image = Gtk.Image.new_from_file(img_path) if is_plug: self.pack_start(image, False, False, 10) self.pack_start(bbox, False, False, 30) else: self.pack_start(image, False, False, 20) self.pack_end(bbox, False, False, 30) self.show_all()
class NewUserView(Gtk.Grid): def __init__(self, greeter): Gtk.Grid.__init__(self) self.get_style_context().add_class('password') self.set_row_spacing(12) self.greeter = greeter title = Heading(_('Add new account'), _('Login with Kano World\n' 'or create a new account.')) self.attach(title.container, 0, 0, 1, 1) # the 2 push buttons self.login_btn = KanoButton(_('Kano World')) self.login_btn.connect('clicked', self._login_button_pressed) self.attach(self.login_btn, 0, 1, 1, 1) self.newuser_btn = KanoButton(_('New Account')) self.newuser_btn.connect('clicked', self._new_user_reboot) self.attach(self.newuser_btn, 0, 2, 1, 1) def _login_button_pressed(self, event=None, button=None): win = self.get_toplevel() win.go_to_login_with_kw() def _new_user_reboot(self, event=None, button=None): ''' Schedules kano-init to create a new user from scratch on next reboot, then performs the actual reboot ''' confirm = KanoDialog( title_text=_('Are you sure you want to create a new account?'), description_text=_('A reboot will be required'), button_dict=[ { 'label': _('Cancel').upper(), 'color': 'red', 'return_value': False }, { 'label': _('Create').upper(), 'color': 'green', 'return_value': True } ]) confirm.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) if confirm.run(): os.system("sudo kano-init schedule add-user") LightDM.restart()
def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win reset_button = KanoButton(text='RESET YOUR DESKTOP', color='orange') reset_button.connect('button-release-event', self.reset_button_cb) reset_button.connect('key-release-event', self.reset_button_cb) reset_button.pack_and_align() reset_button.align.set(0.5, 0.5, 0, 0) self.pack_start(reset_button.align, True, True, 0)
def __init__(self, title, description, buttons, is_plug=False, img_path=None): super(Template, self).__init__(orientation=Gtk.Orientation.VERTICAL) self._focus_widget = None heading = Heading(title, description, is_plug, back_btn=False) bbox = Gtk.ButtonBox() bbox.set_spacing(20) bbox.set_layout(Gtk.ButtonBoxStyle.CENTER) bbox.set_margin_right(10) bbox.set_margin_left(10) for b in buttons: label = b["label"] if not label: gtk_button = Gtk.Label() else: button_type = b["type"] callback = b["callback"] if button_type == "KanoButton": color = b["color"] gtk_button = KanoButton(label, color=color) elif button_type == "OrangeButton": gtk_button = OrangeButton(label) gtk_button.connect("clicked", callback) bbox.pack_start(gtk_button, False, False, 0) if "focus" in b: self._focus_widget = gtk_button self.pack_start(heading.container, False, False, 0) heading.container.set_margin_right(15) heading.container.set_margin_left(15) if img_path: image = Gtk.Image.new_from_file(img_path) if is_plug: self.pack_start(image, False, False, 10) self.pack_start(bbox, False, False, 30) else: self.pack_start(image, False, False, 20) self.pack_end(bbox, False, False, 30) self.show_all()
def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.get_style_context().add_class('notebook_page') reset_button = KanoButton(text=_("RESET YOUR DESKTOP"), color='orange') reset_button.connect('button-release-event', self.reset_button_cb) reset_button.connect('key-release-event', self.reset_button_cb) reset_button.pack_and_align() reset_button.align.set(0.5, 0.5, 0, 0) self.pack_start(reset_button.align, True, True, 0)
def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.get_style_context().add_class('notebook_page') reset_button = KanoButton(text=_("RESET YOUR DESKTOP"), color='orange') reset_button.connect('button-release-event', self.reset_button_cb) reset_button.connect('key-release-event', self.reset_button_cb) reset_button.pack_and_align() reset_button.align.set(0.5, 0.5, 0, 0) self.pack_start(reset_button.align, True, True, 0)
class KanoButtonBox(Gtk.Box): def __init__(self, kano_button_text, orange_text="", orange_text_2="", orange_text_3=""): Gtk.Box.__init__(self) self.kano_button = KanoButton(kano_button_text) if orange_text != "": self.orange_button = OrangeButton(orange_text) self.pack_start(self.orange_button, False, False, 0) self.pack_start(self.kano_button, False, False, 0) if orange_text_2 == "": # The empty label is to centre the kano_button label = Gtk.Label(" ") self.pack_start(label, False, False, 0) else: self.orange_button2 = OrangeButton(orange_text_2) if orange_text_3 == "": self.pack_start(self.orange_button2, False, False, 0) else: # If two orange button texts, we align them vertically self.vertbox = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=6) self.add(self.vertbox) self.orange_button3 = OrangeButton(orange_text_3) self.vertbox.pack_start(self.orange_button2, False, False, 0) self.vertbox.pack_start(self.orange_button3, False, False, 0) else: self.pack_start(self.kano_button, False, False, 0) def get_kano_button(self): return self.kano_button def set_kano_button_cb(self, cb, args=[]): self.kano_button.connect('button-release-event', cb, args) def set_orange_button_cb(self, cb, args=[]): self.orange_button.connect('button-release-event', cb, args) def set_orange_button2_cb(self, cb, args=None): self.orange_button2.connect('button-release-event', cb, args) def set_orange_button3_cb(self, cb, args=None): self.orange_button3.connect('button-release-event', cb, args)
def __init__(self): Gtk.Window.__init__(self, title=self._TITLE) apply_common_to_screen() window_height = self._IMAGE_HEIGHT + 220 self.set_size_request(self._IMAGE_WIDTH, window_height) self.set_decorated(False) self.set_resizable(False) self.set_position(Gtk.WindowPosition.CENTER) self.set_icon_name('kano-updater') # Put the window above all the existing ones when it starts # FIXME: this needs to happen within a 'realized' signal handler # disabled for now #self.get_window().raise_() image = Gtk.Image() image.set_from_file(self._HEADER_IMAGE) background = Gtk.EventBox() background.set_size_request(self._IMAGE_WIDTH, self._IMAGE_HEIGHT) background.add(image) # Header heading = Heading(self._HEADING, self._BYLINE) heading.description.set_line_wrap(True) action = KanoButton(self._ACTION.upper()) action.connect('clicked', self._do_action) action.set_halign(Gtk.Align.CENTER) later = OrangeButton(_("Later")) later.connect('clicked', self._do_later) later.set_halign(Gtk.Align.START) later.set_margin_left(40) buttons = Gtk.Overlay() buttons.add(action) buttons.add_overlay(later) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box.pack_start(background, False, False, 0) box.pack_start(heading.container, False, False, 10) box.pack_start(buttons, False, False, 0) self.add(box) self.show_all()
def __init__(self): Gtk.Window.__init__(self, title=self._TITLE) apply_common_to_screen() window_height = self._IMAGE_HEIGHT + 220 self.set_size_request(self._IMAGE_WIDTH, window_height) self.set_decorated(False) self.set_resizable(False) self.set_position(Gtk.WindowPosition.CENTER) self.set_icon_name('kano-updater') # Make sure this window is always above self.set_keep_above(True) image = Gtk.Image() image.set_from_file(self._HEADER_IMAGE) background = Gtk.EventBox() background.set_size_request(self._IMAGE_WIDTH, self._IMAGE_HEIGHT) background.add(image) # Header heading = Heading(self._HEADING, self._BYLINE) heading.description.set_line_wrap(True) action = KanoButton(self._ACTION.upper()) action.connect('clicked', self._do_action) action.set_halign(Gtk.Align.CENTER) later = OrangeButton(_('Later')) later.connect('clicked', Gtk.main_quit) later.set_halign(Gtk.Align.START) later.set_margin_left(40) buttons = Gtk.Overlay() buttons.add(action) buttons.add_overlay(later) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box.pack_start(background, False, False, 0) box.pack_start(heading.container, False, False, 10) box.pack_start(buttons, False, False, 0) self.add(box) self.show_all()
class KanoButtonBox(Gtk.Box): def __init__(self, kano_button_text, orange_text="", orange_text_2="", orange_text_3=""): Gtk.Box.__init__(self) self.kano_button = KanoButton(kano_button_text) if orange_text != "": self.orange_button = OrangeButton(orange_text) self.pack_start(self.orange_button, False, False, 0) self.pack_start(self.kano_button, False, False, 0) if orange_text_2 == "": # The empty label is to centre the kano_button label = Gtk.Label(" ") self.pack_start(label, False, False, 0) else: self.orange_button2 = OrangeButton(orange_text_2) if orange_text_3 == "": self.pack_start(self.orange_button2, False, False, 0) else: # If two orange button texts, we align them vertically self.vertbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) self.add(self.vertbox) self.orange_button3 = OrangeButton(orange_text_3) self.vertbox.pack_start(self.orange_button2, False, False, 0) self.vertbox.pack_start(self.orange_button3, False, False, 0) else: self.pack_start(self.kano_button, False, False, 0) def get_kano_button(self): return self.kano_button def set_kano_button_cb(self, cb, args=[]): self.kano_button.connect('button-release-event', cb, args) def set_orange_button_cb(self, cb, args=[]): self.orange_button.connect('button-release-event', cb, args) def set_orange_button2_cb(self, cb, args=None): self.orange_button2.connect('button-release-event', cb, args) def set_orange_button3_cb(self, cb, args=None): self.orange_button3.connect('button-release-event', cb, args)
class NoInternet(Gtk.Box): selected_button = 0 initial_button = 0 def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.win.set_main_widget(self) # Main image image = Gtk.Image.new_from_file(media + "/Graphics/no-internet-screen.png") # Orange button later_button = OrangeButton(_("Later")) later_button.connect('button-release-event', self.win.close_window) # Green button self.kano_button = KanoButton(_("CONNECT NOW")) self.kano_button.pack_and_align() self.kano_button.connect('button-release-event', self.go_to_wifi) self.kano_button.connect('key-release-event', self.go_to_wifi) # Text label text_align = self.create_text_align() # Place elements image.set_margin_top(50) image.set_margin_bottom(30) self.pack_start(image, False, False, 0) self.pack_start(text_align, False, False, 2) self.pack_start(self.kano_button.align, False, False, 10) self.pack_start(later_button, False, False, 3) # Refresh window self.win.show_all() def go_to_wifi(self, widget=None, event=None): self.win.clear_win() SetWifi(self.win) def create_text_align(self): label = Gtk.Label(_("You need internet to continue")) label.get_style_context().add_class('about_version') align = Gtk.Alignment(xalign=0.5, xscale=0, yalign=0, yscale=0) align.add(label) return align
class NoInternet(Gtk.Box): selected_button = 0 initial_button = 0 def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.win.set_main_widget(self) # Main image image = Gtk.Image.new_from_file(media + "/Graphics/no-internet-screen.png") # Orange button later_button = OrangeButton(_("Later")) later_button.connect('button-release-event', self.win.close_window) # Green button self.kano_button = KanoButton(_("CONNECT NOW")) self.kano_button.pack_and_align() self.kano_button.connect('button-release-event', self.go_to_wifi) self.kano_button.connect('key-release-event', self.go_to_wifi) # Text label text_align = self.create_text_align() # Place elements image.set_margin_top(50) image.set_margin_bottom(30) self.pack_start(image, False, False, 0) self.pack_start(text_align, False, False, 2) self.pack_start(self.kano_button.align, False, False, 10) self.pack_start(later_button, False, False, 3) # Refresh window self.win.show_all() def go_to_wifi(self, widget=None, event=None): self.win.clear_win() SetWifi(self.win) def create_text_align(self): label = Gtk.Label(_("You need internet to continue")) label.get_style_context().add_class('about_version') align = Gtk.Alignment(xalign=0.5, xscale=0, yalign=0, yscale=0) align.add(label) return align
def __init__(self, cb): super(CharacterWindow, self).__init__() self.get_style_context().add_class("character_window") self.set_decorated(False) self.close_cb = cb self.char_edit = CharacterCreator(randomise=True, no_sync=True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(vbox) vbox.pack_start(self.char_edit, False, False, 0) button = KanoButton(_("OK")) button.connect("clicked", self.close_window) button.pack_and_align() self.connect("delete-event", Gtk.main_quit) self.set_keep_above(True) vbox.pack_start(button.align, False, False, 10) self.show_all()
class LoggedIn(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title='Profile') self.set_size_request(200, 150) self.set_decorated(False) self.set_position(Gtk.WindowPosition.CENTER) self.set_resizable(False) self.ok_button = KanoButton(_("OK")) self.ok_button.pack_and_align() self.ok_button.set_padding(20, 20, 0, 0) self.ok_button.connect("clicked", Gtk.main_quit) self.title = Heading(_("Logged in!"), _("You're already logged in")) self.main_container = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=0) self.add(self.main_container) self.main_container.pack_start(self.title.container, False, False, 0) self.main_container.pack_start(self.ok_button.align, False, False, 0) # To get a logout button, uncomment out the lines below #self.logout_button = OrangeButton(_("Log out?")) #self.logout_button.connect("clicked", self.logout) #self.main_container.pack_start(self.logout_button, False, False, 0) self.connect('delete-event', Gtk.main_quit) self.show_all() def logged_out_screen(self): for child in self.main_container: self.main_container.remove(child) self.title.set_text(_("Logged out!"), "") self.main_container.pack_start(self.title.container, False, False, 0) self.main_container.pack_start(self.alignment, False, False, 0) def logout(self, event): remove_token() self.logged_out_screen() def close_window(self, event, button, win): self.ok_button.disconnect_handlers() cursor.arrow_cursor(None, None, win) Gtk.main_quit()
class LoggedIn(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title='Profile') self.set_size_request(200, 150) self.set_decorated(False) self.set_position(Gtk.WindowPosition.CENTER) self.set_resizable(False) self.ok_button = KanoButton(_("OK")) self.ok_button.pack_and_align() self.ok_button.set_padding(20, 20, 0, 0) self.ok_button.connect('clicked', Gtk.main_quit) self.title = Heading(_("Logged in!"), _("You're already logged in")) self.main_container = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=0) self.add(self.main_container) self.main_container.pack_start(self.title.container, False, False, 0) self.main_container.pack_start(self.ok_button.align, False, False, 0) # To get a logout button, uncomment out the lines below #self.logout_button = OrangeButton(_("Log out?")) #self.logout_button.connect('clicked', self.logout) #self.main_container.pack_start(self.logout_button, False, False, 0) self.connect('delete-event', Gtk.main_quit) self.show_all() def logged_out_screen(self): for child in self.main_container: self.main_container.remove(child) self.title.set_text(_("Logged out!"), "") self.main_container.pack_start(self.title.container, False, False, 0) self.main_container.pack_start(self.alignment, False, False, 0) def logout(self, event): remove_token() self.logged_out_screen() def close_window(self, event, button, win): self.ok_button.disconnect_handlers() cursor.arrow_cursor(None, None, win) Gtk.main_quit()
def __init__(self, stage, now_cb, later_cb): super(ParentalScreen, self).__init__(False, 0) self.set_hexpand(False) self.set_vexpand(False) self.set_margin_left(40) self.set_margin_right(40) heading = Gtk.Label('For parents...') add_class(heading, 'console-screen-heading') copy = 'You can put a safety filter on Kano\'s internet. ' + \ 'You can set it now or later.' desc = Gtk.Label(copy) desc.set_line_wrap(True) add_class(desc, 'console-screen-desc') padlock = Gtk.Image.new_from_file(stage.media_path('padlock.png')) later = KanoButton('GOT IT') later.connect('clicked', self._cb_wrapper, later_cb, 'init-flow-parental-skipped') later.set_size_request(200, 50) now = OrangeButton('SET NOW') now.connect('clicked', self._cb_wrapper, now_cb, 'init-flow-parental-set') emptylabel = Gtk.Label(" ") buttons = Gtk.ButtonBox() buttons.set_layout(Gtk.ButtonBoxStyle.SPREAD) buttons.pack_start(emptylabel, True, True, 0) buttons.pack_start(later, True, True, 20) buttons.pack_start(now, True, True, 20) self.pack_start(heading, False, False, 30) self.pack_start(desc, False, False, 20) self.pack_start(padlock, False, False, 10) self.pack_start(buttons, True, True, 30)
def __init__(self, stage, scene, yes_cb, no_cb): super(ConsoleScreen, self).__init__() self.set_size_request(368*scene.scale_factor, 179*scene.scale_factor) add_class(self, 'console-screen') if scene.scale_factor > 0.8: add_class(self, 'font-large') else: add_class(self, 'font-small') vbox = Gtk.VBox(False, 0) vbox.set_halign(Gtk.Align.CENTER) # vbox.set_valign(Gtk.Align.CENTER) question = Gtk.Label('Can you see the lights?') question.set_line_wrap(True) question.set_justify(Gtk.Justification.CENTER) question.set_valign(Gtk.Align.CENTER) add_class(question, 'console-screen-question') vbox.pack_start(question, True, False, 0) hbox = Gtk.HBox(False, 20) hbox.set_halign(Gtk.Align.CENTER) hbox.set_valign(Gtk.Align.CENTER) yes = KanoButton('YES') yes.connect('clicked', cb_wrapper, yes_cb) no = KanoButton('NO', color='red') no.connect('clicked', cb_wrapper, no_cb) hbox.pack_start(yes, False, False, 0) hbox.pack_start(no, False, False, 0) vbox.pack_start(hbox, True, False, 0) self.add(vbox)
def __init__(self, cb): super(CharacterWindow, self).__init__() self.get_style_context().add_class("character_window") self.set_decorated(False) self.close_cb = cb self.char_edit = CharacterCreator(randomise=True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(vbox) vbox.pack_start(self.char_edit, False, False, 0) button = KanoButton("OK") button.connect("clicked", self.close_window) button.pack_and_align() self.connect("delete-event", Gtk.main_quit) self.set_keep_above(True) vbox.pack_start(button.align, False, False, 10) self.show_all() self.char_edit.show_pop_up_menu_for_category("judoka-faces") self.char_edit.select_category_button("judoka-faces")
class CharacterWindow(Gtk.Window): def __init__(self, cb, css_path): super(CharacterWindow, self).__init__() apply_styling_to_screen(css_path) self.get_style_context().add_class("character_window") self.set_decorated(False) self.close_cb = cb self.char_edit = CharacterCreator(randomise=True, no_sync=True) self.char_edit.connect("character_changed", self._make_button_sensitive) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(vbox) vbox.pack_start(self.char_edit, False, False, 0) self._kano_button = KanoButton("OK") self._kano_button.connect("clicked", self.close_window) self._kano_button.pack_and_align() self._kano_button.set_sensitive(False) self.connect("delete-event", Gtk.main_quit) self.set_keep_above(True) vbox.pack_start(self._kano_button.align, False, False, 10) self.show_all() self.char_edit.show_pop_up_menu_for_category("judoka-faces") self.char_edit.select_category_button("judoka-faces") def _make_button_sensitive(self, widget=None): self._kano_button.set_sensitive(True) def close_window(self, widget): self.char_edit.save() self.destroy() GLib.idle_add(self.close_cb)
class SetAbout(Gtk.Box): selected_button = 0 initial_button = 0 def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.win.set_main_widget(self) self.win.top_bar.enable_prev() self.win.change_prev_callback(self.win.go_to_home) image = Gtk.Image.new_from_file(media + "/Graphics/about-screen.png") version_align = self.create_align( "Kano OS v.{version}".format(version=get_current_version()), 'about_version' ) space_align = self.create_align( _("Disk space used: {used}B / {total}B").format(**get_space_available()) ) try: celsius = u"{:.1f}\N{DEGREE SIGN}C".format(get_temperature()) except ValueError: celsius = "?" temperature_align = self.create_align( _(u"Temperature: {celsius}").format(celsius=celsius) ) model_align = self.create_align( _("Model: {model}").format(model=get_model_name()) ) terms_and_conditions = OrangeButton(_("Terms and conditions")) terms_and_conditions.connect( 'button_release_event', self.show_terms_and_conditions ) credits_button = OrangeButton(_("Meet the team")) credits_button.connect( 'button_release_event', self.show_credits ) changelog_button = OrangeButton(_("Changelog")) changelog_button.connect( 'button_release_event', self.show_changelog ) self.kano_button = KanoButton(_("BACK")) self.kano_button.pack_and_align() hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) hbox.pack_start(terms_and_conditions, False, False, 4) hbox.pack_start(credits_button, False, False, 4) hbox.pack_start(changelog_button, False, False, 4) hbutton_container = Gtk.Alignment( xalign=0.5, xscale=0, yalign=0, yscale=0 ) hbutton_container.add(hbox) image.set_margin_top(10) self.pack_start(image, False, False, 10) self.pack_start(version_align, False, False, 2) self.pack_start(space_align, False, False, 1) self.pack_start(temperature_align, False, False, 1) self.pack_start(model_align, False, False, 1) self.pack_start(hbutton_container, False, False, 3) self.pack_start(self.kano_button.align, False, False, 10) self.kano_button.connect('button-release-event', self.win.go_to_home) self.kano_button.connect('key-release-event', self.win.go_to_home) # Refresh window self.win.show_all() def create_align(self, text, css_class='about_label'): '''This styles the status information in the 'about' dialog ''' label = Gtk.Label(text) label.get_style_context().add_class(css_class) align = Gtk.Alignment(xalign=0.5, xscale=0, yalign=0, yscale=0) align.add(label) return align def show_terms_and_conditions(self, widget, event): '''This is the dialog containing the terms and conditions - same as shown before creating an account ''' legal_text = '' for file in os.listdir(legal_dir): with open(legal_dir + file, 'r') as f: legal_text = legal_text + f.read() + '\n\n\n' kdialog = KanoDialog(_("Terms and conditions"), "", scrolled_text=legal_text, parent_window=self.win) kdialog.run() def show_credits(self, widget, event): '''Launch the credits ''' os.system( "/usr/bin/kano-launcher \"kdesk-blur 'urxvt -bg " "rgba:0000/0000/0000/FFFF -title 'Credits' -e " "/usr/bin/kano-credits'\"" ) def show_changelog(self, widget, event): '''Launch chromium with the link of the relevent changelog ''' # Assuming current_version is of the form 1.3.4 current_version = get_current_version() # Full link should be analogous to # http://world.kano.me/forum/topic/kanux-beta-v1.2.3 link = "http://world.kano.me/forum/topic/kanux-beta-v{}".format( current_version ) launch_browser(link) return
class PasswordView(Gtk.Grid): def __init__(self, user, greeter): Gtk.Grid.__init__(self) self.get_style_context().add_class('password') self.set_row_spacing(10) self.greeter = greeter self.user = user self.title = self._set_title() self.attach(self.title.container, 0, 0, 1, 1) self.label = Gtk.Label(user) self.label.get_style_context().add_class('login') self.attach(self.label, 0, 1, 1, 1) self.password = Gtk.Entry() self.password.set_visibility(False) self.password.set_alignment(0.5) self.password.connect('activate', self._login_cb) self.attach(self.password, 0, 2, 1, 1) self.login_btn = KanoButton(_('Login').upper()) self.login_btn.connect('clicked', self._login_cb) self.attach(self.login_btn, 0, 3, 1, 1) # Protect against removing the last Kano user # so you do not get locked out from logging into the Kit system_users = KanoUserList().get_users() if len(system_users) > 1: delete_account_btn = OrangeButton(_('Remove Account')) delete_account_btn.connect('clicked', self.delete_user) self.attach(delete_account_btn, 0, 4, 1, 1) def _set_title(self, create=True): ''' Creates a Heading text widget, or updates it with the currently selected username. ''' text_title = _('{}: Enter your password').format(self.user) text_description = _( 'If you haven\'t changed your\npassword, use "kano"') if create: title = Heading(text_title, text_description) return title else: self.title.set_text(text_title, text_description) return self.title def _reset_greeter(self): # connect signal handlers to LightDM self.cb_one = self.greeter.connect('show-prompt', self._send_password_cb) self.cb_two = self.greeter.connect('authentication-complete', self._authentication_complete_cb) self.cb_three = self.greeter.connect('show-message', self._auth_error_cb) self.greeter.connect_sync() return (self.cb_one, self.cb_two, self.cb_three) def _login_cb(self, event=None, button=None): logger.debug('Sending username to LightDM') self.login_btn.start_spinner() Gtk.main_iteration_do(True) self.greeter.authenticate(self.user) if self.greeter.get_is_authenticated(): logger.debug('User is already authenticated, starting session') start_session() def _send_password_cb(self, _greeter, text, prompt_type): logger.debug('Need to show prompt: {}'.format(text)) if _greeter.get_in_authentication(): logger.debug('Sending password to LightDM') _greeter.respond(self.password.get_text()) def _authentication_complete_cb(self, _greeter): logger.debug('Authentication process is complete') if not _greeter.get_is_authenticated(): logger.warn('Could not authenticate user {}'.format(self.user)) self._auth_error_cb( _('Incorrect password (The default is "kano")')) return logger.info( 'The user {} is authenticated. Starting LightDM X Session'.format( self.user)) set_last_user(self.user) if not _greeter.start_session_sync('lightdm-xsession'): logger.error('Failed to start session') else: logger.info('Login failed') def _auth_error_cb(self, text, message_type=None): logger.info('There was an error logging in: {}'.format(text)) self.greeter.cancel_authentication() self.login_btn.stop_spinner() self.password.set_text('') win = self.get_toplevel() error = KanoDialog(title_text=_('Error Logging In'), description_text=text, parent_window=win) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() win.go_to_users() def grab_focus(self, user): ''' Update username title, clear previous password, and give focus to password entry field. ''' self.user = user self._set_title(create=False) self.password.set_text('') self.password.grab_focus() def delete_user(self, *args): import pam password_input = Gtk.Entry() password_input.set_visibility(False) password_input.set_alignment(0.5) confirm = KanoDialog( title_text=_('Are you sure you want to delete this account?'), description_text=_( 'Enter {}\'s password - A reboot will be required'.format( self.user)), widget=password_input, has_entry=True, button_dict=[{ 'label': _('Cancel').upper(), 'color': 'red', 'return_value': False }, { 'label': _('Ok').upper(), 'color': 'green', 'return_value': True }]) confirm.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) # Kano Dialog will return False if cancel button is clicked, or text from the entry field if Ok response = confirm.run() if response == False: return elif type(response) == str and not len(response): error = KanoDialog(title_text=_( 'Please enter the password for user {}'.format(self.user))) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() return else: password = response # Authenticate user and schedule removal. Protect against unknown troubles. try: if pam.authenticate(self.user, password): info = KanoDialog(title_text = _('User {} scheduled for removal'.format(self.user)), \ description_text = _('Press OK to reboot')) info.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) info.run() os.system('sudo kano-init schedule delete-user "{}"'.format( self.user)) LightDM.restart() else: error = KanoDialog(title_text=_( 'Incorrect password for user {}'.format(self.user))) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() except Exception as e: logger.error('Error deleting account {} - {}'.format( self.user, str(e))) error = KanoDialog( title_text=_('Could not delete account {}'.format(self.user))) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run()
class EditableList(Gtk.Grid): def __init__(self, size_x=400, size_y=150): Gtk.Grid.__init__(self) self.set_row_spacing(10) self.set_column_spacing(10) scroll = ScrolledWindow() scroll.set_size_request(size_x, size_y) scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.edit_list_store = Gtk.ListStore(str) self.edit_list = Gtk.TreeView(self.edit_list_store) self.edit_list.set_headers_visible(False) renderer = Gtk.CellRendererText() renderer.set_property('editable', True) renderer.connect('edited', self._item_edited_handler) renderer.connect('editing-started', self._item_edit_started) renderer.connect('editing-canceled', self._item_edit_canceled) column = Gtk.TreeViewColumn(cell_renderer=renderer, text=0) self.edit_list.append_column(column) self._add_btn = KanoButton(_("ADD")) self._add_btn.connect('button-release-event', self.add) self._rm_btn = KanoButton(_("REMOVE")) self._set_rm_btn_state() self._rm_btn.connect('button-release-event', self.rm) scroll.add_with_viewport(self.edit_list) self.attach(scroll, 0, 0, 2, 1) self.attach(self._add_btn, 0, 1, 1, 1) self.attach(self._rm_btn, 1, 1, 1, 1) def __contains__(self, item): return item in [row[0] for row in self.edit_list_store] def add(self, button, event): self.edit_list_store.append(['']) self.edit_list.grab_focus() row = len(self.edit_list_store) - 1 col = self.edit_list.get_column(0) self.edit_list.set_cursor(row, col, start_editing=True) self._rm_btn.set_sensitive(False) def rm(self, button=None, event=None): selection = self.edit_list.get_selection() dummy, selected = selection.get_selected() if not selected: return self.edit_list_store.remove(selected) self._set_rm_btn_state() def _item_edited_handler(self, cellrenderertext, path, new_text): if new_text is None: # FIXME: the reason for the os.system here is that the 'edited' signal # triggers on a key-pressed-event and the dialog closes on release. So # you would only see the dialog while holding down the 'ENTER' key. title = _("Invalid website given") description = _("\nWe need to make sure the website URL is valid.\n" \ "Please enter the full URL as it appears in your browser.\n\n" \ "Example: http://www.google.com\n") buttons = _("OK:red:1") cmd = 'kano-dialog title="{}" description="{}" buttons="{}" no-taskbar &'.format( title.encode('utf8'), description.encode('utf8'), buttons.encode('utf8')) os.system(cmd) self.rm() else: selection = self.edit_list.get_selection() dummy, selected = selection.get_selected() if new_text and new_text not in self: self.edit_list_store.set_value(selected, 0, new_text) else: row = self.edit_list_store[selected] old_text = row[0] if not old_text: self.rm() self._add_btn.set_sensitive(True) self._set_rm_btn_state() def _item_edit_started(self, *_): self._add_btn.set_sensitive(False) def _item_edit_canceled(self, *_): self._add_btn.set_sensitive(True) self.rm() def _set_rm_btn_state(self): state = len(self.edit_list_store) != 0 self._rm_btn.set_sensitive(state)
class PasswordView(Gtk.Grid): def __init__(self, user, greeter): Gtk.Grid.__init__(self) self.get_style_context().add_class('password') self.set_row_spacing(10) self.greeter = greeter self.user = user self.title = self._set_title() self.attach(self.title.container, 0, 0, 1, 1) self.label = Gtk.Label(user) self.label.get_style_context().add_class('login') self.attach(self.label, 0, 1, 1, 1) self.password = Gtk.Entry() self.password.set_visibility(False) self.password.set_alignment(0.5) self.password.connect('activate', self._login_cb) self.attach(self.password, 0, 2, 1, 1) self.login_btn = KanoButton(_('Login').upper()) self.login_btn.connect('clicked', self._login_cb) self.attach(self.login_btn, 0, 3, 1, 1) # Protect against removing the last Kano user # so you do not get locked out from logging into the Kit system_users = KanoUserList().get_users() if len(system_users) > 1: delete_account_btn = OrangeButton(_('Remove Account')) delete_account_btn.connect('clicked', self.delete_user) self.attach(delete_account_btn, 0, 4, 1, 1) def _set_title(self, create=True): ''' Creates a Heading text widget, or updates it with the currently selected username. ''' text_title = _('{}: Enter your password').format(self.user) text_description = _('If you haven\'t changed your\npassword, use "kano"') if create: title = Heading(text_title, text_description) return title else: self.title.set_text(text_title, text_description) return self.title def _reset_greeter(self): # connect signal handlers to LightDM self.cb_one = self.greeter.connect('show-prompt', self._send_password_cb) self.cb_two = self.greeter.connect('authentication-complete', self._authentication_complete_cb) self.cb_three = self.greeter.connect('show-message', self._auth_error_cb) self.greeter.connect_sync() return (self.cb_one, self.cb_two, self.cb_three) def _login_cb(self, event=None, button=None): logger.debug('Sending username to LightDM') self.login_btn.start_spinner() Gtk.main_iteration_do(True) # The call below will simply initiate the login flow. # The greeter library will inform us through callbacks # See: http://web.mit.edu/Source/debathena/config/lightdm-config/debian/debathena-lightdm-greeter self.greeter.authenticate(self.user) def _send_password_cb(self, _greeter, text, prompt_type): logger.debug(u'Need to show prompt: {}'.format(text)) if _greeter.get_in_authentication(): logger.debug('Sending password to LightDM') _greeter.respond(self.password.get_text()) def _authentication_complete_cb(self, _greeter): logger.debug('Authentication process is complete') if not _greeter.get_is_authenticated(): logger.warn('Could not authenticate user {}'.format(self.user)) self._auth_error_cb(_('Incorrect password (The default is "kano")')) return logger.info( 'The user {} is authenticated. Starting LightDM X Session' .format(self.user)) set_last_user(self.user) if not _greeter.start_session_sync('lightdm-xsession'): logger.error('Failed to start session') else: logger.info('Login failed') def _auth_error_cb(self, text, message_type=None): logger.info(u'There was an error logging in: {}'.format(text)) self.greeter.cancel_authentication() self.login_btn.stop_spinner() self.password.set_text('') win = self.get_toplevel() error = KanoDialog( title_text=_('Error Logging In'), description_text=text, parent_window=win ) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() win.go_to_users() def grab_focus(self, user): ''' Update username title, clear previous password, and give focus to password entry field. ''' self.user = user self._set_title(create=False) self.password.set_text('') self.password.grab_focus() def delete_user(self, *args): import pam password_input = Gtk.Entry() password_input.set_visibility(False) password_input.set_alignment(0.5) confirm = KanoDialog( title_text = _('Are you sure you want to delete this account?'), description_text = _('Enter {}\'s password - A reboot will be required'.format(self.user)), widget=password_input, has_entry=True, button_dict = [ { 'label': _('Cancel').upper(), 'color': 'red', 'return_value': False }, { 'label': _('Ok').upper(), 'color': 'green', 'return_value': True } ]) confirm.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) # Kano Dialog will return False if cancel button is clicked, or text # from the entry field if Ok response = confirm.run() if not response: return elif type(response) == str and not len(response): error = KanoDialog( title_text=_('Please enter the password for user {}'.format(self.user)) ) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() return else: password = response # Authenticate user and schedule removal. Protect against unknown troubles. try: if pam.authenticate(self.user, password): info = KanoDialog( title_text=_('User {} scheduled for removal'.format(self.user)), description_text=_('Press OK to reboot') ) info.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) info.run() os.system('sudo kano-init schedule delete-user "{}"'.format(self.user)) LightDM.restart() else: error = KanoDialog( title_text=_('Incorrect password for user {}'.format(self.user)) ) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() except Exception as e: logger.error(u'Error deleting account {} - {}'.format(self.user, str(e))) error = KanoDialog( title_text=_('Could not delete account {}'.format(self.user)) ) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run()
class RegistrationScreen(Gtk.Box): """ """ def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.win.set_main_widget(self) self.win.set_decorated(True) title = Heading(_("Kano World"), _("Choose a cool name and secure password")) self.pack_start(title.container, False, False, 0) self.data_screen = GetData() # TODO: refactor this self.data_screen.connect('widgets-filled', self._enable_register_button) self.data_screen.connect('widgets-empty', self._disable_register_button) self.add(self.data_screen) self.register_button = KanoButton(_("JOIN KANO WORLD")) self.register_button.set_sensitive(False) self.register_button.set_margin_top(10) self.register_button.set_margin_left(30) self.register_button.set_margin_right(30) self.register_button.set_margin_bottom(30) self.register_button.connect('clicked', self._on_register_button) self.pack_end(self.register_button, False, False, 0) self.win.show_all() def _enable_register_button(self, widget=None): """ """ self.register_button.set_sensitive(True) def _disable_register_button(self, widget=None): """ """ self.register_button.set_sensitive(False) def _on_register_button(self, widget=None): # TODO: refactor this """ """ if not is_internet(): self._show_not_internet_dialog() return # Get the username, password and birthday data = self.data_screen.get_widget_data() email = data['email'] username = data['username'] # Validate that the email address format is correct email_error = validate_email(email) if email_error: self._show_error_dialog(_("Incorrect Email address"), email_error) return if not self._is_username_available(username): self._show_username_taken_dialog(username) return # We can save the username to kano-profile # Don't save password as this is private self.data_screen.save_username_and_birthday() # TODO: rename this self.data_screen.cache_emails() data = self.data_screen.get_widget_data() # This means no threads are needed. while Gtk.events_pending(): # TODO: why is this needed? Gtk.main_iteration() # Try and register the account on the server password = data['password'] success, text = register_(email, username, password, marketing_enabled=True) # This should no longer be needed, since this is checked in the first # screen. However there is a small chance someone could take the # username while the user is in the process of registering if not success: if text.strip() == _("Cannot register, problem: " "Username already registered"): self._show_username_taken_dialog(username) else: logger.info("problem with registration: {}".format(text)) return_value = 'FAIL' self._create_dialog(title=_("Houston, we have a problem"), description=str(text)) track_data('world-registration-failed', {'reason': text}) else: logger.info("registration successful") # saving hardware info and initial Kano version save_hardware_info() save_kano_version() # running kano-sync after registration logger.info("running kano-sync after successful registration") cmd = '{bin_dir}/kano-sync --sync -s'.format(bin_dir=bin_dir) run_bg(cmd) return_value = 'SUCCEED' self._create_dialog( title=_("Profile activated!"), description=_("Now you can share stuff, build your character, " "and connect with friends.")) self.win.get_window().set_cursor(None) # Close the app if it was successful if return_value == 'SUCCEED': Gtk.main_quit() def _is_username_available(self, name): """ Returns True if username is available, and False otherwise """ # Use the endpoint api.kano.me/users/username/:name success, text, data = request_wrapper( 'get', '/users/username/{}'.format(name), headers=content_type_json) if not success and text.strip() == "User not found": return True elif success: # Username is definitely taken return False else: # Maybe let the user know something went wrong? e.g. if there's no # internet, launch a dialog at this point return False def _create_dialog(self, title, description): # TODO: refactor this kdialog = KanoDialog(title, description, parent_window=self.win) rv = kdialog.run() return rv def _show_error_dialog(self, title, description): # TODO: refactor this kdialog = KanoDialog(title, description, parent_window=self.win) kdialog.run() def _show_username_taken_dialog(self, username): # TODO: refactor this track_data('world-registration-username-taken', {'username': username}) kd = KanoDialog(_("This username is taken!"), _("Try another one"), parent_window=self.win) kd.run() self.data_screen.username.set_text("") self.data_screen.validate_username() self._disable_register_button() self.data_screen.username.grab_focus() def _show_not_internet_dialog(self): # TODO: refactor this kd = KanoDialog(_("You don't have internet"), _("Do you want to connect to WiFi?"), [{ 'label': _("YES"), 'color': 'green', 'return_value': 0 }, { 'label': _("NO"), 'color': 'red', 'return_value': 1 }], parent_window=self.win) response = kd.run() # Close the dialog while Gtk.events_pending(): # TODO: why is this needed? Gtk.main_iteration() if response == 0: subprocess.Popen("sudo kano-wifi-gui", shell=True)
class SetProxy(Gtk.Box): def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.kano_button = KanoButton() self.win = win self.win.set_main_widget(self) self.heading = Heading( _("Proxy"), _("Connect via a friend") ) grid = Gtk.Grid(column_homogeneous=False, column_spacing=10, row_spacing=10) self.kano_button.connect('button-release-event', self.apply_changes) self.kano_button.connect('key-release-event', self.apply_changes) self.win.top_bar.enable_prev() self.win.change_prev_callback(self.go_to_wifi) self.ip_entry = Gtk.Entry() self.ip_entry.props.placeholder_text = _("IP address") self.ip_entry.connect('key-release-event', self.proxy_enabled) self.username_entry = Gtk.Entry() self.username_entry.props.placeholder_text = _("Username") self.username_entry.connect('key-release-event', self.proxy_enabled) self.port_entry = Gtk.Entry() self.port_entry.props.placeholder_text = _("Port") self.port_entry.connect('key-release-event', self.proxy_enabled) self.password_entry = Gtk.Entry() self.password_entry.props.placeholder_text = _("Password") self.password_entry.set_visibility(False) self.password_entry.connect('key-release-event', self.proxy_enabled) password_box = Gtk.Box() password_box.add(self.password_entry) self.checkbutton = Gtk.CheckButton(_("enable proxy")) self.read_config() self.checkbutton.connect('clicked', self.proxy_status) self.checkbutton.set_can_focus(False) bottom_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) bottom_row.pack_start(self.checkbutton, False, False, 0) bottom_row.pack_start(self.kano_button, False, False, 60) bottom_row.set_margin_bottom(30) bottom_row.set_margin_left(70) grid.attach(self.ip_entry, 0, 0, 2, 2) grid.attach(self.username_entry, 0, 2, 2, 2) grid.attach(self.port_entry, 2, 0, 2, 2) grid.attach(password_box, 2, 2, 3, 2) grid_alignment = Gtk.Alignment(xscale=0, xalign=0.5, yscale=0, yalign=0.2) grid_alignment.add(grid) self.pack_start(self.heading.container, False, False, 0) self.pack_start(grid_alignment, True, True, 0) self.pack_end(bottom_row, False, False, 0) self.proxy_status(self.checkbutton) self.kano_button.set_sensitive(False) # Change text of kano button depending on if proxy is enabled if self.checkbutton.get_active(): self.kano_button.set_label(_("ENABLE PROXY")) else: self.kano_button.set_label(_("DISABLE PROXY")) self.win.show_all() def clear_entries(self): self.ip_entry.set_text("") self.username_entry.set_text("") self.port_entry.set_text("") self.password_entry.set_text("") def go_to_wifi(self, widget=None, event=None): self.win.clear_win() SetWifi(self.win) # Update for proxy def read_config(self): self.enable_proxy, data, _ = get_all_proxies() self.enabled_init = self.enable_proxy if self.enable_proxy: try: self.ip_entry.set_text(data['host']) self.port_entry.set_text(data['port']) if data['username']: self.username_entry.set_text(data['username']) if data['password']: self.password_entry.set_text(data['password']) except: # Something went wrong > disable proxy set_all_proxies(False) common.proxy_enabled = False self.enable_proxy = False self.enabled_init = False self.clear_entries() self.checkbutton.set_active(self.enable_proxy) def apply_changes(self, button, event): # If enter key is pressed or mouse button is clicked if not hasattr(event, 'keyval') or event.keyval == 65293: # This is a callback called by the main loop, so it's safe to # manipulate GTK objects: watch_cursor = Gdk.Cursor(Gdk.CursorType.WATCH) self.win.get_window().set_cursor(watch_cursor) self.kano_button.start_spinner() self.kano_button.set_sensitive(False) def lengthy_process(): if self.enable_proxy: host = self.ip_entry.get_text() port = self.port_entry.get_text() username = self.username_entry.get_text() password = self.password_entry.get_text() set_all_proxies(enable=True, host=host, port=port, username=username, password=password) common.proxy_enabled = True success, text = test_proxy() if not success: title = _("Error with proxy") description = text return_value = 1 # disable proxy if we couldn't successfully enable it set_all_proxies(False) common.proxy_enabled = False else: title = _("Successfully enabled proxy") description = "" return_value = 0 else: set_all_proxies(False) common.proxy_enabled = False title = _("Successfully disabled proxy") description = "" return_value = 0 def done(title, description, return_value): kdialog = KanoDialog( title, description, [ { 'label': _("OK"), 'color': 'green', 'return_value': return_value } ], parent_window=self.win ) response = kdialog.run() self.win.get_window().set_cursor(None) self.kano_button.stop_spinner() if response == 0: self.go_to_wifi() elif response == 1: self.checkbutton.set_active(False) self.kano_button.set_sensitive(False) GObject.idle_add(done, title, description, return_value) thread = threading.Thread(target=lengthy_process) thread.start() # Validation functions # If the "enable proxy" checkbox is checked/uncheckout, this function is activated # Disables the text entries if enable proxy is not checked def proxy_status(self, widget): self.enable_proxy = widget.get_active() if self.enable_proxy: self.ip_entry.set_sensitive(True) self.port_entry.set_sensitive(True) self.password_entry.set_sensitive(True) self.username_entry.set_sensitive(True) # Run to see if it need enabling self.proxy_enabled() self.kano_button.set_label(_("ENABLE PROXY")) else: self.ip_entry.set_sensitive(False) self.port_entry.set_sensitive(False) self.password_entry.set_sensitive(False) self.username_entry.set_sensitive(False) self.kano_button.set_label(_("DISABLE PROXY")) self.kano_button.set_sensitive(True) # if proxy enabled: ip address, port are mandatory def proxy_enabled(self, widget=None, event=None): # Get IP address # Get port # Get # If these entries are non empty, good - else, disable the next button ip_text = self.ip_entry.get_text() port_text = self.port_entry.get_text() if ip_text == "" or port_text == "": self.kano_button.set_sensitive(False) return False else: self.kano_button.set_sensitive(True) return True return False
class LoginWithKanoWorldView(Gtk.Grid): def __init__(self, greeter): Gtk.Grid.__init__(self) self.get_style_context().add_class('password') self.set_row_spacing(12) self.greeter = greeter title = Heading(_('Login with Kano World'), _('Enter your Kano World details.')) self.attach(title.container, 0, 0, 1, 1) self.username = Gtk.Entry() self.username.set_placeholder_text('username') self.attach(self.username, 0, 1, 1, 1) self.password = Gtk.Entry() self.password.set_visibility(False) self.password.set_placeholder_text('password') self.attach(self.password, 0, 2, 1, 1) self.login_btn = KanoButton(_('LOGIN')) self.login_btn.connect('clicked', self._btn_login_pressed) self.attach(self.login_btn, 0, 3, 1, 1) def _btn_login_pressed(self, event=None, button=None): ''' Authenticates against Kano World. If successful synchronizes to a local Unix account, and tells lightdm to go forward with local a login. ''' logger.debug('Synchronizing Kano World account') self.login_btn.start_spinner() self.login_btn.set_sensitive(False) t = threading.Thread(target=self._thr_login) t.start() def _thr_login(self): loggedin = False reason = '' # TODO: Disable the "login" button unless these entry fields are non-empty # Collect credentials from the view self.unix_password = self.password.get_text() self.world_username = self.username.get_text() self.unix_username = self.username.get_text() atsign = self.unix_username.find('@') if atsign != -1: # For if we are in "staging" mode (see /etc/kano-world.conf) self.unix_username = self.unix_username[:atsign] # Now try to login to Kano World try: logger.debug('Authenticating user: {} to Kano World'.format(self.username.get_text())) (loggedin, reason) = kano_world_authenticate(self.username.get_text(), self.password.get_text()) logger.debug('Kano World auth response: {} - {}'.format(loggedin, reason)) except Exception as e: reason = str(e) logger.debug('Kano World auth Exception: {}'.format(reason)) pass if not loggedin: # Kano world auth unauthorized # FIXME: Localizing the below string fails with an exception GObject.idle_add(self._error_message_box, 'Failed to authenticate to Kano World', reason) return else: # We are authenticated to Kano World: proceed with forcing local user rc = -1 try: # Create the local unix user, bypass kano-init-flow, login & sync to Kano World createuser_cmd = 'sudo /usr/bin/kano-greeter-account {} {} {}'.format( self.unix_username, self.unix_password, self.world_username) _, _, rc = run_cmd(createuser_cmd) if rc == 0: logger.debug('Local user created correctly: {}'.format(self.unix_username)) elif rc == 1: logger.debug('Local user already exists, proceeding with login: {}'.format(self.unix_username)) created = True except: created = False if not created: logger.debug('Error creating new local user: {}'.format(self.unix_username)) GObject.idle_add(self._error_message_box, "Could not create local user", rc) return # Tell Lidghtdm to proceed with login session using the new user # We bind LightDM at this point only, this minimizes the number of attempts # to bind the Greeter class to a view, which he does not like quite well. logger.debug('Scheduling lightdm authentication in math thread') GObject.idle_add(self._auth_call) def _auth_call(self): logger.debug('Starting lightdm authentication') self._reset_greeter() self.greeter.authenticate(self.unix_username) if self.greeter.get_is_authenticated(): logger.debug('User is already authenticated, starting session') def _reset_greeter(self): # connect signal handlers to LightDM self.cb_one = self.greeter.connect('show-prompt', self._send_password_cb) self.cb_two = self.greeter.connect('authentication-complete', self._authentication_complete_cb) self.cb_three = self.greeter.connect('show-message', self._auth_error_cb) self.greeter.connect_sync() return (self.cb_one, self.cb_two, self.cb_three) def _send_password_cb(self, _greeter, text, prompt_type): logger.debug('Need to show prompt: {}'.format(text)) if _greeter.get_in_authentication(): logger.debug('Sending password to LightDM') _greeter.respond(self.unix_password) def _authentication_complete_cb(self, _greeter): logger.debug('Authentication process is complete') if not _greeter.get_is_authenticated(): logger.warn('Could not authenticate user {}'.format(self.unix_username)) self._auth_error_cb(_('Incorrect password (The default is "kano")')) return logger.info( 'The user {} is authenticated. Starting LightDM X Session' .format(self.unix_username)) set_last_user(self.unix_username) if not _greeter.start_session_sync('lightdm-xsession'): logger.error('Failed to start session') else: logger.info('Login failed') def _auth_error_cb(self, text, message_type=None): logger.info('There was an error logging in: {}'.format(text)) win = self.get_toplevel() win.go_to_users() self.login_btn.stop_spinner() self.login_btn.set_sensitive(True) self.newuser_btn.set_sensitive(True) error = KanoDialog(title_text=_('Error Synchronizing account'), description_text=text, parent_window=self.get_toplevel()) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() def _error_message_box(self, title, description): ''' Show a standard error message box ''' self.login_btn.stop_spinner() self.login_btn.set_sensitive(True) errormsg = KanoDialog(title_text=title, description_text=description, button_dict=[ { 'label': _('OK').upper(), 'color': 'red', 'return_value': True }]) errormsg.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) errormsg.run() # Clean up password field self.password.set_text('') return def grab_focus(self): ''' Clear username and password previous text, and gain focus. ''' self.username.set_text('') self.password.set_text('')
class EditableList(Gtk.Grid): def __init__(self, size_x=400, size_y=150): Gtk.Grid.__init__(self) self.set_row_spacing(10) self.set_column_spacing(10) scroll = ScrolledWindow() scroll.set_size_request(size_x, size_y) scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.edit_list_store = Gtk.ListStore(str) self.edit_list = Gtk.TreeView(self.edit_list_store) self.edit_list.set_headers_visible(False) renderer = Gtk.CellRendererText() renderer.set_property('editable', True) renderer.connect('edited', self._item_edited_handler) renderer.connect('editing-started', self._item_edit_started) renderer.connect('editing-canceled', self._item_edit_canceled) column = Gtk.TreeViewColumn(cell_renderer=renderer, text=0) self.edit_list.append_column(column) self._add_btn = KanoButton(_("ADD")) self._add_btn.connect('button-release-event', self.add) self._rm_btn = KanoButton(_("REMOVE")) self._set_rm_btn_state() self._rm_btn.connect('button-release-event', self.rm) scroll.add_with_viewport(self.edit_list) self.attach(scroll, 0, 0, 2, 1) self.attach(self._add_btn, 0, 1, 1, 1) self.attach(self._rm_btn, 1, 1, 1, 1) def __contains__(self, item): return item in [row[0] for row in self.edit_list_store] def add(self, button, event): self.edit_list_store.append(['']) self.edit_list.grab_focus() row = len(self.edit_list_store) - 1 col = self.edit_list.get_column(0) self.edit_list.set_cursor(row, col, start_editing=True) self._rm_btn.set_sensitive(False) def rm(self, button=None, event=None): selection = self.edit_list.get_selection() dummy, selected = selection.get_selected() if not selected: return self.edit_list_store.remove(selected) self._set_rm_btn_state() def _item_edited_handler(self, cellrenderertext, path, new_text): if new_text is None: # FIXME: the reason for the os.system here is that the 'edited' signal # triggers on a key-pressed-event and the dialog closes on release. So # you would only see the dialog while holding down the 'ENTER' key. title = _("Invalid website given") description = _("\nWe need to make sure the website URL is valid.\n" \ "Please enter the full URL as it appears in your browser.\n\n" \ "Example: http://www.google.com\n") buttons = _("OK:red:1") cmd = 'kano-dialog title="{}" description="{}" buttons="{}" no-taskbar &'.format( title.encode('utf8'), description.encode('utf8'), buttons.encode('utf8')) os.system(cmd) self.rm() else: selection = self.edit_list.get_selection() dummy, selected = selection.get_selected() if new_text and new_text not in self: self.edit_list_store.set_value(selected, 0, new_text) else: row = self.edit_list_store[selected] old_text = row[0] if not old_text: self.rm() self._add_btn.set_sensitive(True) self._set_rm_btn_state() def _item_edit_started(self, *_): self._add_btn.set_sensitive(False) def _item_edit_canceled(self, *_): self._add_btn.set_sensitive(True) self.rm() def _set_rm_btn_state(self): state = len(self.edit_list_store) != 0 self._rm_btn.set_sensitive(state)
class LoginWithKanoWorldView(Gtk.Grid): def __init__(self, greeter): Gtk.Grid.__init__(self) self.get_style_context().add_class('password') self.set_row_spacing(12) self.greeter = greeter title = Heading(_('Login with Kano World'), _('Enter your Kano World details.')) self.attach(title.container, 0, 0, 1, 1) self.username = Gtk.Entry() self.username.set_placeholder_text('username') self.attach(self.username, 0, 1, 1, 1) self.password = Gtk.Entry() self.password.set_visibility(False) self.password.set_placeholder_text('password') self.attach(self.password, 0, 2, 1, 1) self.login_btn = KanoButton(_('LOGIN')) self.login_btn.connect('clicked', self._btn_login_pressed) self.attach(self.login_btn, 0, 3, 1, 1) def _btn_login_pressed(self, event=None, button=None): ''' Authenticates against Kano World. If successful synchronizes to a local Unix account, and tells lightdm to go forward with local a login. ''' logger.debug('Synchronizing Kano World account') self.login_btn.start_spinner() self.login_btn.set_sensitive(False) t = threading.Thread(target=self._thr_login) t.start() def _thr_login(self): loggedin = False reason = '' # TODO: Disable the "login" button unless these entry fields are non-empty # Collect credentials from the view self.unix_password = self.password.get_text() self.world_username = self.username.get_text() self.unix_username = self.username.get_text() atsign = self.unix_username.find('@') if atsign != -1: # For if we are in "staging" mode (see /etc/kano-world.conf) self.unix_username = self.unix_username[:atsign] # Now try to login to Kano World try: logger.debug('Authenticating user: {} to Kano World'.format( self.username.get_text())) (loggedin, reason) = kano_world_authenticate(self.username.get_text(), self.password.get_text()) logger.debug('Kano World auth response: {} - {}'.format( loggedin, reason)) except Exception as e: reason = str(e) logger.debug('Kano World auth Exception: {}'.format(reason)) pass if not loggedin: # Kano world auth unauthorized # FIXME: Localizing the below string fails with an exception GObject.idle_add(self._error_message_box, 'Failed to authenticate to Kano World', reason) return else: # We are authenticated to Kano World: proceed with forcing local user rc = -1 try: # Create the local unix user, bypass kano-init-flow, login & sync to Kano World createuser_cmd = 'sudo /usr/bin/kano-greeter-account {} {} {}'.format( self.unix_username, self.unix_password, self.world_username) _, _, rc = run_cmd(createuser_cmd) if rc == 0: logger.debug('Local user created correctly: {}'.format( self.unix_username)) elif rc == 1: logger.debug( 'Local user already exists, proceeding with login: {}'. format(self.unix_username)) created = True except: created = False if not created: logger.debug('Error creating new local user: {}'.format( self.unix_username)) GObject.idle_add(self._error_message_box, "Could not create local user", rc) return # Tell Lidghtdm to proceed with login session using the new user # We bind LightDM at this point only, this minimizes the number of attempts # to bind the Greeter class to a view, which he does not like quite well. logger.debug('Scheduling lightdm authentication in math thread') GObject.idle_add(self._auth_call) def _auth_call(self): logger.debug('Starting lightdm authentication') self._reset_greeter() self.greeter.authenticate(self.unix_username) if self.greeter.get_is_authenticated(): logger.debug('User is already authenticated, starting session') def _reset_greeter(self): # connect signal handlers to LightDM self.cb_one = self.greeter.connect('show-prompt', self._send_password_cb) self.cb_two = self.greeter.connect('authentication-complete', self._authentication_complete_cb) self.cb_three = self.greeter.connect('show-message', self._auth_error_cb) self.greeter.connect_sync() return (self.cb_one, self.cb_two, self.cb_three) def _send_password_cb(self, _greeter, text, prompt_type): logger.debug('Need to show prompt: {}'.format(text)) if _greeter.get_in_authentication(): logger.debug('Sending password to LightDM') _greeter.respond(self.unix_password) def _authentication_complete_cb(self, _greeter): logger.debug('Authentication process is complete') if not _greeter.get_is_authenticated(): logger.warn('Could not authenticate user {}'.format( self.unix_username)) self._auth_error_cb( _('Incorrect password (The default is "kano")')) return logger.info( 'The user {} is authenticated. Starting LightDM X Session'.format( self.unix_username)) set_last_user(self.unix_username) if not _greeter.start_session_sync('lightdm-xsession'): logger.error('Failed to start session') else: logger.info('Login failed') def _auth_error_cb(self, text, message_type=None): logger.info('There was an error logging in: {}'.format(text)) win = self.get_toplevel() win.go_to_users() self.login_btn.stop_spinner() self.login_btn.set_sensitive(True) self.newuser_btn.set_sensitive(True) error = KanoDialog(title_text=_('Error Synchronizing account'), description_text=text, parent_window=self.get_toplevel()) error.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) error.run() def _error_message_box(self, title, description): ''' Show a standard error message box ''' self.login_btn.stop_spinner() self.login_btn.set_sensitive(True) errormsg = KanoDialog(title_text=title, description_text=description, button_dict=[{ 'label': _('OK').upper(), 'color': 'red', 'return_value': True }]) errormsg.dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) errormsg.run() # Clean up password field self.password.set_text('') return def grab_focus(self): ''' Clear username and password previous text, and gain focus. ''' self.username.set_text('') self.password.set_text('')
class BluetoothDeviceItem(Gtk.Box): __gsignals__ = { 'pairing': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), 'done-pairing': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), } def __init__(self, device): Gtk.Box.__init__(self, hexpand=True) self.device = device device_name = device.name dev_label = Gtk.Label(device_name, hexpand=True) dev_label.get_style_context().add_class('normal_label') self.pack_start(dev_label, False, False, 0) self._pair_button = KanoButton() self._set_paired_button_state() self._pair_button.set_margin_top(10) self._pair_button.set_margin_bottom(10) self._pair_button.set_margin_left(10) self._pair_button.set_margin_right(10) self._pair_button.connect('clicked', self.pair) self.pack_start(self._pair_button, False, False, 0) def _set_paired_button_state(self, *dummy_args, **dummy_kwargs): if not self.device.connected: label = _("PAIR") colour = 'green' else: label = _("UNPAIR") colour = 'red' self._pair_button.set_label(label) self._pair_button.set_color(colour) def error(self, err_msg): KanoDialog(err_msg).run() def pair(self, *dummy_args, **dummy_kwargs): def done_pairing(): self._set_paired_button_state() self.emit('done-pairing') @queue_cb(callback=done_pairing, gtk=True) def do_pair(): if not self.device.fuse(): GObject.idle_add(self.error, _("Pairing failed")) @queue_cb(callback=done_pairing, gtk=True) def do_unpair(): if not self.device.unfuse(): GObject.idle_add(self.error, _("Unpairing failed")) self.emit('pairing') if self.device.connected: pair_fn = do_unpair logger.info("Unpairing {}".format(self.device)) else: pair_fn = do_pair logger.info("Pairing {}".format(self.device)) pair_thr = threading.Thread(target=pair_fn) pair_thr.start()
class OverscanTemplate(Gtk.Box): def __init__(self, win, title, description, original_overscan=None): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.kano_button = KanoButton(_("APPLY CHANGES")) self.kano_button.connect('button-release-event', self.apply_changes) self.kano_button.pack_and_align() self.heading = Heading(title, description) self.pack_start(self.heading.container, False, False, 0) self.win = win self.win.set_main_widget(self) self.win.top_bar.enable_prev() # Launch pipe for the overscan c code launch_pipe() self.overscan_values = get_overscan_status() self.original_overscan = original_overscan # Pass original overscan values between the classes # If original_overscan hasn't been generated yet, get it from current # overscan status. Alternatively, maybe read this from a file in future if original_overscan is None: self.original_overscan = get_overscan_status() # Reset button self.reset_button = OrangeButton() reset_icon_path = os.path.join(common.media, '/Icons/reset.png') reset_image = Gtk.Image().new_from_file(reset_icon_path) self.reset_button.set_image(reset_image) self.reset_button.connect('button_press_event', self.reset) def apply_changes(self, button, event): # NB, write_overscan_values ends the transaction write_overscan_values(self.overscan_values) self.original_overscan = self.overscan_values # Tell user to reboot to see changes common.need_reboot = True self.go_to_display() def adjust(self, adj, varname): self.overscan_values[varname] = int(adj.get_value()) set_overscan_status(self.overscan_values) def adjust_all(self, adj): self.overscan_values['top'] = int(adj.get_value()) self.overscan_values['bottom'] = int(adj.get_value()) self.overscan_values['left'] = int(adj.get_value()) self.overscan_values['right'] = int(adj.get_value()) set_overscan_status(self.overscan_values) def go_to_display(self, widget=None, button=None): self.reset() self.win.clear_win() SetDisplay(self.win) def reset(self, widget=None, event=None): pass
class PasswordScreen(Gtk.Box): def __init__( self, win, wiface, network_name, encryption, wrong_password=False ): ''' Show the screen with the option of adding a password and connecting to a network ''' Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self._win = win self._win.set_main_widget(self) self._win.top_bar.enable_prev() self._wiface = wiface self._network_name = network_name self._encryption = encryption # Keep track if the user has already entered the wrong password before # so that we only pack the "password incorrect" label once self._wrong_password_used_before = False self._heading = Heading( "Connect to the network", self._network_name, self._win.is_plug(), True ) self._heading.set_prev_callback(self._refresh_networks) self._heading.container.set_margin_right(20) self._heading.container.set_margin_left(20) if wrong_password: image_path = os.path.join(img_dir, "password-fail.png") wrong_password = self._create_wrong_password_label() self._heading.container.pack_start(wrong_password, True, True, 0) else: image_path = os.path.join(img_dir, "password.png") self._padlock_image = Gtk.Image.new_from_file(image_path) self._password_entry = Gtk.Entry() self._password_entry.set_placeholder_text("Password") self._password_entry.set_visibility(False) self._password_entry.get_style_context().add_class("password_entry") self._password_entry.set_margin_left(60) self._password_entry.set_margin_right(60) self._password_entry.connect("key-release-event", self._set_button_sensitive) # If Enter key is pressed on the password entry, we want to act as # though the connect_btn was clicked self._password_entry.connect( "key-release-event", self._on_connect_key_wrapper ) self._connect_btn = KanoButton("CONNECT") self._connect_btn.connect('clicked', self._on_connect) self._connect_btn.set_sensitive(False) self._connect_btn.set_margin_right(100) self._connect_btn.set_margin_left(100) self._connect_btn.pack_and_align() self._show_password = Gtk.CheckButton.new_with_label("Show password") self._show_password.get_style_context().add_class("show_password") self._show_password.connect("toggled", self._change_password_entry_visiblity) self._show_password.set_active(True) self._show_password.set_margin_left(100) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(vbox) vbox.pack_start(self._heading.container, False, False, 10) vbox.pack_start(self._padlock_image, False, False, 10) vbox.pack_start(self._password_entry, False, False, 10) vbox.pack_start(self._show_password, False, False, 10) vbox.pack_end(self._connect_btn.align, False, False, 40) # Entry should have the keyboard focus self._password_entry.grab_focus() self.show_all() def _create_wrong_password_label(self): label = Gtk.Label("Password incorrect") label.get_style_context().add_class("wrong_password_label") return label def _change_password_entry_visiblity(self, widget): ''' Depending on the checkbox, change the writing in the password entry to be readable. ''' visibility = self._show_password.get_active() self._password_entry.set_visibility(visibility) def _refresh_networks(self, widget=None): from kano_wifi_gui.RefreshNetworks import RefreshNetworks RefreshNetworks(self._win) def _on_connect_key_wrapper(self, widget, event): if event.keyval == Gdk.KEY_Return: self._on_connect() def _on_connect(self, widget=None): passphrase = self._password_entry.get_text() ConnectToNetwork( self._win, self._network_name, passphrase, self._encryption ) def _set_button_sensitive(self, widget, event): self._connect_btn.set_sensitive(True) def _thread_finish(self, success): if success: self._success_screen() else: self._wrong_password_screen() def _success_screen(self): self._win.remove_main_widget() title = "Success!" description = "You're connected" buttons = [ { "label": "OK", "color": "green", "type": "KanoButton", "callback": Gtk.main_quit } ] img_path = os.path.join(img_dir, "internet.png") self._win.set_main_widget( Template( title, description, buttons, self._win.is_plug(), img_path ) ) def _disable_widgets_start_spinner(self): self._connect_btn.start_spinner() self._connect_btn.set_sensitive(False) self._win.top_bar.prev_button.set_sensitive(False) self._password_entry.set_sensitive(False) self._show_password.set_sensitive(False) def _enable_widgets_stop_spinner(self): self._connect_btn.stop_spinner() self._connect_btn.set_sensitive(True) self._win.top_bar.prev_button.set_sensitive(True) self._password_entry.set_sensitive(True) self._show_password.set_sensitive(True)
class SetWifi(Template): wifi_connection_attempted = False def __init__(self, win): Template.__init__(self, "", _("to be set"), _("COMPLETE")) self.win = win self.win.set_main_widget(self) self.kano_button.connect('button-release-event', self.win.go_to_home) internet_img = Gtk.Image() # Very hacky way to centre the Proxy button - put spaces in the label self.proxy_button = OrangeButton(_("Proxy ")) self.proxy_button.connect('button-release-event', self.go_to_proxy) self.disable_proxy = OrangeButton(_("Disable proxy")) self.win.change_prev_callback(self.win.go_to_home) self.win.top_bar.enable_prev() internet_status = Gtk.Label() internet_status_style = internet_status.get_style_context() internet_status.set_alignment(xalign=1, yalign=0.5) internet_action = Gtk.Label() internet_action_style = internet_action.get_style_context() internet_status_style.add_class('internet_status_top') internet_action_style.add_class('internet_status_bottom') internet_action.set_alignment(xalign=1, yalign=0.5) status_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) status_box.props.valign = Gtk.Align.CENTER configure_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) container.pack_start(status_box, False, False, 2) container.pack_start(internet_img, False, False, 2) self.box.pack_start(container, False, False, 0) network_info_dict = network_info() common.has_internet = is_internet() if not common.has_internet or not network_info_dict: if network_info_dict: description = _("Use the browser to log in or configure proxy") else: description = _("Configure wireless") title = _("Get connected") self.add_connection = KanoButton(_("WIFI")) self.add_connection.connect('button_release_event', self.configure_wifi) # We removed the ability to use keyboard to click, so we also remove ability # to get keyboard focus self.add_connection.set_can_focus(False) # For now, this is removed as the event listener is interefering with the # kano-connect #self.add_connection.connect("key_release_event", self.configure_wifi) status_box.pack_start(self.add_connection, False, False, 0) internet_img.set_from_file(common.media + "/Graphics/Internet-noConnection.png") internet_status.set_text(_("No network found")) self.kano_button.set_label(_("BACK")) status_box.pack_start(configure_container, False, False, 3) go_to_portal_button = OrangeButton(_("Browser Login")) go_to_portal_button.connect('button-press-event', self.cb_launch_browser) configure_container.pack_start(go_to_portal_button, False, False, 0) divider_label = Gtk.Label("|") configure_container.pack_start(divider_label, False, False, 3) configure_container.pack_end(self.proxy_button, False, False, 0) else: self.kano_button.set_label(_("COMPLETE")) status_box.pack_start(internet_status, False, False, 3) status_box.pack_start(internet_action, False, False, 3) status_box.pack_start(configure_container, False, False, 3) network = network_info_dict.keys()[0] ip = network_info_dict[network]['address'] network_text = network_info_dict[network]['nice_name'] internet_img.set_from_file(common.media + "/Graphics/Internet-Connection.png") internet_status.set_text(network_text) internet_action.set_text(ip) go_to_portal_button = OrangeButton(_("Browser Login")) go_to_portal_button.connect('button-press-event', self.cb_launch_browser) configure_container.pack_start(go_to_portal_button, False, False, 0) if network_text == 'Ethernet': title = _("Connection found!") description = _("You're on a wired network") # Change to ethernet image here internet_img.set_from_file(common.media + "/Graphics/Internet-ethernetConnection.png") else: title = _("Connection found!") description = _("You're on a wireless network") divider_label = Gtk.Label("|") configure_container.pack_start(divider_label, False, False, 3) configure_button = OrangeButton(_("Configure")) configure_button.connect('button_press_event', self.configure_wifi) configure_container.pack_start(configure_button, False, False, 0) divider_label = Gtk.Label("|") configure_container.pack_start(divider_label, False, False, 3) configure_container.pack_end(self.proxy_button, False, False, 0) self.title.title.set_text(title) self.title.description.set_text(description) self.win.show_all() def cb_launch_browser(self, control, signal): # start the default browser to visit the default page launch_browser() def go_to_proxy(self, widget, event): self.win.clear_win() SetProxy(self.win) def configure_wifi(self, widget=None, event=None): # If is a mouse click event or key pressed is ENTER if not hasattr(event, 'keyval') or event.keyval == Gdk.KEY_Return: self.kano_button.set_sensitive(True) self.wifi_connection_attempted = True # Blur window self.win.blur() self.win.show_all() # Force blur to be shown while Gtk.events_pending(): Gtk.main_iteration() # Call WiFi config os.system('/usr/bin/kano-wifi-gui') # Refresh window after WiFi Setup self.win.unblur() self.win.clear_win() SetWifi(self.win)
class RegistrationScreen(Gtk.Box): """ """ def __init__(self, win): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.win = win self.win.set_main_widget(self) self.win.set_decorated(True) title = Heading( _("Kano World"), _("Choose a cool name and secure password") ) self.pack_start(title.container, False, False, 0) self.data_screen = GetData() # TODO: refactor this self.data_screen.connect('widgets-filled', self._enable_register_button) self.data_screen.connect('widgets-empty', self._disable_register_button) self.add(self.data_screen) self.register_button = KanoButton(_("JOIN KANO WORLD")) self.register_button.set_sensitive(False) self.register_button.set_margin_top(10) self.register_button.set_margin_left(30) self.register_button.set_margin_right(30) self.register_button.set_margin_bottom(30) self.register_button.connect('clicked', self._on_register_button) self.pack_end(self.register_button, False, False, 0) self.win.show_all() def _enable_register_button(self, widget=None): """ """ self.register_button.set_sensitive(True) def _disable_register_button(self, widget=None): """ """ self.register_button.set_sensitive(False) def _on_register_button(self, widget=None): # TODO: refactor this """ """ if not is_internet(): self._show_not_internet_dialog() return # Get the username, password and birthday data = self.data_screen.get_widget_data() email = data['email'] username = data['username'] # Validate that the email address format is correct email_error = validate_email(email) if email_error: self._show_error_dialog(_("Incorrect Email address"), email_error) return if not self._is_username_available(username): self._show_username_taken_dialog(username) return # We can save the username to kano-profile # Don't save password as this is private self.data_screen.save_username_and_birthday() # TODO: rename this self.data_screen.cache_emails() data = self.data_screen.get_widget_data() # This means no threads are needed. while Gtk.events_pending(): # TODO: why is this needed? Gtk.main_iteration() # Try and register the account on the server password = data['password'] success, text = register_(email, username, password, marketing_enabled=True) # This should no longer be needed, since this is checked in the first # screen. However there is a small chance someone could take the # username while the user is in the process of registering if not success: if text.strip() == _("Cannot register, problem: " "Username already registered"): self._show_username_taken_dialog(username) else: logger.info("problem with registration: {}".format(text)) return_value = 'FAIL' self._create_dialog( title=_("Houston, we have a problem"), description=str(text) ) track_data('world-registration-failed', {'reason': text}) else: logger.info("registration successful") # saving hardware info and initial Kano version save_hardware_info() save_kano_version() # running kano-sync after registration logger.info("running kano-sync after successful registration") cmd = '{bin_dir}/kano-sync --sync -s'.format(bin_dir=bin_dir) run_bg(cmd) return_value = 'SUCCEED' self._create_dialog( title=_("Profile activated!"), description=_("Now you can share stuff, build your character, " "and connect with friends.") ) self.win.get_window().set_cursor(None) # Close the app if it was successful if return_value == 'SUCCEED': Gtk.main_quit() def _is_username_available(self, name): """ Returns True if username is available, and False otherwise """ # Use the endpoint api.kano.me/users/username/:name success, text, data = request_wrapper( 'get', '/users/username/{}'.format(name), headers=content_type_json ) if not success and text.strip() == "User not found": return True elif success: # Username is definitely taken return False else: # Maybe let the user know something went wrong? e.g. if there's no # internet, launch a dialog at this point return False def _create_dialog(self, title, description): # TODO: refactor this kdialog = KanoDialog( title, description, parent_window=self.win ) rv = kdialog.run() return rv def _show_error_dialog(self, title, description): # TODO: refactor this kdialog = KanoDialog( title, description, parent_window=self.win ) kdialog.run() def _show_username_taken_dialog(self, username): # TODO: refactor this track_data('world-registration-username-taken', {'username': username}) kd = KanoDialog( _("This username is taken!"), _("Try another one"), parent_window=self.win ) kd.run() self.data_screen.username.set_text("") self.data_screen.validate_username() self._disable_register_button() self.data_screen.username.grab_focus() def _show_not_internet_dialog(self): # TODO: refactor this kd = KanoDialog( _("You don't have internet"), _("Do you want to connect to WiFi?"), [ { 'label': _("YES"), 'color': 'green', 'return_value': 0 }, { 'label': _("NO"), 'color': 'red', 'return_value': 1 } ], parent_window=self.win ) response = kd.run() # Close the dialog while Gtk.events_pending(): # TODO: why is this needed? Gtk.main_iteration() if response == 0: subprocess.Popen("sudo kano-wifi-gui", shell=True)