def __init__(self): DialogBox.__init__(self) # Use this opportunity to set the dialog's caption. self.setText("About the Mail Sample") # Create a DockPanel to contain the 'about' label and the 'OK' button. outer = DockPanel() outer.setSpacing(4) outer.add(Image(AboutDialog.LOGO_IMAGE), DockPanel.WEST) # Create the 'OK' button, along with a listener that hides the dialog # when the button is clicked. Adding it to the 'south' position within # the dock causes it to be placed at the bottom. buttonPanel = HorizontalPanel() buttonPanel.setHorizontalAlignment(HasAlignment.ALIGN_RIGHT) buttonPanel.add(Button("Close", self)) outer.add(buttonPanel, DockPanel.SOUTH) # Create the 'about' label. Placing it in the 'rest' position within the # dock causes it to take up any remaining space after the 'OK' button # has been laid out. textplain = "This sample application demonstrates the construction " textplain += "of a complex user interface using pyjamas' built-in widgets. Have a look " textplain += "at the code to see how easy it is to build your own apps!" text = HTML(textplain) text.setStyleName("mail-AboutText") outer.add(text, DockPanel.CENTER) # Add a bit of spacing and margin to the dock to keep the components from # being placed too closely together. outer.setSpacing(8) self.setWidget(outer)
def __init__(self): DialogBox.__init__(self) self.setText("Sample DialogBox with embedded Frame") iframe = Frame(Popups.baseURL() + "rembrandt/LaMarcheNocturne.html") closeButton = Button("Close", self) msg = HTML("<center>This is an example of a standard dialog box component.<br> You can put pretty much anything you like into it,<br>such as the following IFRAME:</center>", True) dock = DockPanel() dock.setSpacing(4) dock.add(closeButton, DockPanel.SOUTH) dock.add(msg, DockPanel.NORTH) dock.add(iframe, DockPanel.CENTER) dock.setCellHorizontalAlignment(closeButton, HasAlignment.ALIGN_RIGHT) dock.setCellWidth(iframe, "100%") dock.setWidth("100%") iframe.setWidth("36em") iframe.setHeight("20em") self.setWidget(dock)
def edit_password(net_id): conf_fail = False try: conf_data = read_conf_data.read_data() except: conf_data = {} conf_fail = True ssid = wpa_cli.get_network(net_id, "ssid") conf = conf_data.get(ssid, None) if not conf: if conf_fail: Printer( "Configuration file can't be read, can't get current password!", i, o) else: Printer( "Network not found in the configuration file, can't get current password!", i, o) psk = "" else: psk = conf.get("psk", "") if not psk: if conf.get("key_mgmt", None) == "NONE": result = DialogBox("yn", i, o, message="Is open, edit?").activate() if not result: return psk = "" else: # weird, no psk in file # using an empty string for now psk = "" input = UniversalInput(i, o, value=psk, message="Password:"******"WiFi password enter UI element") password = input.activate() if password is None: return False wpa_cli.set_network(net_id, 'psk', '"{}"'.format(password)) wpa_cli.save_config() Printer("Password entered", i, o, 1)
def confirm_exit(self): with self.moving: if self.game.get_game_state() == 'not over': choices = ["n", ["Restart", "restart"], "y"] else: choices = ["y", ["Restart", "restart"], "n"] choice = DialogBox(choices, self.i, self.o, message="Exit the game?").activate() if choice == "restart": self.start_new_game() self.set_keymap() self.refresh() elif choice is True: self.do_exit.set() else: self.set_keymap() self.refresh()
def test_start_option(self): db = DialogBox("ync", get_mock_input(), get_mock_output(), name=db_name) db.refresh = lambda *args, **kwargs: None db.set_start_option(1) #So, "No" option would be selected def scenario(): db.accept_value() # KEY_ENTER assert not db.in_foreground # Should exit with patch.object(db, 'idle_loop', side_effect=scenario) as p: return_value = db.activate() assert return_value is False #Second element - No, means False
def update(): Printer("Updating...", i, o, 1) try: output = check_output(["git", "pull"]) if "Already up-to-date." in output: Printer("All up-to-date", i, o, 1) else: Printer("Updated ZPUI", i, o, 1) needs_restart = DialogBox('yn', i, o, message="Restart the UI?").activate() if needs_restart: os.kill(os.getpid(), signal.SIGTERM) except OSError as e: if e.errno == 2: Printer("git not found!", i, o, 1) else: Printer("Unknown OSError!", i, o, 1) except CalledProcessError as e: if e.returncode == 1: Printer( ["Can't connect", "to GitHub?"], i, o, 1 ) #Need to check output - can also be "Conflicting local changes" and similar else: Printer(["Failed with", "code {}".format(e.returncode)], i, o, 1)
def game_loop(self): self.set_keymap() self.refresh() while self.game.get_game_state( ) == 'not over' and not self.do_exit.isSet(): sleep(1) if self.do_exit.isSet(): return #Waiting for player to click any of five primary keys #Then, prompting to restart the game eh = ExitHelper(self.i, keys=self.i.reserved_keys).start() while eh.do_run(): sleep(0.1) do_restart = DialogBox("ync", self.i, self.o, message="Restart the game?").activate() if do_restart is None: #Cancel, leaving the playing field as-is return elif do_restart is False: #No, not restarting, thus exiting the game self.do_exit.set() else: self.start_new_game( ) #Yes, restarting (game_loop will be entered once more from on_start() )
def callback(): logger.info(DialogBox('ync', i, o, message="It's working?").activate()) logger.info((DialogBox('yyy', i, o, message="Isn't it beautiful?").activate())) logger.info((DialogBox([["Yes", True], ["Absolutely", True]], i, o, message="Do you like it").activate()))
class NPC: def __init__(self, renderer, factory, json_data): self.dialog_timer = Timer(10000) self.close_dialog_timer = Timer(10000) self.db = DataBase() self.renderer = renderer self.factory = factory data = json.loads(json_data) self.name = data["name"] self.start_pos = data["start_pos"] self.npc_data = self.db.get_npc(self.name) self.level = self.npc_data["level"] self.quest = self.npc_data["quest"] self.sprite_size = 128 self.position = [0, 0] self.movement = [0, 0] self.moving = False self.npc_sprites = [ RESOURCES.get_path("{0}_standing.png".format(self.name)), RESOURCES.get_path("{0}_walking.png".format(self.name)) ] self.sprite_sheets = {} self.sprites = [] self.facing = Facing.LEFT_DOWN self.last_facing = self.facing self.motion_type = MotionType.STANDING self.last_motion_type = self.motion_type self.frame_index = 0 self.walk_frames = 60 self.move_rate = 100 self.init_sprite_sheet() self.dialogs = self.db.get_npc_dialog(self.name) self.dialog_box = None self.dialog_sprites = [] self.text = None def init_sprite_sheet(self): for motion_type in range(MotionType.COUNT): self.load_image(self.npc_sprites[motion_type], motion_type) def load_image(self, file_path, motion_type): sprite_sheets = self.sprite_sheets.get(file_path) if not sprite_sheets: sprite_surface = self.factory.from_image(file_path) self.sprite_sheets[motion_type] = sprite_surface def update(self, position, elapsed_time): self.position = position self.sprites.clear() self.frame_index += 1 if self.frame_index == (self.sprite_sheets[self.motion_type].size[0] / self.sprite_size): self.frame_index = 0 if not self.moving: move = dice(self.move_rate) if move[0] == self.move_rate - 1: self.moving = True self.walk_frames = 60 facing = dice(Facing.COUNT - 1) self.facing = facing[0] if self.moving: if self.walk_frames: self.motion_type = MotionType.WALKING if self.facing == 0: self.movement[0] -= 2 self.movement[1] += 1 self.facing = Facing.LEFT_DOWN elif self.facing == 1: self.movement[1] += 1 self.facing = Facing.DOWN elif self.facing == 2: self.movement[0] += 2 self.movement[1] += 1 self.facing = Facing.RIGHT_DOWN elif self.facing == 3: self.movement[0] += 2 self.facing = Facing.RIGHT elif self.facing == 4: self.movement[0] += 2 self.movement[1] -= 1 self.facing = Facing.RIGHT_UP elif self.facing == 5: self.movement[1] -= 1 self.facing = Facing.UP elif self.facing == 6: self.movement[0] -= 2 self.movement[1] -= 1 self.facing = Facing.LEFT_UP elif self.facing == 7: self.movement[0] -= 2 self.facing = Facing.LEFT self.walk_frames -= 1 else: self.moving = False self.motion_type = MotionType.STANDING x = int((int(self.start_pos[0]) + self.position[0] + self.movement[0]) - (self.sprite_size / 2)) y = int((int(self.start_pos[1]) + self.position[1] + self.movement[1]) - (self.sprite_size / 2)) self.position = x, y sprite_sheet = self.sprite_sheets[self.motion_type] sprite_crop = [self.frame_index * self.sprite_size, self.facing * self.sprite_size, self.sprite_size, self.sprite_size] sprite = sprite_sheet.subsprite(sprite_crop) sprite.position = self.position self.sprites.append(sprite) """ renderer = self.renderer src_rect = SDL_Rect() src_rect.x = frame_index * sprite_size src_rect.y = facing * sprite_size src_rect.w = sprite_size src_rect.h = sprite_size dest_rect = SDL_Rect() dest_rect.x = x dest_rect.y = y dest_rect.w = sprite_size dest_rect.h = sprite_size SDL_RenderCopy(renderer, sprite.texture, src_rect, dest_rect) """ def get_sprites(self): return self.sprites def dialog_update(self): self.dialog_timer.update() self.close_dialog_timer.update() if self.dialog_timer.check(): self.dialog_timer.reset() self.close_dialog_timer.activate() self.create_dialog() if self.close_dialog_timer.check(): self.close_dialog_timer.reset() self.dialog_timer.activate() self.dialog_box = None self.dialog_sprites.clear() def create_dialog(self): self.text = dice(len(self.dialogs) - 1) name = self.dialogs[self.text[0]]['npc'] text = self.dialogs[self.text[0]]['text'] message = {0: "{0}:".format(name)} max_chars = 24 i = 1 for j in range(0, len(text), max_chars): message[i] = text[j:j + max_chars] i += 1 self.dialog_box = DialogBox(self.factory, font_size=32, fg_color=Colors.WHITE, bg_color=Colors.BLACK, font_name="04B_20__.TTF", text=message, position=self.position, renderer=self.renderer) sprites = self.dialog_box.get_sprites() for sprite in sprites: self.dialog_sprites.append(sprite)
def wifi_connect_wizard(): global current_interface, wifi_connect_status_cb # picking a wireless interface to go with # needed on i.e. RPi3 to avoid the p2p-dev-wlan0 stuff # thanks Raspbian developers, you broke a lot of decent WiFi setup tutorials # even if by accident =( # also needed to support proper multi-interface work for the app answer = DialogBox("yn", i, o, message="Connect to WiFi?").activate() if not answer: Printer( "Please connect to WiFi later on so that ZPUI and system can be updated - or add some other connectivity now.", i, o, 3, skippable=True) return None winterfaces = pyw.winterfaces() if not winterfaces: Printer("No wireless cards found, can't configure WiFi!", i, o, 3, skippable=True) return False current_interface = winterfaces[0] # Simple, I know # Might add some ZP-specific logic here later, so that # i.e. the ESP-12 based WiFi is guaranteed to be the first # Testing if we actually can connect try: wpa_cli.set_active_interface(current_interface) except OSError as e: if e.errno == 2: Printer("wpa_cli not found, exiting", i, o, 3, skippable=True) else: logger.exception( "Exception while using wpa_cli to set active interface to {}". format(current_interface)) return False except wpa_cli.WPAException: Printer("Can't find any wireless cards. Do you have wireless cards?", i, o, 3, skippable=True) return False start_monitor() # setting up the status collection callback def wifi_connect_status_cb(status): global wifi_connect_last_status wifi_connect_last_status = status print(status) connected = Event() start_scan_thread(connected) show_scan_results(activate_spinner=True) connected.set() stop_monitor() wifi_connect_status_cb = None if wifi_connect_last_status: return wifi_connect_last_status["connected"] return False
def usb_prepare(): """Makes a SJM drive from pretty much any USB drive.""" block_devices = get_block_devices() #Raspberry Pi-specific filtering block_devices = { k: v for k, v in block_devices.items() if not k["name"].startswith("/dev/mmcblk0") } #Filtering for SJM partitions - we don't need to prepare partitions we've already prepared if not block_devices: pylprint("No suitable drives found!") return else: pylprint("Select a drive to prepare") lb_contents = [[ "{}: {}".format(drive, pretty_part_size(drive["size"])), drive ] for drive in block_devices] current_partition = Listbox(lb_contents, i, o, "Drive selection listbox").activate() if not current_partition: pylprint("Aborting!") return #Now working on the block device selected bd_path = selected_bd["path"] pylprint( "All the data will be removed from the drive! Do you wish to proceed?") answer = DialogBox("nyc", i, o, "Proceed").activate() if not answer: pylprint("Aborting!") return try: #If block device size <= 1GB (plus a little bit for all those different sizes) if selected_bd["blocks"] < 1200000: #Delete all partitions #Create one EXT3 partition for the whole disk call([os.path.join(base_dir, "scripts/make_data.sh")]) else: #Delete all partitions #Create 1GB EXT3 partition #Create one FAT partition for the whole remaining disk call([os.path.join(base_dir, "scripts/make_data_and_storage.sh")]) except CalledProcessError as e: #A called script returned an error, let's process it code = e.returncode with open([os.path.join(base_dir, "scripts/errorcodes.json")]) as f: ecode_dict = json.load(f) emessage, critical = ecode_dict[str(code)] pylprint(emessage) if critical: return 1 #Getting the new block device list and checking if the block devices got created new_bd = {bd["path"]: bd for bd in get_block_devices()}[bd_path] if not len(new_bd["partitions"]): pylprint("Unknown error - partitions didn't get created!") return [value[0] for value in values] first_partition_path = new_bd["partitions"][0]["path"] #Mark the first partition as SJM try: libmerlin.mark_partition_as_sjm(first_partition_path) except IOError: pylprint("Unknown error while marking the partition!") return #Ask if the user wants to help ICeeData if not config["never_send_os_files"]: #If he/she does, add the "get FS image" script pylprint( "Agree to help ICeeData by sending us OS files from the base station?" ) answer = Dialog([["Yes", True], ["No", False], ["Never", "fuckno"]], i, o, "Share files?").activate() if answer is True: Printer("", i, o, 0.1) libmerlin.add_autopwn_to_partition(first_partition_path) elif answer == "fuckno": config["never_send_os_files"] = True write_config() pylprint("Successfully prepared the flash drive!")
def suggest_restart(self): needs_restart = DialogBox('yn', i, o, message="Restart ZPUI?").activate() if needs_restart: os.kill(os.getpid(), signal.SIGTERM)
def option_switch_dialog(option): answer = DialogBox([["On", 'o'], ["Off", 'u'], ["Toggle", "t"]], i, o, message=option.capitalize()+":", name="MOCP {} option control dialog".format(option)).activate() if answer: mocp_switch_option(answer, option)
def test_constructor(self): """tests constructor""" db = DialogBox([["Option", "option"]], get_mock_input(), get_mock_output(), name=db_name) self.assertIsNotNone(db)
def del_history_entry(num): answer = DialogBox("yn", i, o, message="Delete entry?").activate() if answer: config["history"].pop(num) save_config(config)
def do_firstboot(self): firstboot_actions = cm.am.get_firstboot_actions() # Skipping actions that shouldn't be run in an emulator if is_emulator(): firstboot_action_names = [ name for name, action in firstboot_actions.items() if not action.not_on_emulator ] else: firstboot_action_names = list(firstboot_actions.keys()) firstboot_file, is_new_file = self.get_firstboot_file() if firstboot_file is None: logger.error( "Can't read/create a firstboot file, no sense for the firstboot application to continue" ) return completed_action_names = [] failed_action_names = [] skipped_action_names = [] if not is_new_file: completed_action_names = [] completed_actions = self.get_completed_actions_from_file( firstboot_file) for action in completed_actions: if isinstance(action, basestring): completed_action_names.append(action) elif isinstance(action, dict): action_name = action["action"] action_result = action["status"] if action_result == "success": # If action has been recorded as failed/skipped before and later was marked as successful # we don't need to count the fails/skips in while action_name in skipped_action_names: skipped_action_names.remove(action_name) while action_name in failed_action_names: failed_action_names.remove(action_name) completed_action_names.append(action_name) elif action_result == "fail": # if action failed before, it being skipped before/after is irrelevant failed_action_names.append(action_name) while action_name in skipped_action_names: skipped_action_names.remove(action_name) elif action_result == "skip": if action_name not in failed_action_names: skipped_action_names.append(action_name) non_completed_action_names = [ n for n in firstboot_action_names if n not in completed_action_names and n not in skipped_action_names ] # print(non_completed_action_names, skipped_action_names, failed_action_names, completed_action_names) if non_completed_action_names: if is_new_file or (not completed_action_names and not skipped_action_names and not failed_action_names): # first boot, no info whatsoever yet message = "Let's go through first boot setup!" elif not completed_action_names and not failed_action_names: # Not the first boot - some actions have been completed before message = "New setup actions for your ZP found!" elif failed_action_names and set(completed_action_names) == set( failed_action_names ): # Some actions have not been successfully completed message = "Want to retry failed first boot actions?" else: # ought to make a truth table, I guess message = "New setup actions for your ZP found!" if not self.context.request_exclusive(): logger.error("Can't get an exclusive context switch, exiting") return choice = DialogBox('yn', self.i, self.o, message=message, name="Firstboot wizard setup menu").activate() if not choice: self.context.rescind_exclusive() return else: # User confirmed that they want to go through with the firstboot wizard # Let's sort the actions and resolve their dependencies # For that, we need some storage variables. # Here, we store actions by their fullname, sorted in order to # resolve the dependency problems sorted_actions = OrderedDict() # Here, we store lists of actions that depend on some other action, # sorted by the execution order (after resolving the dependencies) action_dependants = {} # Here, we store action fullnames (provider+separator+name) # by their short names (just action name, no 'provider' appended) # because short names are used in dependencies. action_fullname_by_name = {} def get_prov_and_name(action_fullname): return action_fullname.split(cm.am.action_name_delimiter, 1) # First, creating a lookup table for looking up dependencies for action_fullname in firstboot_action_names: _, action_name = get_prov_and_name(action_fullname) action_fullname_by_name[action_name] = action_fullname # Then, compiling the list of actions that depend on other action for action_fullname in non_completed_action_names: _, action_name = get_prov_and_name(action_fullname) action = firstboot_actions[action_fullname] if action.depends: has_unresolved_dependencies = False for dependency in action.depends: dep_fullname = action_fullname_by_name.get( dependency, None) if dep_fullname is None: logger.error( "Dependency {} for action {} is not found!" .format(dep_fullname, action_name)) continue if dep_fullname in non_completed_action_names: has_unresolved_dependencies = True # dependency hasn't been completed yet if dep_fullname in action_dependants: action_dependants[dep_fullname].append( action_fullname) else: action_dependants[dep_fullname] = [ action_fullname ] else: logger.info( "Dependency {} (for action {}) is already completed!" .format(dep_fullname, action_name)) if not has_unresolved_dependencies: # No non-completed dependencies have been found # so, we can just add the action to the list sorted_actions[action_fullname] = action else: # Action doesn't depend on anything, just adding it to the list sorted_actions[action_fullname] = action # This code untangles an arbitrarily long chain of dependencies # except, well, circular dependencies actions_involved_in_dependencies = [] if action_dependants: original_dependants = copy(action_dependants) logger.info( "Resolving dependencies: {}".format(action_dependants)) while action_dependants: all_dependency_actions = action_dependants.keys() all_dependent_actions = flatten( action_dependants.values()) independent_dependencies = [ n for n in all_dependency_actions if n not in all_dependent_actions ] if not independent_dependencies: logger.error( "No independent dependencies found while resolving dependencies: {} (original: {})!" .format(action_dependants, original_dependants)) return for action_fullname in independent_dependencies: if action_fullname not in sorted_actions: sorted_actions[ action_fullname] = firstboot_actions[ action_fullname] actions_involved_in_dependencies.append( action_fullname) action_dependants.pop(action_fullname) # The while() has run its course and the dependencies have been linearized all_dependent_actions = flatten( original_dependants.values()) all_unadded_dependent_actions = [n for n in all_dependent_actions \ if n not in sorted_actions] for action_fullname in all_unadded_dependent_actions: sorted_actions[action_fullname] = firstboot_actions[ action_fullname] actions_involved_in_dependencies.append( action_fullname) logger.info("Dependencies resolved!") # Sorting actions for consistent firstboot experience sorted_actions = self.sort_actions_by_ordering( sorted_actions, actions_involved_in_dependencies) # Now, executing actions one-by-one failed_actions = [] log_completed_action_has_failed = False for action_fullname, action in sorted_actions.items(): if action.depends: if any([d in failed_actions for d in action.depends]): logger.error( "Not executing action {} because some of its dependencies ({}) are among failed dependencies: {}" .format(action_fullname, action.depends, failed_actions)) continue action_provider, action_name = get_prov_and_name( action_fullname) if action.will_context_switch: self.context.request_switch(action_provider, start_thread=False) try: result = action.func() except: logger.exception("Action {} failed to execute!".format( action_fullname)) failed_actions.append(action_name) else: if result is False: # Action failed internally failed_actions.append(action_name) status = { False: "fail", True: "success", None: "skip" }.get(result, "success") action_dict = { "action": action_fullname, "status": status } action_result = json.dumps(action_dict) try: with open(firstboot_file, 'a') as f: f.write(action_result + '\n') except: # Avoid cluttering the logs - logger.exception writes the entire traceback into logs # while logger.error just writes the error message if not log_completed_action_has_failed: logger.exception( "Can't write action {} into firstboot logfile {}!" .format(action_fullname, firstboot_file)) log_completed_action_has_failed = True else: logger.error( "Can't write action {} into firstboot logfile {}!" .format(action_fullname, firstboot_file)) self.context.request_switch()
def remove_file(file): choice = DialogBox("yn", i, o, message="Remove from list?", name="Bugreport app custom file picker remove confirmation DialogBox").activate() if choice: file_list.remove(file)
class Menu: def __init__(self, window, world, renderer, factory): self.window = window self.renderer = renderer self.world = world self.factory = factory self.rsystem = factory.create_sprite_render_system(window) self.menu_bg = RESOURCES.get_path("menu_bg.png") self.menu_cursor = RESOURCES.get_path("menu_cursor.png") self.running = True self.position = 460, 340 self.cursor_start_position = 370, 330 self.cursor_position = 0 self.cursor_sprite_size = 32 self.background_sprite = self.factory.from_image(self.menu_bg) self.cursor_sprite = self.factory.from_image(self.menu_cursor) self.text = {0: "START", 1: "OPTIONS", 2: "EXIT"} self.dialog = DialogBox(self.factory, font_size=32, fg_color=Colors.WHITE, bg_color=Colors.BLACK, font_name="04B_20__.TTF", text=self.text, position=self.position, renderer=self.renderer) self.sprites = [self.background_sprite] sprites = self.dialog.get_sprites() for sprite in sprites: self.sprites.append(sprite) self.sprites.append(self.cursor_sprite) def __del__(self): SDL_Quit() def update(self, elapsed_time): self.cursor_sprite.position = self.cursor_start_position[0], self.cursor_start_position[1] \ + self.cursor_position * self.cursor_sprite_size def run(self): menu_input = Input() last_update_time = SDL_GetTicks() # units.MS while self.running: start_time = SDL_GetTicks() # units.MS menu_input.begin_new_frame() menu_events = get_events() for event in menu_events: if event.type == SDL_KEYDOWN: menu_input.key_down_event(event) elif event.type == SDL_KEYUP: menu_input.key_up_event(event) elif event.type == SDL_QUIT: self.running = False break # Exit if menu_input.was_key_pressed(SDLK_ESCAPE): self.running = False # Move the cursor elif menu_input.was_key_pressed(SDLK_UP): if self.cursor_position != 0: self.cursor_position -= 1 elif menu_input.was_key_pressed(SDLK_DOWN): if self.cursor_position != 2: self.cursor_position += 1 # Select option elif menu_input.was_key_pressed(SDLK_RETURN): self.running = False if self.cursor_position == 0: self.launch_game() current_time = SDL_GetTicks() # units.MS elapsed_time = current_time - last_update_time # units.MS self.update(min(elapsed_time, MAX_FRAME_TIME)) last_update_time = current_time self.renderer.render(self.sprites) # This loop lasts 1/60th of a second, or 1000/60th ms ms_per_frame = 1000 // FPS # units.MS elapsed_time = SDL_GetTicks() - start_time # units.MS if elapsed_time < ms_per_frame: SDL_Delay(ms_per_frame - elapsed_time) def launch_game(self): game = Game(self.world, self.window, self.renderer, self.factory) game.run() self.running = True self.run()