class Egg(object): def __init__(self, width, height): # display initializations self.__window = Window(width, height, vsync=True) self.__background_color = (0, 0, 0, 1.0) # self._fps_display = pyglet.clock.ClockDisplay() self.__key_state_handler = key.KeyStateHandler() self.__scene = None self.__window.event(self.on_draw) self.__window.push_handlers(self.__key_state_handler) self.__camera = None #schedule regular updates pyglet.clock.schedule_interval(self.update, 1 / 100.0) @property def window(self): return self.__window @property def background_color(self): return self.__background_color @background_color.setter def background_color(self, value): self.__background_color = value def register_scene(self, scene): self.__scene = scene self.__camera = Camera("main_camera", (0, 0), self.__window.height / 2.) self.__scene.root.add_child(self.__camera) self.__camera.target.x = self.__window.width / 2. self.__camera.target.y = self.__window.height / 2. self.__window.event(self.__scene.on_mouse_press) self.__window.event(self.__scene.on_mouse_release) self.__window.event(self.__scene.on_mouse_drag) self.__window.event(self.__scene.on_mouse_motion) def on_draw(self): self.__window.clear() glClearColor(*self.__background_color) if self.__scene is not None: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) # draw batches self.__camera.focus(self.__window.width, self.__window.height) self.__scene.draw() # draw specific batches for the hud self.__camera.hud_mode(self.__window.width, self.__window.height) self.__scene.draw_head_display() def update(self, dt): if self.__scene is not None: self.__scene.update(dt) self.__scene.process_keyboard(self.__key_state_handler, dt) self.__camera.update(dt)
class ExpWindow(object): def __init__(self, background_color=dark_gray, clock=mono_clock.get_time): # lazy load, partially to avoid auto-formatter that wants to # do imports, *then* dict setting from pyglet import gl from pyglet.window import Window self._background_color = Vector4f(background_color) self.clock = clock self.current_time = 0 self.prev_time = 0 # can bump down `samples` if performance is hurting config = gl.Config(depth_size=0, double_buffer=True, alpha_size=8, sample_buffers=1, samples=4, vsync=False, major_version=3, minor_version=3) display = pyglet.canvas.get_display() screen = display.get_screens()[0] self._win = Window(resizable=False, fullscreen=True, screen=screen, config=config, style='borderless', vsync=True) self._win.event(self.on_key_press) atexit.register(self._on_close) self.context = mgl.create_context(require=int('%i%i0' % (config.major_version, config.minor_version))) self.context.viewport = (0, 0, self.width, self.height) self.context.enable(mgl.BLEND) self.frame_period # do this before we've drawn anything # in principle, should be disconnected from the window # but we're saving time & mental energy self.cam = Camera(projection=height_ortho(self.width, self.height)) def on_key_press(self, symbol, modifiers): if symbol == pyglet.window.key.ESCAPE: sys.exit(1) def _on_close(self): if self._win.context: self._win.close() def flip(self): self._win.switch_to() self._win.dispatch_events() self._win.flip() self.context.clear(*self._background_color) current_time = self.clock() self.prev_time = self.current_time self.current_time = current_time return self.current_time def close(self): self._win.close() @property def dt(self): return self.current_time - self.prev_time def set_mouse_visible(self, val): self._win.set_mouse_visible(val) @property def width(self): return self._win.width @property def height(self): return self._win.height @property def background_color(self): return self._background_color @background_color.setter def background_color(self, val): if len(val) != 4: raise ValueError('Background color must be RGBA.') self._background_color.xyzw = val @property def frame_period(self): # get a whole-number version of the frame period # very coarse and fragile, should get this from the # the video mode, e.g. glfw.get_video_mode if not hasattr(self, '_frame_period'): possible = [60.0, 144.0, 240.0] # TODO: infer from machine... vals = [] for _ in range(20): self.flip() vals.append(self.dt) # chop off the first few, which are not reflective of the # "real" FPS avg = 1/(sum(vals[5:])/float(len(vals[5:]))) dff = [abs(avg - p) for p in possible] fps = [v for v, d in zip(possible, dff) if d == min(dff)] if not len(fps): self._frame_period = 1/60.0 # default else: self._frame_period = 1/fps[0] return self._frame_period
class ImageViewer(object): """A simple class for viewing images using pyglet.""" def __init__(self, caption, height, width, monitor_keyboard=False, relevant_keys=None): """ Initialize a new image viewer. Args: caption (str): the caption/title for the window height (int): the height of the window width (int): the width of the window monitor_keyboard: whether to monitor events from the keyboard relevant_keys: the relevant keys to monitor events from Returns: None """ self.caption = caption self.height = height self.width = width self.monitor_keyboard = monitor_keyboard self.relevant_keys = relevant_keys self._window = None self._pressed_keys = [] self._is_escape_pressed = False @property def is_open(self): """Return a boolean determining if this window is open.""" return self._window is not None @property def is_escape_pressed(self): """Return True if the escape key is pressed.""" return self._is_escape_pressed @property def pressed_keys(self): """Return a sorted list of the pressed keys.""" return tuple(sorted(self._pressed_keys)) def _handle_key_event(self, symbol, is_press): """ Handle a key event. Args: symbol: the symbol in the event is_press: whether the event is a press or release Returns: None """ # remap the key to the expected domain symbol = KEY_MAP.get(symbol, symbol) # check if the symbol is the escape key if symbol == key.ESCAPE: self._is_escape_pressed = is_press return # make sure the symbol is relevant if self.relevant_keys is not None and symbol not in self.relevant_keys: return # handle the press / release by appending / removing the key to pressed if is_press: self._pressed_keys.append(symbol) else: self._pressed_keys.remove(symbol) def on_key_press(self, symbol, modifiers): """Respond to a key press on the keyboard.""" self._handle_key_event(symbol, True) def on_key_release(self, symbol, modifiers): """Respond to a key release on the keyboard.""" self._handle_key_event(symbol, False) def open(self): """Open the window.""" # create a window for this image viewer instance self._window = Window( caption=self.caption, height=self.height, width=self.width, vsync=False, resizable=True, ) # add keyboard event monitors if enabled if self.monitor_keyboard: self._window.event(self.on_key_press) self._window.event(self.on_key_release) self._window.set_exclusive_keyboard() def close(self): """Close the window.""" if self.is_open: self._window.close() self._window = None def show(self, frame): """ Show an array of pixels on the window. Args: frame (numpy.ndarray): the frame to show on the window Returns: None """ # check that the frame has the correct dimensions if len(frame.shape) != 3: raise ValueError('frame should have shape with only 3 dimensions') # open the window if it isn't open already if not self.is_open: self.open() # prepare the window for the next frame self._window.clear() self._window.switch_to() self._window.dispatch_events() # create an image data object image = ImageData(frame.shape[1], frame.shape[0], 'RGB', frame.tobytes(), pitch=frame.shape[1] * -3) # send the image to the window image.blit(0, 0, width=self._window.width, height=self._window.height) self._window.flip()