class ClipLauncher(object): event_loop = None tracks = [] transport = None def __init__(self, tracks=None, tempo=None): self.main_loop = MainLoop(widget=None) self.osc = OscUI(self) self.transport = JACKOSCKlickTransport(tempo, osc=self.osc.server) self.tracks = tracks or self.tracks self.urwid = UrwidUI(self) self.main_loop.widget = self.urwid def start(self): try: self.main_loop.run() except: self.main_loop.stop() exc_type, value, tb = exc_info() print( "\nLooks like ClipLauncher has encountered an error :/" + "\nHere's a chance to clean up and/or see what's going on.\n") print_exc() post_mortem(tb)
class Window: def __init__(self, body=SolidFill(), header=None, footer=None, focus_part=None, unhandled_input=None, palette=None): self.document = Frame(body, header, footer, focus_part) self.loop = MainLoop( self.document, palette, event_loop=AsyncioEventLoop(loop=asyncio.get_event_loop()), unhandled_input=unhandled_input ) self.loop.screen.set_terminal_properties(colors=256) self.body = body self.footer = footer self.header = header def run(self): self.loop.run() def exit(self): raise ExitMainLoop() def set_state(self, key, value): self.state[key] = value def get_state(self, key): try: return self.state[key] except KeyError: return None
def main(): kern = QueueKernelManager() kern.start_kernel() kern.start_channels() queue = kern.rcvd_queue widget = InterpreterWidget() screen = PagerScreen() interp = IpyInterpreter(widget, screen, kern) eventloop = IpyEventLoop(queue, interp) mainloop = MainLoop(widget, unhandled_input = interp.handle_input, event_loop = eventloop) try: mainloop.run() except: try: kern.stop_channels() kern.kill_kernel() except RuntimeError: pass raise
def menu(): hello = Text( "Приветствую! Для продолжения настройте параметры лабиринта.\n" + "Если карта лабиринта будет некорректно отображаться, " + "попробуйте уменьшить значение ширины или развернуть окно.") height_enter = IntEdit("Высота лабиринта: ", 30) width_enter = IntEdit("Ширина лабиринта: ", 45) done = Button("Готово") done_pad = Padding(done, align="center", width=10) back = AttrMap(SolidFill("\u25E6"), "blueprint") pile = Pile( [hello, Divider("\u2500"), height_enter, width_enter, done_pad]) menu = Filler(LineBox(pile)) main_widget = Overlay(menu, back, align="center", width=35, height=12, valign="middle") loop = MainLoop(main_widget, palette) connect_signal(done, 'click', start_game) loop.run() return MazeGame(height_enter.value(), width_enter.value())
def main(): background = AttrMap(SolidFill(' '), 'basic') pwdialog = PasswordDialog().compose() box = AttrMap(LineBox(pwdialog), 'blackongrey') window = Overlay(box, background, 'center', 30, 'middle', 10) mainloop = MainLoop(window, unhandled_input=callback, palette=simple_colours) mainloop.run()
class Application(object): def __init__(self): pass def run(self): """Run commander""" self.parse_args() self.init_logger() self.create_ui() self.create_remote_api() self._run() self.finalize() def parse_args(self): print("Parsing args...") def init_logger(self): print("Initiating logger...") def create_ui(self): print("Creating user interface...") self.main_window = MainWindow(self) self.login_window = LoginWindow(self, get_user=self.get_user) def create_remote_api(self): print("Creating remote api ...") def _run(self): self.loop = MainLoop(self.main_window, input_filter=self.input_filter) self.login_window.show() self.loop.run() def exit(self): raise ExitMainLoop() def finalize(self): print("Finalizing") def get_user(self, username, password): if username == "18" and password == "123": return True return False def input_filter(self, keys, raw): if 'f10' in keys: self.exit() return keys
def top(period=1, get_state=get_demo_state): """Display process information. Arguments: - period (float) : update period - get_state (callable) : function to generate state information """ engine_table = Columns([]) process_table = Columns([]) def make_text(title, attr="", align="left"): """Create a Text object with given content, style and alignment.""" return Text((attr, " %s" % title), align=align) def make_separator(): """Create a separator.""" return make_text("") def update(): engines, processes = get_state() engine_table.contents = get_engine_table_content(engines) process_table.contents = get_process_table_content([header] + processes) items = [ make_separator(), make_text("Process Viewer", attr="section", align="center"), make_text("Engine Usage:"), LineBox(engine_table, **{arg: " " for arg in linebox_args}), make_text("Process Table:"), make_separator(), LineBox(process_table), make_separator(), make_text("Press (q) to quit.") ] def on_timer(loop, user_data): update() loop.set_alarm_in(period, on_timer) def on_key(key): if key in ('q', 'Q'): raise ExitMainLoop() loop = MainLoop(get_padded(items), palette, unhandled_input=on_key) on_timer(loop, None) # Start timer loop.run()
class Application(object): allowed_colors = (1, 16, 88, 256) def __init__(self, appname='nobix'): self.appname = appname self.conf = ConfigParser() def run(self, args=None): """Main entry point for application""" if args is None: args = sys.argv[1:] self.configure() self.init_ui() self.main_loop() def configure(self): self.conf.read('nobix.cfg') def init_ui(self): colors = self.conf.getint("ui", "colors") skins_dir = self.conf.get("paths", "skins") if colors not in self.allowed_colors: self.stop() self.window = MainWindow(self) def main_loop(self): tmp_palette = [ ('mainbody', 'white', 'dark blue'), ('menubar', 'black', 'light gray'), ('statusbar', 'white', 'dark cyan'), ] self.loop = MainLoop(self.window, palette=tmp_palette, unhandled_input=self.unhandled_input) self.loop.run() def quit(self): raise ExitMainLoop() def unhandled_input(self, key): if key in ('q', 'Q'): self.quit()
def main(): # optional param # rows p = ProcessTable() r = ResourceManager() # height is num_cpus rows + mem row + swap row + empty row r_height = len(r.cpu_meters) + 3 frame = Frame(p, header=BoxAdapter(r, r_height), footer=Footer()) def exit(key): if key in ('q', 'Q'): raise ExitMainLoop() def refresh(loop, data): p.update() r.update() loop.set_alarm_in(2, refresh) mainloop = MainLoop(frame, palette, pop_ups=True, unhandled_input=exit) mainloop.set_alarm_in(1, refresh) mainloop.run()
class TUI: def __init__(self): self.keybind = {} self.main_helper_text = self.generate_helper_text([ ("F10", "Quit", "helper_text_red"), ]) self.subview_helper_text = self.generate_helper_text([ ("F1", "Confirm", "helper_text_green"), ("F5", "Abort", "helper_text_brown"), ("F10", "Quit", "helper_text_red"), ("TAB", "Next", "helper_text_light"), ("S-TAB", "Previous", "helper_text_light") ]) self.root = Frame(self.generate_main_view(), Text(("header", ""), "center"), self.main_helper_text) self.loop = MainLoop(self.root, palette, unhandled_input=self.unhandled_input) self.bind_global("f10", self.quit) self.handle_os_signals() def generate_main_view(self): main_view = HydraWidget("Welcome to INF1900 interactive grading tool!") subviews = (("c", ClonePanel()), ("g", GradePanel()), ("a", AssemblePanel()), ("p", PushPanel()), ("m", MailPanel())) heads = [] for letter, view, in subviews: hint = view.name connect_signal(view, QUIT_SIGNAL, self.display_main) connect_signal(view, SET_HEADER_TEXT_SIGNAL, self.set_header_text) connect_signal(view, DRAW_SIGNAL, self.draw_screen) heads.append((letter, "blue_head", hint, self.display_subview, { "view": view, "hint": hint })) main_view.add_heads(heads) return main_view def start(self): try: self.loop.run() finally: self.loop.screen.stop() def unhandled_input(self, key): if key in self.keybind: self.keybind[key]() return None def bind_global(self, key, callback): self.keybind[key] = callback def set_header_text(self, string): self.root.header.set_text(string) def quit(self, *kargs): raise ExitMainLoop() def pause(self, *kargs): print("PAUSE") self.loop.stop() os.kill(os.getpid(), signal.SIGSTOP) self.loop.start() self.loop.draw_screen() def interrupt(self, *kargs): pass def handle_os_signals(self): signal.signal(signal.SIGQUIT, self.quit) signal.signal(signal.SIGTSTP, self.pause) signal.signal(signal.SIGINT, self.interrupt) @staticmethod def generate_helper_text(hints): markup = [] for key, text, text_palette in hints: markup.extend( (("helper_key", key), " ", (text_palette, text), " ")) return Text(markup, align="center") def draw_screen(self): self.loop.draw_screen() def __change_view(self, view, hint): self.root.body = view if not hasattr(view, "root") else view.root self.set_header_text(hint) def display_subview(self, view, hint): self.root.footer = self.subview_helper_text self.__change_view(view, hint) def display_main(self, *kargs): self.root.footer = self.main_helper_text self.root.body = self.generate_main_view( ) # to reload data from app state self.__change_view(self.root.body, "")
self.open_pop_up() def create_pop_up (self): p = PopUpDialog(self.item) connect_signal(p, 'close', lambda button: self.close_pop_up()) return p def get_pop_up_parameters (self): return {'left':0, 'top':1, 'overlay_width':25, 'overlay_height':20} """ Testing """ if __name__ == '__main__': b = Button('click me') p1 = Process.Process(1, lambda x: x, lambda: x) popup = ProcessItemDialog(b, p1) connect_signal(b, 'click', lambda x: popup.open()) proc = AttrMap(popup, None) lb = ListBox(SimpleFocusListWalker([proc])) def exit (p): if p is 'q': raise ExitMainLoop m = MainLoop( lb, palette=palette, pop_ups=True, unhandled_input=exit ) m.run()
The text will automatically align correctly. To exit, press Return.""") string = [] cursor = 0 if len(sys.argv) > 1: string = hilbert_to_str(sys.argv[1]) cursor = len(string) if len(string) > 1: text.set_text(str_to_hilbert(string)) elif len(string) == 1: text.set_text(''.join(string)) filler = SolidFill(" ") overlay = Overlay(Filler(text), filler, "center", 40, "middle", 40) main = MainLoop(overlay, unhandled_input=keystroke) main.run() print("Do you want to save the file? (Ctrl+C to abort)") try: default = sys.argv[1] if len(sys.argv) > 1 else "test.act" fn = input("Save as [{}]: ".format(default)) if not fn: fn = default with open(fn, "w") as f: if len(string) > 1: lines = str_to_hilbert(string).split("\n") for line in lines: f.write(line.rstrip()) f.write("\n") else: f.write(string)
def __init__(self, verbose: bool, server_conn: ServerConnection): def _handle_global_input(key: str): if key == 'f5': self._input_view.set_edit_text('cont') self._input_view.keypress(0, 'enter') elif key == 'f8': self._input_view.set_edit_text('step') self._input_view.keypress(0, 'enter') elif key == 'f10': self._input_view.set_edit_text('quit') self._input_view.keypress(0, 'enter') else: logger.error(f"Function key '{key}' not implemented") self._source_view = Text("Source code will be shown here...") source_widget = LineBox( Padding(Filler(Pile([ Text(('banner', "Source code"), align='center'), self._source_view ]), valign='top', top=1, bottom=1), left=1, right=1)) self._disasm_view = Text("Dissambled code will be shown here...") disasm_widget = LineBox( Padding(Filler(Pile([ Text(('banner', "Disassembled code"), align='center'), self._disasm_view ]), valign='top', top=1, bottom=1), left=1, right=1)) self._register_view = Text("Registers will be shown here...") register_widget = LineBox( Padding(Filler(Pile([ Text(('banner', "Registers"), align='center'), self._register_view ]), valign='top', top=1, bottom=1), left=1, right=1)) self._stack_view = Text("Stack will be shown here...") stack_widget = LineBox( Padding(Filler(Pile( [Text(('banner', "Stack"), align='center'), self._stack_view]), valign='top', top=1, bottom=1), left=1, right=1)) self._input_view = CommandInput(self, server_conn) input_widget = LineBox( Padding(Filler( self._input_view, valign='top', ), left=1, right=1)) self._log_view = Text("Log messages will be shown here...") log_widget = LineBox( Padding(Filler( self._log_view, valign='top', ), left=1, right=1)) title = AttrMap( Text("CWDebug - a source-level debugger for the AmigaOS", align='center'), 'banner') menu = AttrMap( Text("F5 = Continue, F8 = Single-step over, F10 = Quit"), 'banner') screen = Frame( header=title, body=Pile([ Columns([ Pile([source_widget, disasm_widget]), Pile([register_widget, stack_widget]) ]), # 2 needs to be added for the line box (INPUT_WIDGET_HEIGHT + 2, input_widget), (MAX_NUM_OF_LOG_MESSAGES + 2, log_widget) ]), footer=menu) logger.remove() logger.add(UrwidHandler(self._log_view)) logger.info("Created main screen, starting event loop") self._disassembler = capstone.Cs(capstone.CS_ARCH_M68K, capstone.CS_MODE_32) loop = MainLoop(screen, PALETTE, unhandled_input=_handle_global_input) loop.run()
def run(self): loop = MainLoop(self.frame_widget, unhandled_input=self.handle_input) loop.run()
def run(self): self.tick() with utils.console_blank(): MainLoop.run(self)
from urwid import MainLoop, ExitMainLoop, Text, Filler, AttrMap PALETTE = [ ('banner', 'black', 'light gray'), ('streak', 'black', 'dark red'), ('bg', 'black', 'dark blue'), ] def exit_on_q(key): if key == 'Q': raise ExitMainLoop() txt = Text(('banner', u"Hello World"), align='center') map1 = AttrMap(txt, 'streak') fill = Filler(map1) map2 = AttrMap(fill, 'bg') loop = MainLoop(map2, PALETTE, unhandled_input=exit_on_q) if __name__ == '__main__': loop.run()
class Controller(object): """ The controller class - holds the map, instantiates the rooms, tracks the player and enemies """ DIRECTIONS = { 'EAST': (1, 0), 'WEST': (-1, 0), 'SOUTH': (0, -1), 'NORTH': (0, 1) } def __init__(self, startCoords=(0, 0)): """ Controller constructor New game: self._startinPosition = (0, 0) Load game: self._startingPosition = startCoords """ self._saveDir = 'saves' self._player = Player("Patrick", 15, gold=100) self._playerLocation = startCoords self._room = None self.map = None self._currentRoom = None self.loadFile('src/data/world.csv') self._roomKey = self.getRoomKey() # Preseed _visited dict with special rooms self._visited = { '00': Room(0, 0, self.map['00'][1]), # Office '01': SearchRoom(0, 1, self.map['01'][1]), # Search '02': TutorialRoom(0, 2, self.map['02'][1]), # Interrogate '03': Room(0, 3, self.map['03'][1]), # Combat '-55': Bookstore(-5, 5, self.map['-55'][1]), # Bookstore '-312': GroceryStore(-3, 12, self.map['-312'][1]), # Grocery '110': Bar(1, 10, self.map['110'][1]), # Bar '615': Room(6, 15, self.map['615'][1]) # Apartment } self._gameView = GameView(self.getDescriptionText(), self.getStatText(), directions=self.getDirectionOptions(), actions=self.getActionOptions(), gameOpts=self.getGameOptions(), controller=self) self._initialView = InitialView(['New game', 'Load game', 'Exit'], self._gameView, game_loop=None, controller=self) self._loop = MainLoop(self._initialView.screen, palette=[('reversed', 'standout', '')]) self._initialView.set_game_loop(self._loop) def start(self): """ Start the main game loop """ logging.info('Game started') self._loop.run() def stop(self): """ Stop the main game loop """ self._loop.stop() def loadFile(self, mapFile): """ReturnType void""" self.map = {} with open(mapFile) as csvDataFile: csvReader = csv.reader(csvDataFile, delimiter='|') for row in csvReader: key = "{0}{1}".format(row.pop(0), row.pop(0)) roomTitle = row.pop(0) self.map.update({key: [roomTitle, row]}) logging.debug(str(self.map)) def getPlayerLocation(self): """ Returns the player's current location as a tuple ReturnType tuple (x, y) """ logging.debug('Accessing Controller._playerLocation: %s', self._playerLocation) return self._playerLocation def getRoomKey(self): """ Generates a key for the map based on the player's current location @ReturnType String """ return '{}{}'.format(self._playerLocation[0], self._playerLocation[1]) def getRooms(self): """ Returns the loaded map. It is slated for removal very soon and should not be used @ReturnType dict """ return self.map def getDescriptionText(self): """ Gets the description text from the room at the player's current location @ReturnType String """ text = self._visited[self._roomKey].getText() return text def getStatText(self): """ Returns a formatted string representation of the player's basic stats, including current health / max health, equipped weapon, and damage range """ stats = "HP: {}/{} Equipped weapon: {} Damage: {}".format( self._player.getHP(), self._player.getMaxHP(), self._player._weapon._name, self._player._weapon.getDamage()) return stats def _canMove(self, direction): """ Checks if there is a room in <direction> from current room """ dirX, dirY = Controller.DIRECTIONS[direction] roomKey = '{}{}'.format(self._playerLocation[0] + dirX, self._playerLocation[1] + dirY) return roomKey in self.map def getDirectionOptions(self): """ Builds the list of directions the player can move ReturnType list of Strings """ return [ "Move {}".format(x).title() for x in ["NORTH", "WEST", "EAST", "SOUTH"] if self._canMove(x) ] def getActionOptions(self): """ Builds and returns the list of actions the player can take in the room ReturnType list of Strings """ options = [] # Try to add item to menu try: if not self._room.item.isHidden(): logging.debug("Visible item in room") options.append("Pick up " + self._room.item.identify()) else: logging.debug("Hidden item in room") options.append("Search room") except AttributeError: logging.debug("No item in room") pass # Try to add enemy to menu try: if not self._room.enemy.isDead(): options.append("Fight " + self._room.enemy.getName()) except AttributeError: pass # Try to add NPC to menu try: options.append("Interrogate " + self._room.character.getName()) except AttributeError: pass return options def getGameOptions(self): """ Returns the metagame options (e.g. Save, Load, Quit) ReturnType list of strings """ options = ["Save", "Load", "Exit game"] return options def movePlayer(self, direction): """ Updates the player's current location and instantiates a room if necessary ReturnType None """ if not self._canMove(direction): return self._player.move(Controller.DIRECTIONS[direction]) self._playerLocation = self._player.getLocation() self._roomKey = self.getRoomKey() try: logging.debug('Returning to previously visited room') self._room = self._visited[self._roomKey] except KeyError: logging.debug('Visiting a new room, generating room') self._visited[self._roomKey] = Factory.roomFactory( self._playerLocation[0], self._playerLocation[1], self.map[self._roomKey][1]) self._room = self._visited[self._roomKey] logging.debug('Created new room') finally: logging.debug('Room %s', self._roomKey) logging.debug('Room description: %s', self._room.getText()) def updateGameView(self): """ Updates the GameView screen after player action """ self._gameView.updateDescription(self.getDescriptionText()) self._gameView.updateStats(self.getStatText()) self._gameView.updateDirectionMenu(self.getDirectionOptions()) self._gameView.updateActionMenu(self.getActionOptions()) self._gameView.setMenuFocus(0) def moveCallback(self, button): """ Updates the gameView object every time the player moves """ functions = { 'move_north': (self.movePlayer, "NORTH"), 'move_south': (self.movePlayer, "SOUTH"), 'move_east': (self.movePlayer, "EAST"), 'move_west': (self.movePlayer, "WEST") } label = button._w.original_widget.text.lower().replace(' ', '_') try: functions[label][0](functions[label][1]) except KeyError: return self.updateGameView() def optionCallback(self, button): """ Updates the gameView object whenever the player uses the game options menu (save/load/quit/etc) """ functions = { 'save': self.saveGame, 'load': self.loadGame, 'exit_game': exitGame } label = button._w.original_widget.text.lower().replace(' ', '_') try: functions[label]() except KeyError: pass def actionCallback(self, button): """ Updates the gameView object whenever the player performs an action from the action menu Precondition: Action menu item is selected by player. Action menu items should be in the format 'pick up <item>', 'fight <enemy>', 'interrogate <npc>' Postcondition: The appropriate action method is run @ReturnType None""" label = button._w.original_widget.text.lower().replace(' ', '_') try: if self._room.item.isHidden(): logging.debug("Trying to search the room") if self._room.search(): logging.debug("Found item %s", self._room.item.identify()) self._room.item.find() self._room.updateText( "You're in the hallway outside your office. \ This is where you found the {}.".format(self._room.item.identify())) else: logging.debug("Item still hidden") else: logging.debug("Trying to add item to inventory") logging.debug(self._room.item) self._player.addItem(self._room.item) self._room.removeItem() except AttributeError: logging.debug("No item in room") try: logging.debug("Trying to fight enemy") self._player.fight(self._room.enemy) logging.debug( "Fought enemy - results: Player HP: %s\nEnemy HP: %s", self._player.getHP(), self._room.enemy.getHP()) if self._room.enemy.isDead(): self._room.killEnemy() else: self.playerDead() except AttributeError: logging.debug("No enemy to fight") self._player.interrogate(self._room.character) finally: self.updateGameView() logging.debug("Action menu item %s pressed", label) def playerDead(self): """ Handle the player dying """ pass def saveGame(self): """ Pickles the controller state and player to save the current game state Precondition: None Postcondition: Saves game state in saves/<player name>_<save index> @ReturnType None """ try: makedirs(self._saveDir) except OSError as e: if e.errno != errno.EEXIST: raise with open(self._saveDir + '/patrick_001', 'w+') as f: dill.dump(self._player, f) dill.dump(self._playerLocation, f) dill.dump(self._currentRoom, f) dill.dump(self.map, f) dill.dump(self._visited, f) dill.dump(self._roomKey, f) def loadGame(self): """ Unpickles the controller (self) and player (self._player) to load the saved game state Precondition: Save game file in saves/<player name>_<save index> Postcondition: Loads game state @ReturnType None """ try: with open(self._saveDir + '/patrick_001') as f: self._player = dill.load(f) self._playerLocation = dill.load(f) self._currentRoom = dill.load(f) self.map = dill.load(f) self._visited = dill.load(f) self._roomKey = dill.load(f) self.updateGameView() except IOError: return
def run(): loop = MainLoop(wrapper, palette, handle_mouse=False, unhandled_input=key_pressed) loop.run()
class Shipit(): ISSUE_LIST = 0 ISSUE_DETAIL = 1 PR_DETAIL = 2 PR_DIFF = 3 def __init__(self, ui, repo, user): self.ui = ui self.repo = repo self.user = user self.issues_and_prs = IssuesAndPullRequests(self.repo) self.issues_and_prs.set_modified_callback( self.on_modify_issues_and_prs) self.issues_and_prs.show_open_issues() # Event handlers on("show_all", self.issues_and_prs.show_all) created_by_you = partial(self.issues_and_prs.show_created_by, self.user) on("show_created_by_you", created_by_you) assigned_to_you = partial(self.issues_and_prs.show_assigned_to, self.user) on("show_assigned_to_you", assigned_to_you) mentioning_you = partial(self.issues_and_prs.show_mentioning, self.user) on("show_mentioning_you", mentioning_you) on("show_open_issues", self.issues_and_prs.show_open_issues) on("show_closed_issues", self.issues_and_prs.show_closed_issues) on("show_pull_requests", self.issues_and_prs.show_pull_requests) on("filter_by_labels", self.issues_and_prs.filter_by_labels) on("clear_label_filters", self.issues_and_prs.clear_label_filters) def start(self): self.loop = MainLoop(self.ui, PALETTE, handle_mouse=True, unhandled_input=self.handle_keypress) self.loop.set_alarm_at(0, discard_args(self.issue_list)) self.loop.run() def on_modify_issues_and_prs(self): self.ui.issues_and_pulls(self.issues_and_prs) def issue_list(self): self.mode = self.ISSUE_LIST self.ui.issues_and_pulls(self.issues_and_prs) self.loop.draw_screen() def issue_detail(self, issue): self.mode = self.ISSUE_DETAIL self.ui.issue(issue) self.loop.draw_screen() def pull_request_detail(self, pr): self.mode = self.PR_DETAIL self.ui.pull_request(pr) self.loop.draw_screen() def diff(self, pr): self.mode = self.PR_DIFF self.ui.diff(pr) self.loop.draw_screen() def handle_keypress(self, key): if key == KEY_OPEN_ISSUE: if self.mode is self.ISSUE_LIST: issue_text = self.spawn_editor(NEW_ISSUE) if issue_text is None: # TODO: cancelled by the user return contents = unlines(issue_text) title, *body = contents if not title: # TODO: incorrect input, at least a title is needed return body = lines(body) issue = self.repo.create_issue(title=title, body=body) if issue: self.issue_detail(issue) else: self.issue_list() elif key == KEY_CLOSE_ISSUE: issue = self.ui.get_issue() if not issue: return self.issues_and_prs.close(issue) if self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_REOPEN_ISSUE: issue = self.ui.get_issue() if issue and is_closed(issue): self.issues_and_prs.reopen(issue) if self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_BACK: if self.mode is self.PR_DIFF: pr = self.ui.get_focused_item() self.pull_request_detail(pr) elif self.mode in [self.ISSUE_DETAIL, self.PR_DETAIL]: self.issue_list() elif key == KEY_DETAIL: if self.mode is self.ISSUE_LIST: issue_or_pr = self.ui.get_focused_item() if is_issue(issue_or_pr): self.issue_detail(issue_or_pr) elif is_pull_request(issue_or_pr): self.pull_request_detail(issue_or_pr) elif key == KEY_EDIT: item = self.ui.get_focused_item() if item is None: return if is_pull_request(item): item = item.issue # In case you aren't the owner of the repo, you are only allowed to # edit things that you created. if self.repo.owner != self.user and item.user != self.user: # TODO: beep return if is_issue(item): self.edit_issue(item) else: self.edit_body(item) elif key == KEY_COMMENT: item = self.ui.get_issue_or_pr() if item is None: return if is_pull_request(item): issue = item.issue self.comment_issue(item.issue, pull_request=item) else: self.comment_issue(item) elif key == KEY_DIFF: if self.mode is self.PR_DETAIL: pr = self.ui.get_focused_item() self.diff(pr) elif key == KEY_BROWSER: item = self.ui.get_focused_item() if hasattr(item, '_api'): webbrowser.open(item.html_url) elif key == KEY_QUIT: raise ExitMainLoop def edit_issue(self, issue): title_and_body = '\n'.join([issue.title, issue.body]) issue_text = self.spawn_editor(title_and_body) if issue_text is None: # TODO: cancelled return contents = unlines(issue_text) title, *body = contents if not title: # TODO: incorrect input, at least a title is needed return body = lines(body) issue.edit(title=title, body=body) if self.mode is self.ISSUE_LIST: # TODO: focus self.issue_list() elif self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) # TODO def edit_pull_request(self, pr): pass def edit_body(self, item): text = self.spawn_editor(item.body) if text is None: # TODO: cancelled return # TODO: ui must be updated! item.edit(text) def comment_issue(self, issue, *, pull_request=False): issue_thread = format_issue_thread(issue) comment_text = self.spawn_editor('\n'.join(issue_thread)) if comment_text is None: # TODO: cancelled return if not comment_text: # TODO: A empty comment is invalid input return issue.create_comment(comment_text) if pull_request: self.pull_request_detail(pull_request) else: self.issue_detail(issue) def spawn_editor(self, help_text=None): """ Open a editor with a temporary file containing ``help_text``. If the exit code is 0 the text from the file will be returned. Otherwise, ``None`` is returned. """ text = '' if help_text is None else help_text tmp_file = tempfile.NamedTemporaryFile(mode='w+', suffix='.markdown', delete=False) tmp_file.write(text) tmp_file.close() fname = tmp_file.name self.loop.screen.stop() return_code = subprocess.call([os.getenv('EDITOR', 'vim'), fname]) self.loop.screen.start() if return_code == 0: with open(fname, 'r') as f: contents = f.read() if return_code != 0: return None else: return strip_comments(contents)
(4, Text('run│', wrap='clip')), (Text('key', wrap='clip')), ]), 'title_header') body_frame = Frame(body, header=title_header, footer=footer.get_widget()) wrapper = GazuaFrame(body_frame, arrow_callback=on_arrow_pressed) palette = [ ('header', 'white', 'dark red', 'bold'), ('footer', 'white', 'light gray', 'bold'), ('title_header', 'black', 'dark cyan', 'bold'), ('footer', 'black', 'light gray'), ('group', 'black', 'yellow', 'bold'), ('host', 'black', 'dark green'), ('aws_focus', 'black', 'dark green'), ('group_focus', 'black', 'dark green'), ('instance_focus', 'black', 'yellow'), ] def key_pressed(key): if key == 'esc': raise urwid.ExitMainLoop() loop = MainLoop(wrapper, palette, handle_mouse=False, unhandled_input=key_pressed) loop.run()
class Shipit(): ISSUE_LIST = 0 ISSUE_DETAIL = 1 PR_DETAIL = 2 PR_DIFF = 3 def __init__(self, ui, repo, user): self.ui = ui self.repo = repo self.user = user self.issues_and_prs = IssuesAndPullRequests(self.repo) self.issues_and_prs.set_modified_callback(self.on_modify_issues_and_prs) self.issues_and_prs.show_open_issues() # Event handlers on("show_all", self.issues_and_prs.show_all) created_by_you = partial(self.issues_and_prs.show_created_by, self.user) on("show_created_by_you", created_by_you) assigned_to_you = partial(self.issues_and_prs.show_assigned_to, self.user) on("show_assigned_to_you", assigned_to_you) mentioning_you = partial(self.issues_and_prs.show_mentioning, self.user) on("show_mentioning_you", mentioning_you) on("show_open_issues", self.issues_and_prs.show_open_issues) on("show_closed_issues", self.issues_and_prs.show_closed_issues) on("show_pull_requests", self.issues_and_prs.show_pull_requests) on("filter_by_labels", self.issues_and_prs.filter_by_labels) on("clear_label_filters", self.issues_and_prs.clear_label_filters) def start(self): self.loop = MainLoop(self.ui, PALETTE, handle_mouse=True, unhandled_input=self.handle_keypress) self.loop.set_alarm_at(0, discard_args(self.issue_list)) self.loop.run() def on_modify_issues_and_prs(self): self.ui.issues_and_pulls(self.issues_and_prs) def issue_list(self): self.mode = self.ISSUE_LIST self.ui.issues_and_pulls(self.issues_and_prs) self.loop.draw_screen() def issue_detail(self, issue): self.mode = self.ISSUE_DETAIL self.ui.issue(issue) self.loop.draw_screen() def pull_request_detail(self, pr): self.mode = self.PR_DETAIL self.ui.pull_request(pr) self.loop.draw_screen() def diff(self, pr): self.mode = self.PR_DIFF self.ui.diff(pr) self.loop.draw_screen() def handle_keypress(self, key): if key == KEY_OPEN_ISSUE: if self.mode is self.ISSUE_LIST: issue_text = self.spawn_editor(NEW_ISSUE) if issue_text is None: # TODO: cancelled by the user return contents = unlines(issue_text) title, *body = contents if not title: # TODO: incorrect input, at least a title is needed return body = lines(body) issue = self.repo.create_issue(title=title, body=body) if issue: self.issue_detail(issue) else: self.issue_list() elif key == KEY_CLOSE_ISSUE: issue = self.ui.get_issue() if not issue: return self.issues_and_prs.close(issue) if self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_REOPEN_ISSUE: issue = self.ui.get_issue() if issue and is_closed(issue): self.issues_and_prs.reopen(issue) if self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_BACK: if self.mode is self.PR_DIFF: pr = self.ui.get_focused_item() self.pull_request_detail(pr) elif self.mode in [self.ISSUE_DETAIL, self.PR_DETAIL]: self.issue_list() elif key == KEY_DETAIL: if self.mode is self.ISSUE_LIST: issue_or_pr = self.ui.get_focused_item() if is_issue(issue_or_pr): self.issue_detail(issue_or_pr) elif is_pull_request(issue_or_pr): self.pull_request_detail(issue_or_pr) elif key == KEY_EDIT: item = self.ui.get_focused_item() if item is None: return if is_pull_request(item): item = item.issue # In case you aren't the owner of the repo, you are only allowed to # edit things that you created. if self.repo.owner != self.user and item.user != self.user: # TODO: beep return if is_issue(item): self.edit_issue(item) else: self.edit_body(item) elif key == KEY_COMMENT: item = self.ui.get_issue_or_pr() if item is None: return if is_pull_request(item): issue = item.issue self.comment_issue(item.issue, pull_request=item) else: self.comment_issue(item) elif key == KEY_DIFF: if self.mode is self.PR_DETAIL: pr = self.ui.get_focused_item() self.diff(pr) elif key == KEY_BROWSER: item = self.ui.get_focused_item() if hasattr(item, '_api'): webbrowser.open(item.html_url) elif key == KEY_QUIT: raise ExitMainLoop def edit_issue(self, issue): title_and_body = '\n'.join([issue.title, issue.body]) issue_text = self.spawn_editor(title_and_body) if issue_text is None: # TODO: cancelled return contents = unlines(issue_text) title, *body = contents if not title: # TODO: incorrect input, at least a title is needed return body = lines(body) issue.edit(title=title, body=body) if self.mode is self.ISSUE_LIST: # TODO: focus self.issue_list() elif self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) # TODO def edit_pull_request(self, pr): pass def edit_body(self, item): text = self.spawn_editor(item.body) if text is None: # TODO: cancelled return # TODO: ui must be updated! item.edit(text) def comment_issue(self, issue, *, pull_request=False): issue_thread = format_issue_thread(issue) comment_text = self.spawn_editor('\n'.join(issue_thread)) if comment_text is None: # TODO: cancelled return if not comment_text: # TODO: A empty comment is invalid input return issue.create_comment(comment_text) if pull_request: self.pull_request_detail(pull_request) else: self.issue_detail(issue) def spawn_editor(self, help_text=None): """ Open a editor with a temporary file containing ``help_text``. If the exit code is 0 the text from the file will be returned. Otherwise, ``None`` is returned. """ text = '' if help_text is None else help_text tmp_file = tempfile.NamedTemporaryFile(mode='w+', suffix='.markdown', delete=False) tmp_file.write(text) tmp_file.close() fname = tmp_file.name self.loop.screen.stop() return_code = subprocess.call([os.getenv('EDITOR', 'vim'), fname]) self.loop.screen.start() if return_code == 0: with open(fname, 'r') as f: contents = f.read() if return_code != 0: return None else: return strip_comments(contents)
class Ui: def __init__(self, screen_controller, wallpaper_controller): self._scrctrl = screen_controller self._wpctrl = wallpaper_controller self._layout() self._reading_command = False self._loop = MainLoop(widget=self._root, palette=palette, unhandled_input=self._handle_global_input) self._loop.screen.set_terminal_properties( colors=256, bright_is_bold=False, has_underline=True) # make special key combinations available, see # https://github.com/urwid/urwid/issues/140 self._loop.screen.tty_signal_keys(stop='undefined') def _layout(self): self._wallpaper_count = Text(str(len(self._wpctrl.wallpapers))) self._info = Text("", wrap='clip') self._head = Columns([('pack', self._wallpaper_count), (10, Text("Wallpapers")), self._info], dividechars=1) header = Pile([self._head, AttrMap(Divider("─"), 'divider')]) self._screens = [ScreenWidget(screen, self._scrctrl) for screen in self._scrctrl.screens] body = ListBoxWithTabSupport(self._screens) self._root = Frame(header=header, body=body) def run_loop(self): self._log_handler = CallbackLogHandler(self.info) logging.getLogger(__package__).addHandler(self._log_handler) self._loop.run() logging.getLogger(__package__).removeHandler(self._log_handler) def info(self, message): self._info.set_text("⋮ " + str(message)) def _start_reading_command(self): self._reading_command = True self._info = Edit(caption="_⟩ ", wrap='clip') self._head.contents[2] = (self._info, self._head.options('pack')) self._root.set_focus_path(("header", 0, 2)) def _finish_reading_command(self): command = self._info.get_edit_text() self._info = Text("", wrap='clip') self._head.contents[2] = (self._info, self._head.options('pack')) self._root.focus_position = "body" self._reading_command = False return command def _handle_global_input(self, key): if self._reading_command: if key == 'esc': self._finish_reading_command() elif key == 'enter': command = self._finish_reading_command() log.info(f"Commands ({command}) are not implemented yet.") return if key == 'esc': raise ExitMainLoop() elif key == ':' or key == '-': self._start_reading_command() elif key == 'ctrl s': self._wpctrl.save_updates() elif key == 'x': self._scrctrl.cycle_collections() elif key in _shift_number_keys: current_screen_idx = self._root.focus.focus._screen.idx key_number = _shift_number_keys[key] self._scrctrl.move_wallpaper(current_screen_idx, key_number) else: log.info("unhandled key: '%s'", key) return for screen_widget in self._screens: screen_widget.update() return True
class Shipit(): ISSUE_LIST = 0 ISSUE_DETAIL = 1 PR_DETAIL = 2 PR_DIFF = 3 def __init__(self, ui, repo): self.ui = ui self.repo = repo self.issues_and_prs = IssuesAndPullRequests(self.repo) self.issues_and_prs.set_modified_callback(self.on_modify_issues_and_prs) self.issues_and_prs.show_open_issues() # Event handlers on("show_open_issues", self.issues_and_prs.show_open_issues) on("show_closed_issues", self.issues_and_prs.show_closed_issues) on("show_pull_requests", self.issues_and_prs.show_pull_requests) def start(self): self.issue_list() self.loop = MainLoop(self.ui, PALETTE, handle_mouse=True, unhandled_input=self.handle_keypress) self.loop.run() def on_modify_issues_and_prs(self): self.ui.issues_and_pulls(self.issues_and_prs) def issue_list(self): self.mode = self.ISSUE_LIST self.ui.issues_and_pulls(self.issues_and_prs) def issue_detail(self, issue): self.mode = self.ISSUE_DETAIL self.ui.issue(issue) def pull_request_detail(self, pr): self.mode = self.PR_DETAIL self.ui.pull_request(pr) def diff(self, pr): self.mode = self.PR_DIFF self.ui.diff(pr) def handle_keypress(self, key): # R: reopen # D: delete if key == KEY_OPEN_ISSUE: if self.mode is self.ISSUE_LIST: issue_text = spawn_editor(NEW_ISSUE) if issue_text is None: # TODO: cancelled by the user return contents = unlines(issue_text) title, *body = contents if not title: # TODO: incorrect input, at least a title is needed return body = lines(body) issue = self.repo.create_issue(title=title, body=body) if issue: self.issue_detail(issue) elif key == KEY_CLOSE_ISSUE: issue = self.ui.get_issue() if not issue: return issue.close() if self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_REOPEN_ISSUE: issue = self.ui.get_issue() if issue and is_closed(issue): issue.reopen() if self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_BACK: if self.mode is self.PR_DIFF: pr = self.ui.get_focused_item() self.pull_request_detail(pr) elif self.mode in [self.ISSUE_DETAIL, self.PR_DETAIL]: self.issue_list() elif key == KEY_DETAIL: if self.mode is self.ISSUE_LIST: issue_or_pr = self.ui.get_focused_item() if is_issue(issue_or_pr): self.issue_detail(issue_or_pr) elif is_pull_request(issue_or_pr): self.pull_request_detail(issue_or_pr) elif key == KEY_EDIT: # TODO: not the issue but what it's focused, could be a comment! issue = self.ui.get_issue() if issue is None: return title_and_body = '\n'.join([issue.title, issue.body_text]) issue_text = spawn_editor(title_and_body) if issue_text is None: # TODO: cancelled return contents = unlines(issue_text) title, *body = contents if not title: # TODO: incorrect input, at least a title is needed return body = lines(body) issue.edit(title=title, body=body) if self.mode is self.ISSUE_LIST: self.issue_list() elif self.mode is self.ISSUE_DETAIL: self.issue_detail(issue) elif key == KEY_COMMENT: issue = self.ui.get_issue() if issue is None: return # Inline all the thread comments issue_thread = [format_comment(comment) for comment in issue.iter_comments()] issue_thread.insert(0,'\n\n'.join([issue.title, issue.body_text, ''])) # Make the whole thread a comment issue_thread.insert(0, '<!---\n') issue_thread.append('-->') comment_text = spawn_editor('\n'.join(issue_thread)) if comment_text is None: # TODO: cancelled return # TODO: strip comments first! if not comment_text.strip(): # TODO: invalid input return issue.create_comment(comment_text) self.issue_detail(issue) elif key == KEY_QUIT: raise ExitMainLoop elif key == KEY_DIFF: if self.mode is self.PR_DETAIL: pr = self.ui.get_focused_item() self.diff(pr)
class Chat(object): """ The main chat class. Connects the GUI pieces together. """ def __init__(self, nickname): self.nickname = nickname self.output = ChatMessages(self) self.message = ChatInput(self) self.window = Frame( body=self.output, footer=self.message, focus_part='footer' ) self.main_loop = None self.connection = Connection(self) def connected(self): self.write_status('Connected') def send_message(self, message): self.connection.send('MSG', self.nickname, message) def write(self, message): self.output.add(message) def write_message(self, nickname, message): self.write("%s <%s> %s" % (self.time(), nickname, message)) def write_status(self, message): self.write("*** %s ***" % message) def time(self, date=False): format_string = '%X' if date: format_string = '%c' return datetime.now().strftime(format_string).strip() def user_command(self, command): arguments = command.split(' ') command = arguments.pop(0) def _require_args(args, n): if len(args) < n: self.write("%s needs %d argument(s), %d given" % (command, n, len(args))) return False return True def _quit(): raise ExitMainLoop() def _unknown(): self.write('Unknown command %r' % command) def _nickname(new_nickname): self.nickname = new_nickname self.write_status('Nickname changed to %r' % new_nickname) command_map = { 'quit': (_quit, 0), 'nickname': (_nickname, 1) } command, args_needed = command_map.get(command, (_unknown, 0)) if _require_args(arguments, args_needed): command(*arguments) def connect(self): self.write_status('Connecting') self.connection.open() def run(self): """ Start the chat client """ self.write_status("Chat started at %s" % self.time(date=True)) self.main_loop = MainLoop(self.window, event_loop=GeventLoop()) gevent.spawn(self.connect) self.main_loop.run()
class Chat(object): """ The main chat class. Connects the GUI pieces together. """ def __init__(self, nickname): self.nickname = nickname self.output = ChatMessages(self) self.message = ChatInput(self) self.window = Frame(body=self.output, footer=self.message, focus_part="footer") self.main_loop = None self.connection = Connection(self) def connected(self): self.write_status("Connected") def send_message(self, message): self.connection.send("MSG", self.nickname, message) def write(self, message): self.output.add(message) def write_message(self, nickname, message): self.write("%s <%s> %s" % (self.time(), nickname, message)) def write_status(self, message): self.write("*** %s ***" % message) def time(self, date=False): format_string = "%X" if date: format_string = "%c" return datetime.now().strftime(format_string).strip() def user_command(self, command): arguments = command.split(" ") command = arguments.pop(0) def _require_args(args, n): if len(args) < n: self.write("%s needs %d argument(s), %d given" % (command, n, len(args))) return False return True def _quit(): raise ExitMainLoop() def _unknown(): self.write("Unknown command %r" % command) def _nickname(new_nickname): self.nickname = new_nickname self.write_status("Nickname changed to %r" % new_nickname) command_map = {"quit": (_quit, 0), "nickname": (_nickname, 1)} command, args_needed = command_map.get(command, (_unknown, 0)) if _require_args(arguments, args_needed): command(*arguments) def connect(self): self.write_status("Connecting") self.connection.open() def run(self): """ Start the chat client """ self.write_status("Chat started at %s" % self.time(date=True)) self.main_loop = MainLoop(self.window, event_loop=GeventLoop()) gevent.spawn(self.connect) self.main_loop.run()
i.split() for i in [line.strip() for line in open('/proc/stat')] ] # Testing if __name__ == '__main__': from sys import argv from timeit import Timer cm = CPUMeter(1) frame = Frame(ListBox([cm]), header=None, footer=Footer()) def exit(key): if key in ('q', 'Q'): raise ExitMainLoop() def refresh(loop, data): cm.update() loop.set_alarm_in(1, refresh) if argv[1] == 'perf': t = Timer(lambda: cm.update()) print 'CPUMeter: ', t.timeit(number=10000) else: main_loop = MainLoop(frame, palette, unhandled_input=exit, pop_ups=True) main_loop.set_alarm_in(1, refresh) main_loop.run()
def run(host, port): """ Start a websocket client and a terminal UI to interact with it. """ # connection state channels = [] bound_channel = None ws = None # UI state lines = ['example text\n'] output = Text(lines) input_field = Edit('>> ') input_state = COMMAND_READ widget = Frame(Filler(output, 'top'), footer=input_field) widget.focus_position = 'footer' # event wiring event_loop = AsyncioEventLoop(loop=loop) input_cb = None def write(msg): """ Show an additional line of text. """ lines.append(msg + '\n') output.set_text(lines) def prompt_for_password(msg): """ Change prompt to password prompt. Return a future for the typed password. """ nonlocal input_cb, input_state input_cb = loop.create_future() input_state = PASSWORD_READ input_cb.add_done_callback(_password_done) input_field.set_mask('*') input_field.set_caption(msg) return input_cb def _password_done(_): nonlocal input_state input_field.set_mask(None) input_state = COMMAND_READ def accept_input(key): """ Process typed lines of text. Dispatches to password prompt or command prompt as needed. """ if key == 'enter': if input_state == PASSWORD_READ: input_cb.set_result(input_field.edit_text) elif input_state == COMMAND_READ: cmd_dispatch(input_field.edit_text) input_field.set_edit_text('') def update_channels(new_channels): """ Receive channel data. """ nonlocal channels, bound_channel channels = new_channels if len(channels) == 1: bound_channel = channels[0] write(f'bound console to channel "{bound_channel}"') else: write(f'bot is in these channels: {", ".join(channels)}') async def ws_dispatch(): """ Handle websocket messages. """ nonlocal ws ws = await websockets.connect(f'ws://{host}:{port}') while True: try: msg = json.loads(await ws.recv()) if msg['type'] == SEND_PASSWORD: loop.create_task( ws.send(await prompt_for_password("Server password:"******"dispatching {repr(command)}") nonlocal bound_channel if not ws: write('Not connected') return parts = command.split() if not parts: print_help() command_part = parts[0].lower() if command_part[0] == '/': command_part = command_part[1:] args = parts[1:] if command_part == 'help': print_help() elif command_part == 'sendcmd': if not bound_channel: write( 'there is not a bound channel! use `/channel <channel>` to bind one!' ) elif not args: write( 'you must provide a command to run to /sendcmd, ex: /sendcmd help' ) else: loop.create_task( ws.send( json.dumps({ 'type': RUN_COMMAND, 'channel': bound_channel, 'command': args[0], 'args': args[1:], 'silent': True, }))) elif command_part == 'chat': if not bound_channel: write( 'there is not a bound channel! use `/channel <channel>` to bind one!' ) else: loop.create_task( ws.send( json.dumps({ 'type': SEND_PRIVMSG, 'channel': bound_channel, 'message': ' '.join(args), }))) elif command_part == 'channel': if not channels: write( 'the bot is not currently in any channels, please have the bot join at least one than relaunch this console' ) elif not args: write( f'the bot is currently in these channels: {", ".join(channels)}\ndo `/channel <channel>` to bind this channel to one' ) elif args[0] not in channels: write(f'the bot is not currently in "{args[0]}"') else: bound_channel = args[0] elif command_part == 'quit': raise ExitMainLoop() else: write(f"Unrecognized command {repr(command_part)}") event_loop.alarm(0, lambda: loop.create_task(ws_dispatch())) mainloop = MainLoop(widget, event_loop=event_loop, unhandled_input=accept_input) mainloop.run()
class Controller(object): """ The controller for aerende. Reacts to keypresses via the key handler. Is responsible for reading and writing notes to the filesystem, and manipulating the underlying urwid interface. Also responsible for exiting aerende. """ def __init__(self, config, interface): self.config = config self.data_path = path.expanduser(self.config.get_data_path()) self.notes = self.load_notes(self.data_path) self.tags = self.load_tags(self.notes) self.interface = interface self.editor_mode = False self.key_handler = KeyHandler(self, config) self.loop = MainLoop(interface, config.get_palette(), input_filter=self.key_handler.handle) self.refresh_interface() self.interface.focus_first_note() self.loop.run() # [ Filesystem Reading / Writing ] def load_notes(self, path): """ Loads notes from a given file path. If no notes file exists at this location, creates an empty one. """ if Path(path).is_file(): with open(path, 'r') as data_file: note_yaml = yaml.load(data_file) notes = [] if note_yaml is None: return notes for unique_id, note in note_yaml.items(): notes.append( Note(note['title'], note['tags'], note['text'], note['priority'], unique_id)) return notes else: open(path, 'x') return [] def write_notes(self, path): """ Writes the current note list to the given file path. """ with open(path, 'w') as data_file: for note in self.notes: yaml.dump(note.to_dictionary(), data_file, default_flow_style=False) # [ Note Creation, Updating and Deletion ] def create_note(self, title, tags, text): """ Creates a new note object, given the title/tags/text, and appends it to the current note list. """ note = Note(title, tags, text) self.notes.append(note) def delete_note(self, unique_id): """ Deletes a note from the current note list, given a note UUID. """ for index, note in enumerate(self.notes): if note.id == unique_id: del self.notes[index] break def update_note(self, new_note): """ Update a note in the current note list to a given new note. """ for index, note in enumerate(self.notes): if note.id == new_note.id: note = new_note break def delete_focused_note(self): """ Deletes the focused note. Gets the currently focused note object, deletes it from the current note list and writes the note list to file. """ note = self.interface.get_focused_note() self.delete_note(note.id) self.write_notes(self.data_path) self.refresh_interface() def change_focused_note_priority(self, amount): """ Changes the focused note priority by a given amount. First, gets the focused note and changes the priority of the note. Then writes the note list to file. """ note = self.interface.get_focused_note() note.change_priority(amount) self.write_notes(self.data_path) self.refresh_interface() # [ Tag Loading ] def load_tags(self, notes): """ Returns a list of tag widgets from a list of notes. Does this by first getting all the tags from all the notes in the list. It then counts the frequency of these notes, then creates the requisite tag widgets from this tag: frequency list. """ note_tags = list(map((lambda note: note.tags), notes)) note_tags = [tag for subtags in note_tags for tag in subtags] tag_frequency = Counter(note_tags) tag_widgets = list( map((lambda tag: Tag(tag, tag_frequency[tag])), tag_frequency)) tag_widgets.insert(0, Tag('ALL', len(note_tags))) return tag_widgets # [ Interface Manipulation ] def refresh_interface(self): """ Refreshes the interface with the current note and tag lists. """ self.interface.draw_notes(self.notes) self.tags = self.load_tags(self.notes) self.interface.draw_tags(self.tags) def show_note_editor(self, note_handler, edit_focused_note=False): """ Shows the note editor at the bottom of the interface. If the editor is to edit the focused note, rather than a new one, then the focused note is retrieved and passed to the interface. """ note_to_edit = None if edit_focused_note: note_to_edit = self.interface.get_focused_note() self.editor_mode = True self.interface.show_note_editor(note_handler, note_to_edit) self.key_handler.editor = self.interface.get_note_editor() def edit_note_handler(self, note, original_note=None): """ Handles the return signal from the note editor. If the note is not None (which happens if the user presses escape, cancelling the editor), then either a new note is created or an existing note is updated, depending on whether the original note returned exists. """ if note is not None: title = note[0] tags = self._convert_tag_input(note[1]) text = note[2] if original_note is not None: original_note.edit_note(title, tags, text) self.update_note(original_note) else: self.create_note(title, tags, text) self.write_notes(self.data_path) # Restart the loop.. Seems to work? self.loop.stop() self.loop.start() self.refresh_interface() self.editor_mode = False def _convert_tag_input(self, tag_text): split_tags = tag_text.split('//') return list(map(lambda tag: tag.strip(), split_tags)) def focus_next_note(self): self.interface.focus_next_note() def focus_previous_note(self): self.interface.focus_previous_note() # [ System Functions ] def exit(self): raise ExitMainLoop()