コード例 #1
0
    def __init__(self, parent, id, item: ExportItem):
        super().__init__(parent,
                         id,
                         "",
                         style=wx.SYSTEM_MENU | wx.CLOSE_BOX | wx.CLIP_CHILDREN
                         | wx.CAPTION)

        if item.item_type != ExportItem.SCENE:
            raise ValueError("item_type is not ExportItem.SCENE")

        self.scene_item = item

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)

        # GLCanvas setup
        attrib_list = [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16]
        if get_platform() == 'macos':
            attrib_list += [WX_GL_CORE_PROFILE]

        self.gl_canvas = GLCanvas(self,
                                  wx.ID_ANY,
                                  self.scene_item.camera,
                                  attrib_list=attrib_list)
        self.gl_canvas.Refresh()
        self.gl_canvas.SetSize(wx.Size(*self.scene_item.size))
        sizer.Add(self.gl_canvas, 1, wx.EXPAND)

        self.SetSize(self.gl_canvas.GetSize())
        self.CenterOnParent()
        self.SetTitle(title="Scene Configurator {}".format(
            self.scene_item.camera.scene.name))
        self.Bind(wx.EVT_CLOSE, self.OnExportSceneClose)
コード例 #2
0
ファイル: gl_canvas.py プロジェクト: kkyong77/pyvistas
    def __init__(self, parent, id, camera, attrib_list=None, can_sync=False):
        super().__init__(parent, id, attribList=attrib_list)

        self.overlay = Overlay(self)
        self.camera = camera
        self.camera_controls = GLCameraControls(self, camera)

        self.selection_mode = None
        self.last_selection_mode = None
        self.selection_mode_depressed = None
        self.selection_controls = GLSelectionControls(self, camera)
        self.selection_controls.hide()

        self.can_sync = can_sync  # Indicate whether this canvas can sync with global interactor

        self.start_x = self.start_y = -1
        self._x = self._y = -1

        self.Bind(wx.EVT_MOTION, self.OnMotion)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
        self.Bind(EVT_CAMERA_SELECT_MODE, self.OnCameraSelectMode)

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        if get_platform() == 'windows':
            self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
コード例 #3
0
ファイル: static_image.py プロジェクト: kkyong77/pyvistas
    def OnPaint(self, event):

        size = self.GetClientSize()
        dc = wx.BufferedPaintDC(self)
        empty = wx.Bitmap(1, 1)

        if not self.dirty and self.cache.GetWidth() == size.GetWidth() and self.cache.GetHeight() == size.GetHeight():
            dc.DrawBitmap(self.cache, 0, 0)
        else:
            self.cache = wx.Bitmap(size.GetWidth(), size.GetHeight())
            mem_dc = wx.MemoryDC(self.cache)
            mem_dc.SetBrush(wx.Brush(wx.Colour(0, 0, 0)))
            if get_platform() == 'windows':
                mem_dc.SetBackground(wx.Brush(wx.Colour(2, 3, 4)))      # Enable transparency effect
            else:
                mem_dc.SetBackground(wx.Brush(wx.Colour(0, 0, 0, 0)))
            mem_dc.Clear()

            if self.image is not None:
                image = wx.Image(*self.image.size)
                image.SetData(self.image.convert("RGB").tobytes())
                image.SetAlpha(self.image.convert("RGBA").tobytes()[3::4])
                mem_dc.SetUserScale(*self.scale)
                mem_dc.DrawBitmap(wx.Bitmap(image), 0, 0)
                mem_dc.SetUserScale(1.0, 1.0)

            mem_dc.SelectObject(empty)
            dc.DrawBitmap(self.cache, 0, 0)
            self.dirty = False
コード例 #4
0
ファイル: app.py プロジェクト: kkyong77/pyvistas
    def OnInit(self):
        if get_platform() == 'macos':
            logs_dir = os.path.join(wx.StandardPaths.Get().UserLocalDataDir,
                                    'VISTAS', 'logs')
        else:
            logs_dir = os.path.join(wx.StandardPaths.Get().UserConfigDir,
                                    'VISTAS', 'logs')

        if not os.path.exists(logs_dir):
            os.makedirs(logs_dir)

        dictConfig({
            'version': 1,
            'formatters': {
                'verbose': {
                    'format':
                    '[%(levelname)s] [%(asctime)s:%(msecs).0f] %(message)s\n',
                    'datefmt': '%Y/%m/%d %H:%M:%S'
                }
            },
            'handlers': {
                'console': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'verbose',
                    'level': 'DEBUG',
                    'stream': 'ext://sys.stdout'
                },
                'file': {
                    'class': 'logging.handlers.TimedRotatingFileHandler',
                    'formatter': 'verbose',
                    'level': 'DEBUG',
                    'filename': os.path.join(logs_dir, 'debug.txt'),
                    'when': 'D',
                    'interval': 7
                }
            },
            'loggers': {
                'vistas': {
                    'level': 'DEBUG',
                    'handlers': ['console'] if profile == 'dev' else ['file']
                }
            }
        })

        sys.excepthook = exception_hook

        if sys.platform == 'win32':
            asyncio.set_event_loop(asyncio.ProactorEventLoop())
        else:
            asyncio.set_event_loop(asyncio.SelectorEventLoop())

        self.app_controller = AppController()

        # Download FFmpeg
        DownloadFFMpegThread().start()

        App.init = True

        return True
コード例 #5
0
ファイル: paths.py プロジェクト: kkyong77/pyvistas
def get_resources_directory():
    """ Return the current resources directory. """
    if get_platform() == 'windows':
        return os.path.join(os.getcwd(), get_assets_dir(), 'resources')
    else:
        return os.path.join(
            os.path.dirname(wx.StandardPaths.Get().ExecutablePath), '..',
            'resources')
コード例 #6
0
def get_font_path(font):
    """ Returns the full path to a font file given the name """

    if get_platform() == 'windows':
        return font

    for path in (os.path.expanduser(x) for x in MACOS_FONT_PATHS):
        match = find_exact_font(font, path)
        if match is not None:
            return match

    return font
コード例 #7
0
def get_paint_dc(win):
    """ A utility function for obtaining a BufferedPaintDC on Windows. """
    if get_platform() == 'windows':
        dc = wx.BufferedPaintDC(win)
        dc.SetBrush(
            wx.Brush((win if win.UseBgCol() else
                      win.GetParent()).GetBackgroundColour()))
        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.DrawRectangle(0, 0, *win.GetSize().Get())
    else:
        dc = wx.PaintDC(win)
    return dc
コード例 #8
0
def make_window_transparent(win):
    """ A utility function for creating transparent windows on Windows. """
    if get_platform() == 'windows':
        try:
            import ctypes
            handle = win.GetHandle()
            _winlib = ctypes.windll.user32
            old_flags = _winlib.GetWindowLongA(handle, -20)  # GWL_EXSTYLE
            old_flags |= 0x00080000  # old_flags | WS_EX_LAYERED
            _winlib.SetWindowLongA(handle, -20, old_flags)
            _winlib.SetLayeredWindowAttributes(
                handle, 262914, 255,
                3)  # 262914 = RGB(2,3,4), 3 = LWA_ALPHA | LWA_COLORKEY
        except:
            print("Something went terribly, terribly wrong!")
コード例 #9
0
    def __init__(self, parent, id):
        super().__init__(parent, id, style=wx.HSCROLL | wx.VSCROLL)
        self.captured_item = None
        self.selected_item = None
        self.items = []
        self.removed_items = []
        self.scroll_x, self.scroll_y = 0, 0

        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        if get_platform() == 'windows':
            self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_SCROLLWIN_TOP, self.OnScroll)
        self.Bind(wx.EVT_SCROLLWIN_BOTTOM, self.OnScroll)
        self.Bind(wx.EVT_SCROLLWIN_THUMBTRACK, self.OnScroll)
        self.Bind(wx.EVT_SCROLLWIN_THUMBRELEASE, self.OnScroll)
コード例 #10
0
    def __init__(self, parent, id, flythrough):
        super().__init__(parent,
                         id,
                         'Flythrough Animation',
                         style=wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT
                         | wx.FRAME_TOOL_WINDOW | wx.CAPTION | wx.SYSTEM_MENU
                         | wx.RESIZE_BORDER)

        self.flythrough = flythrough
        self._auto_keyframe = True
        self.CenterOnParent()
        size = wx.Size(600, 580)
        self.SetMinSize(size)
        self.SetSize(size)
        self.SetIcon(get_icon("flythrough.ico"))

        main_panel = wx.Panel(self, wx.ID_ANY)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(main_sizer)
        main_sizer.Add(main_panel, 1, wx.EXPAND, 1)

        main_panel_sizer = wx.BoxSizer(wx.VERTICAL)
        main_panel.SetSizer(main_panel_sizer)

        # GLCanvas setup
        attrib_list = [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16]
        if get_platform() == 'macos':
            attrib_list += [WX_GL_CORE_PROFILE]

        self.gl_canvas = GLCanvas(main_panel,
                                  wx.ID_ANY,
                                  flythrough.camera,
                                  attrib_list=attrib_list)

        # camera controls
        draggable_panel = wx.Panel(main_panel, wx.ID_ANY)
        position_label = wx.StaticText(draggable_panel, wx.ID_ANY,
                                       "Position: ")
        self.position_x = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                         self.VALUE_PER_PX)
        self.position_y = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                         self.VALUE_PER_PX)
        self.position_z = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                         self.VALUE_PER_PX)
        direction_label = wx.StaticText(draggable_panel, wx.ID_ANY,
                                        "Direction: ")
        self.direction_x = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                          self.VALUE_PER_PX)
        self.direction_y = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                          self.VALUE_PER_PX)
        self.direction_z = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                          self.VALUE_PER_PX)
        up_label = wx.StaticText(draggable_panel, wx.ID_ANY, "Up: ")
        args = {'min_value': -1.0, 'max_value': 1.0}
        self.up_x = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                   self.VALUE_PER_PX, **args)
        self.up_y = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                   self.VALUE_PER_PX, **args)
        self.up_z = DraggableValue(draggable_panel, wx.ID_ANY, 0,
                                   self.VALUE_PER_PX, **args)

        # playback
        playback_panel = wx.Panel(main_panel, wx.ID_ANY)
        self.record_button = wx.BitmapButton(
            playback_panel, wx.ID_ANY,
            get_resource_bitmap("camera_capture_2.png"))
        self.record_button.SetToolTip("Record Keyframe")
        self.backward_button = wx.BitmapButton(
            playback_panel, wx.ID_ANY,
            get_resource_bitmap("camera_backward.png"))
        self.backward_button.SetToolTip("Back one frame")
        self.play_label = get_resource_bitmap("go_button.png")
        self.pause_label = get_resource_bitmap("pause_button.png")
        self.play_pause_button = wx.BitmapButton(playback_panel, wx.ID_ANY,
                                                 self.play_label)
        self.play_pause_button.SetToolTip("Play flythrough")
        self.forward_button = wx.BitmapButton(
            playback_panel, wx.ID_ANY,
            get_resource_bitmap("camera_forward.png"))
        self.forward_button.SetToolTip("Forward one frame")
        self.reset_button = wx.BitmapButton(
            playback_panel, wx.ID_ANY, get_resource_bitmap("reset_button.png"))
        self.reset_button.SetToolTip("Reset flythrough")

        # fps
        fps_label = wx.StaticText(playback_panel, wx.ID_ANY,
                                  "Frames Per Second:")
        self.fps_ctrl = wx.lib.intctrl.IntCtrl(playback_panel,
                                               wx.ID_ANY,
                                               value=self.flythrough.fps)
        self.fps_ctrl.SetToolTip("Change Frames Per Second")

        # length
        length_label = wx.StaticText(playback_panel, wx.ID_ANY,
                                     "Length (sec):")
        self.length_ctrl = wx.lib.intctrl.IntCtrl(playback_panel,
                                                  wx.ID_ANY,
                                                  value=self.flythrough.length)
        self.length_ctrl.SetToolTip("Change length of flythrough animation")

        keyframe_panel = wx.Panel(main_panel, wx.ID_ANY)
        self.keyframe_timeline = KeyframeTimeline(keyframe_panel, wx.ID_ANY,
                                                  flythrough.num_keyframes,
                                                  flythrough.fps)

        keyframe_sizer = wx.BoxSizer(wx.VERTICAL)
        keyframe_panel.SetSizer(keyframe_sizer)
        keyframe_sizer.Add(self.keyframe_timeline, 0, wx.EXPAND | wx.RIGHT)

        draggable_sizer = wx.BoxSizer(wx.HORIZONTAL)
        draggable_sizer.Add(position_label)
        draggable_sizer.Add(self.position_x, 0, wx.RIGHT, 5)
        draggable_sizer.Add(self.position_y, 0, wx.RIGHT, 5)
        draggable_sizer.Add(self.position_z, 0, wx.RIGHT, 5)
        draggable_sizer.AddStretchSpacer(5)
        draggable_sizer.Add(direction_label)
        draggable_sizer.Add(self.direction_x, 0, wx.RIGHT, 5)
        draggable_sizer.Add(self.direction_y, 0, wx.RIGHT, 5)
        draggable_sizer.Add(self.direction_z, 0, wx.RIGHT, 5)
        draggable_sizer.AddStretchSpacer(5)
        draggable_sizer.Add(up_label)
        draggable_sizer.Add(self.up_x, 0, wx.RIGHT, 5)
        draggable_sizer.Add(self.up_y, 0, wx.RIGHT, 5)
        draggable_sizer.Add(self.up_z, 0, wx.RIGHT, 5)
        draggable_panel.SetSizer(draggable_sizer)

        playback_sizer = wx.BoxSizer(wx.HORIZONTAL)
        playback_sizer.Add(self.record_button, 0,
                           wx.ALIGN_CENTRE_VERTICAL | wx.ALL, 5)
        playback_sizer.Add(self.backward_button, 0,
                           wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        playback_sizer.Add(self.play_pause_button, 0,
                           wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        playback_sizer.Add(self.reset_button, 0,
                           wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        playback_sizer.Add(self.forward_button, 0,
                           wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        playback_sizer.AddStretchSpacer()
        playback_sizer.Add(fps_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 1)
        playback_sizer.Add(self.fps_ctrl, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                           5)
        playback_sizer.Add(length_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                           1)
        playback_sizer.Add(self.length_ctrl, 0,
                           wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        playback_panel.SetSizer(playback_sizer)

        main_panel_sizer.Add(self.gl_canvas, 1, wx.ALL | wx.EXPAND, 10)
        main_panel_sizer.Add(keyframe_panel, 0, wx.ALL | wx.EXPAND, 10)
        main_panel_sizer.Add(draggable_panel, 0, wx.ALL | wx.EXPAND, 10)
        main_panel_sizer.Add(playback_panel, 0, wx.ALL | wx.EXPAND, 5)

        self.timer = wx.Timer(self, wx.ID_ANY)

        # Bind events
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.record_button.Bind(wx.EVT_BUTTON, self.RecordKeyframe)
        self.play_pause_button.Bind(wx.EVT_BUTTON, self.OnPlayPause)
        self.reset_button.Bind(wx.EVT_BUTTON, self.OnReset)
        self.backward_button.Bind(wx.EVT_BUTTON, self.OnBackward)
        self.forward_button.Bind(wx.EVT_BUTTON, self.OnForward)

        self.fps_ctrl.Bind(wx.EVT_TEXT, self.OnFPSChange)
        self.length_ctrl.Bind(wx.EVT_TEXT, self.OnLengthChange)

        self.keyframe_timeline.Bind(EVT_KEYTIMELINE, self.OnKeyframe)

        # bind events to gl_canvas
        self.gl_canvas.Bind(wx.EVT_RIGHT_DOWN, self.OnRightClick)
        self.gl_canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnCanvasWheel)
        self.gl_canvas.Bind(wx.EVT_MOTION, self.OnCanvasMotion)

        # bind draggable events
        self.position_x.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.position_y.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.position_z.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.direction_x.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.direction_y.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.direction_z.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.up_x.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.up_y.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)
        self.up_z.Bind(EVT_DRAG_VALUE_EVENT, self.OnDragValue)

        self.RecalculateKeyframeIndices()
        self.flythrough.update_camera_to_keyframe(0)
        self.gl_canvas.Refresh()
        self.UpdateDraggablesFromCamera()
        self.Refresh()
        self.gl_canvas.camera_interactor.reset_position(False)

        self.active_dialogs.append(self)
コード例 #11
0
ファイル: paths.py プロジェクト: kkyong77/pyvistas
def get_config_dir():
    if get_platform() == 'macos':
        return os.path.join(wx.StandardPaths.Get().UserLocalDataDir, 'VISTAS')
    else:
        return os.path.join(wx.StandardPaths.Get().UserConfigDir, 'VISTAS')
コード例 #12
0
ファイル: main.py プロジェクト: kkyong77/pyvistas
    def __init__(self, parent, id):
        super().__init__(parent, id, 'VISTAS')

        self.SetSize(1200, 800)
        self.CenterOnScreen()

        self.graph_panels = []

        menu_bar = wx.MenuBar()

        file_menu = wx.Menu()
        file_menu.Append(self.MENU_FILE_NEW, '&New Project\tCtrl+n')
        file_menu.AppendSeparator()
        file_menu.Append(self.MENU_FILE_OPEN, '&Open Project\tCtrl+o')
        file_menu.AppendSeparator()
        file_menu.Append(self.MENU_FILE_SAVE, '&Save Project\tCtrl+s')
        file_menu.Append(self.MENU_FILE_SAVEAS,
                         'Save Project &As...\tCtrl+Shift+s')
        file_menu.AppendSeparator()
        file_menu.Append(self.MENU_FILE_ADDDATA, 'Add &Data...')
        if get_platform() == 'windows':
            file_menu.AppendSeparator()
        file_menu.Append(wx.ID_ABOUT, '&About')
        file_menu.Append(wx.ID_PREFERENCES, '&Preferences')
        file_menu.Append(wx.ID_EXIT, '&Quit')
        menu_bar.Append(file_menu, '&File')

        self.view_menu = wx.Menu()
        self.view_menu.Append(self.MENU_VIEW_ADD_VIEWER, '&Add Scene Viewer')
        self.view_menu.Append(self.MENU_VIEW_REMOVE_VIEWER,
                              '&Remove Scene Viewer')
        self.view_menu.Append(self.MENU_VIEW_ADD_GRAPH, 'Add &Graph Viewer')
        self.view_menu.Append(self.MENU_VIEW_REMOVE_GRAPH,
                              'Remove Graph Viewer')
        self.view_menu.AppendSeparator()
        self.view_menu.Append(self.MENU_VIEW_COLLAPSE,
                              '&Collapse Project Panel')
        menu_bar.Append(self.view_menu, '&View')

        export_menu = wx.Menu()
        export_menu.Append(self.MENU_EXPORT_EXPORT, '&Export...\tCtrl+e')
        export_current_menu = wx.Menu()
        export_current_menu.Append(self.MENU_EXPORT_CURRENT_COPY,
                                   '&Copy to Clipboard')
        export_current_menu.Append(self.MENU_EXPORT_CURRENT_SAVE,
                                   '&Save to File...')
        export_menu.AppendSubMenu(export_current_menu, 'Current View')
        menu_bar.Append(export_menu, '&Export')

        window_menu = wx.Menu()
        window_menu.Append(self.MENU_WINDOW_PLUGINS, '&Plugins')
        menu_bar.Append(window_menu, '&Window')

        stats_menu = wx.Menu()
        stats_menu.Append(self.MENU_LINREG, '&Linear Regression')
        stats_menu.Append(self.MENU_PCA, '&PCA')
        menu_bar.Append(stats_menu, '&Statistics')

        debug_menu = wx.Menu()
        debug_menu.Append(self.MENU_DEBUG_TOGGLE_WIREFRAME,
                          'Toggle &Wireframe')
        debug_menu.Append(self.MENU_DEBUG_TOGGLE_SELECTION_VIEW,
                          'Toggle &Selection View')
        menu_bar.Append(debug_menu, '&Debug')

        help_menu = wx.Menu()
        help_menu.Append(self.MENU_HELP_REPORT_ISSUE, '&Report an issue')
        menu_bar.Append(help_menu, '&Help')

        self.SetMenuBar(menu_bar)

        toolbar = self.CreateToolBar()
        toolbar.SetToolBitmapSize(wx.Size(20, 20))
        toolbar.AddTool(self.MENU_FILE_NEW, 'New Project',
                        get_resource_bitmap('new_workspace.png'),
                        'Create a new project (Ctrl+N)')
        toolbar.AddTool(self.MENU_FILE_OPEN, 'Open Project',
                        get_resource_bitmap('open_workspace.png'),
                        'Open an existing project file (Ctrl+O)')
        toolbar.AddTool(self.MENU_FILE_SAVE, 'Save Project',
                        get_resource_bitmap('save_workspace.png'),
                        'Save the current project (Ctrl+S)')
        toolbar.AddSeparator()
        toolbar.AddTool(self.MENU_FILE_ADDDATA, 'Add Data',
                        get_resource_bitmap('load_data.png'),
                        'Add data to project')
        toolbar.AddSeparator()
        toolbar.AddTool(self.MENU_FLYTHROUGH_GENERATE, 'Create Flythrough',
                        get_resource_bitmap('flythrough.png'),
                        'Generate flythrough')
        toolbar.AddTool(self.MENU_SYNC_CAMERAS, 'Sync Cameras',
                        get_resource_bitmap('camera_sync.png'),
                        'Sync all viewer windows', wx.ITEM_CHECK)
        toolbar.AddSeparator()
        toolbar.AddTool(self.MENU_OPEN_TIMELINE_FILTER, 'Open Timeline Filter',
                        get_resource_bitmap('glyphicons-541-hourglass.png'),
                        'Set timeline filter options')
        toolbar.Realize()

        self.SetStatusBar(MainStatusBar(self, wx.ID_ANY))

        main_panel = wx.Panel(self, wx.ID_ANY)
        self.main_splitter = wx.SplitterWindow(
            main_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize,
            wx.SP_3DSASH | wx.SP_LIVE_UPDATE)
        self.main_sash_position = None
        self.left_panel = wx.Panel(self.main_splitter, wx.ID_ANY)
        self.right_panel = wx.Panel(self.main_splitter, wx.ID_ANY)
        self.viewer_container_panel = ViewerContainerPanel(
            self.right_panel, wx.ID_ANY)
        self.timeline_panel = TimelinePanel(self.right_panel, wx.ID_ANY)

        self.main_splitter.SplitVertically(self.left_panel, self.right_panel,
                                           300)

        main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(main_sizer)

        main_panel_sizer = wx.BoxSizer(wx.VERTICAL)
        main_panel.SetSizer(main_panel_sizer)
        main_panel_sizer.Add(self.main_splitter, 1, wx.EXPAND)
        main_sizer.Add(main_panel, 1, wx.EXPAND)

        self.left_splitter = wx.SplitterWindow(
            self.left_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize,
            wx.SP_3DSASH | wx.SP_LIVE_UPDATE)
        self.left_sash_position = 0
        self.project_panel = ProjectPanel(self.left_splitter, wx.ID_ANY)
        self.options_panel = OptionsPanel(self.left_splitter, wx.ID_ANY)

        self.left_splitter.SplitHorizontally(self.project_panel,
                                             self.options_panel, 0)
        self.left_splitter.Unsplit(self.options_panel)

        left_panel_sizer = wx.BoxSizer(wx.VERTICAL)
        self.left_panel.SetSizer(left_panel_sizer)
        left_panel_sizer.Add(self.left_splitter, 1,
                             wx.EXPAND | wx.LEFT | wx.BOTTOM, 5)

        self.right_panel_sizer = wx.BoxSizer(wx.VERTICAL)
        self.right_panel.SetSizer(self.right_panel_sizer)
        self.right_panel_sizer.Add(self.viewer_container_panel, 1, wx.EXPAND)
        self.right_panel_sizer.Add(self.timeline_panel, 0, wx.EXPAND)

        self.expand_button = ExpandButton()
        self.viewer_container_panel.GetMainViewerPanel(
        ).gl_canvas.overlay.add_button(self.expand_button)

        self.project_controller = ProjectController(self.project_panel)
        self.Bind(EVT_COMMAND_PROJECT_CHANGED, self.OnProjectChanged)
        self.main_splitter.Bind(wx.EVT_SPLITTER_DCLICK, self.OnSplitterDClick)
        self.expand_button.Bind(wx.EVT_BUTTON, self.OnExpandButton)

        # Listen to plugin events
        self.Bind(EVT_PLUGIN_OPTION, self.OnPluginOption)
        self.Bind(EVT_REDISPLAY, self.OnRedisplay)
        self.Bind(EVT_NEW_LEGEND, self.OnNewLegend)
        self.Bind(EVT_TIMELINE_CHANGED, self.OnTimeline)
        self.Bind(EVT_MESSAGE, self.OnMessage)
        self.Bind(EVT_CAMERA_MODE_CHANGED, self.OnCameraModeChanged)
        self.Bind(EVT_VISUALIZATION_UPDATED, self.OnVisualizationUpdated)