class GUI(object): def __init__(self): self.screen = Screen() self.screen.set_input_timeouts(max_wait=0) self.steps = GridFlow([], 20, 2, 1, 'left') self.progress = SimpleFocusListWalker([]) self.log = SimpleFocusListWalker([]) self.widget = AttrMap( LineBox(Pile([ ('fixed', 6, AttrMap(Filler(self.steps), 'default')), ('fixed', 1, Filler(Divider('\u2500'))), ('fixed', 3, ListBox(self.progress)), AttrMap(LineBox(ListBox(self.log), title='Message log'), 'default') ]), title='Indico 1.2 -> 2.0 migration'), 'global_frame') self.screen.register_palette( [('green', 'light green', ''), ('white', 'white', ''), ('red', 'dark red', ''), ('yellow', 'yellow', ''), ('progress_empty', 'black', 'light gray'), ('progress_progress', 'light cyan', 'light gray'), ('progress_done', 'black', 'light cyan'), ('box', 'white', 'dark gray'), ('step_done', 'light green', ''), ('step_working', 'dark gray', ''), ('global_frame', 'light cyan', ''), ('fill', 'light cyan', 'dark cyan'), ('done', 'white', 'dark green'), ('eta', 'yellow', 'dark gray')] + generate_urwid_palette(PALETTE)) def print_log(self, icon, message, prefix='', event_id=''): self.log.append( Text([ color_segments(icon), ' ', color_segments(prefix), ' ' if prefix else '', color_segments( '%[cyan][%[cyan!]{}%[cyan]]%[reset]'.format(event_id)) if event_id else '', ' ' if event_id else '', color_segments(message) ])) self.log.set_focus(len(self.log) - 1) self.redraw() def start(self): # don't let Python warnings ruin the GUI warnings.filterwarnings('ignore') self.screen.start() self.redraw() def stop(self): self.screen.stop() warnings.filterwarnings('default') def create_progress_bar(self, description): if self.progress: del self.progress[:] return StepProgressBar(self, description) def set_success(self): if self.progress: del self.progress[:] self.progress.append( AttrMap(Text('Migration finished!', align='center'), 'done')) self.progress.append( AttrMap(Text('Please press any key...', align='center'), 'done')) self.redraw() self.wait_for_input() def wait_for_input(self): self.screen._getch(None) def set_step_banner(self, msg): if self.progress: del self.progress[:] self.progress.append(BoxAdapter(AttrMap(SolidFill('#'), 'fill'), 3)) def redraw(self): screen_size = self.screen.get_cols_rows() canvas = self.widget.render(screen_size, focus=True) self.screen.get_input() self.screen.draw_screen(screen_size, canvas)
class ScreenWidget(WidgetWrap): _info_template = ("{collection_size:>2g}" " [ {rating} | {purity} ]" " {format} {width:d}×{height:d}") def __init__(self, screen, screen_controller): self._screen = screen self._scrctrl = screen_controller self._left_border = Text(" ") # self._playpause = Text("⏸") self._info = Text(self._info_template) self._scale = Text("") self._transformations = Parenthesis("") self._tags = Parenthesis("") self._top = Columns([ (1, self._left_border), # (2, self._playpause), ('pack', self._info), ('pack', self._scale), ('pack', self._transformations), ('pack', self._tags), ], dividechars=1) self._path = PathWidget() self._root = AttrMap(Pile([ self._top, Columns([(1, self._left_border), self._path]), ]), None, 'focused screen') super().__init__(self._root) self.update() def selectable(self): return True def render(self, size, focus=False): # maybe this is better: # https://groups.google.com/a/excess.org/forum/#!topic/urwid/3Si0ZRKkFaw self._left_border.set_text("│" if focus else " ") return self._root.render(size, focus) def update(self, *_): wp = self._screen.wallpaper self._info.set_text(self._info_template.format( collection_size=len(self._screen.wallpapers), rating=("☆" if wp.rating == 0 else "★·" + str(wp.rating)), purity=("♡" if wp.purity == 0 else "♥·" + str(wp.purity)), format=wp.format, width=wp.width, height=wp.height, )) self._scale.set_text(( 'warning' if self._screen.wallpaper_scale > 1 else None, f"{self._screen.wallpaper_scale:.0%}", )) trafos = [] if wp.x_offset or wp.y_offset: trafos.append("{:+},{:+}".format(wp.x_offset, wp.y_offset)) if wp.zoom != 1: trafos.append("{:.0%}".format(wp.zoom)) if wp.transformations[2]: trafos.append(str(wp.transformations[2]) + "°") if wp.transformations[0]: trafos.append("↔") if wp.transformations[1]: trafos.append("↕") self._transformations.set_text(",".join(trafos)) self._tags.set_text(",".join(wp.tags)) self._path.set_text(wp.path) def keypress(self, size, key): log.debug("processing keypress '%s'", key) if self._top.focus.selectable(): child_result = self._top.keypress(size, key) if child_result is None: return wp = self._screen.wallpaper if key == 'delete': self._screen.wallpapers.remove_current() elif key == 'o': wp.open() elif key == 'a': self._screen.wallpapers.next() elif key == 'q': self._screen.wallpapers.prev() elif key == 's': wp.rating -= 1 elif key == 'w': wp.rating += 1 elif key == 'd': wp.purity += 1 elif key == 'e': wp.purity -= 1 elif key == 'z': wp.zoom += .05 elif key == 'Z': wp.zoom += .2 elif key == 'u': wp.zoom -= .05 elif key == 'U': wp.zoom -= .2 elif key == 'r': wp.rotate(+90) elif key == 'R': wp.rotate(-90) elif key == 'f': wp.flip_horizontal() elif key == 'F': wp.flip_vertical() elif key == 'h' or key == 'left': wp.x_offset += 10 elif key == 'H' or key == 'shift left': wp.x_offset += 100 elif key == 'l' or key == 'right': wp.x_offset -= 10 elif key == 'L' or key == 'shift right': wp.x_offset -= 100 elif key == 'k' or key == 'up': wp.y_offset += 10 elif key == 'K' or key == 'shift up': wp.y_offset += 100 elif key == 'j' or key == 'down': wp.y_offset -= 10 elif key == 'J' or key == 'shift down': wp.y_offset -= 100 elif key == '1': # 100% zoom wp.zoom = min(wp.width / self._screen.width, wp.height / self._screen.height) elif key == '!': # zoom to fit w_rel = self._screen.width / wp.width h_rel = self._screen.height / wp.height wp.zoom = min(w_rel, h_rel) / max(w_rel, h_rel) elif key == '0': # zoom to fill (default) del wp.zoom del wp.x_offset del wp.y_offset del wp.transformations elif key == 't': if wp.tags: self._tags.text += "," self._tags.editable = True # make sure to adjust when changing the layout: self._top.focus_position = 4 return elif key == 'enter' and self._tags.editable: self._tags.editable = False wp.tags = self._tags.text elif key == 'esc' and self._tags.editable: self._tags.editable = False self._tags.text = ",".join(wp.tags) else: return key self.update() self._scrctrl.display_wallpapers() def mouse_event(self, size, event, button, *_): if event == 'mouse release': self._screen.wallpaper.open() elif event == 'mouse press' and button == 4: self._screen.wallpapers.prev() self.update() self._scrctrl.display_wallpapers() elif event == 'mouse press' and button == 5: self._screen.wallpapers.next() self.update() self._scrctrl.display_wallpapers()