Пример #1
0
    def UpdateFontSize(self):
        self.__fnt_tiny = wx.Font(ScaleFont(self.GetSettings().playback_font_size), wx.NORMAL, wx.NORMAL, wx.NORMAL)
        self.__fnt_bold_med = wx.Font(ScaleFont(self.GetSettings().playback_font_size), wx.NORMAL, wx.NORMAL, wx.BOLD)
        self.__fnt = wx.Font(ScaleFont(self.GetSettings().playback_font_size), wx.NORMAL, wx.NORMAL, wx.NORMAL)
        self.__fnt_hl = self.__fnt.Underlined()

        self.SetFont(self.__fnt)
        self.__drop_clock.SetFont(self.__fnt)
        self.__static_curcycle.SetFont(self.__fnt_bold_med)
        self.__static_curtick.SetFont(self.__fnt_tiny)
        self.__hl_start.SetFont(self.__fnt_hl)
        self.__hl_end.SetFont(self.__fnt_hl)
        self.__btn_rw_hold.SetFont(self.__fnt)
        self.__btn_back30.SetFont(self.__fnt)
        self.__btn_back10.SetFont(self.__fnt)
        self.__btn_back3.SetFont(self.__fnt)
        self.__btn_back1.SetFont(self.__fnt)
        self.__btn_forward1.SetFont(self.__fnt)
        self.__btn_forward3.SetFont(self.__fnt)
        self.__btn_forward10.SetFont(self.__fnt)
        self.__btn_forward30.SetFont(self.__fnt)
        self.__btn_ff_hold.SetFont(self.__fnt)
        self.__txt_goto.SetFont(self.__fnt)
        self.__btn_goto.SetFont(self.__fnt)
        self.__static_playback_speed_units.SetFont(self.__fnt)
        self.__spin_playback_speed.SetFont(self.__fnt)
        self.__btn_playpause.SetFont(self.__fnt)

        self.Layout()
Пример #2
0
    def __init__(self, parent, id, title, elements=None):
        size = (500, 300)
        self.__parent = parent
        wx.Frame.__init__(self, parent, id, "Element Properties Editor", size = size, \
                          style = wx.RESIZE_BORDER | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.STAY_ON_TOP)

        # used for temporary display of error messages
        self.__timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.Draw, self.__timer)
        self.SetBackgroundColour("WHITE")
        self.CreateStatusBar()

        self.__fnt_location = wx.Font(ScaleFont(12), wx.NORMAL, wx.NORMAL,
                                      wx.NORMAL)
        self.SetFont(self.__fnt_location)

        self.__sizer = wx.BoxSizer(wx.VERTICAL)
        self.__list = ElementPropertyList(self, id)

        self.__sizer.Add(self.__list, 1, wx.EXPAND)
        self.SetSizerAndFit(self.__sizer)
        self.SetAutoLayout(True)

        self.Show(True)
        self.Move((200, 200))  # better than center

        self.Bind(wx.EVT_CLOSE, self.Hide)
        self.Bind(wx.EVT_KEY_DOWN, self.__OnKeyDown)
Пример #3
0
    def __init__(self, parent, id, title, layout_context):
        self.__layout_context = layout_context
        size = (600, 100)
        self.__parent = parent
        wx.Frame.__init__(self,
                          parent,
                          id,
                          title + " properties dialog",
                          size,
                          style=wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION
                          | wx.CLOSE_BOX | wx.CLIP_CHILDREN)

        self.__fnt_location = wx.Font(ScaleFont(12), wx.NORMAL, wx.NORMAL,
                                      wx.NORMAL)
        self.SetFont(self.__fnt_location)

        # work could be done to make these prettier
        self.__sizer = wx.BoxSizer(wx.VERTICAL)
        self.__list = VarsListCtrl(
            self,
            id,
            style=wx.LC_REPORT
            | wx.BORDER_NONE
            | wx.LC_SORT_ASCENDING,
            variables=layout_context.GetLocationVariables())
        self.__sizer.Add(self.__list, 1, wx.EXPAND)
        self.SetSizer(self.__sizer)
        self.SetAutoLayout(True)
Пример #4
0
    def __init__(self, frame):
        super(LayoutExitDialog, self).__init__(frame, -1, title='Unsaved Changes to this Layout')

        layout_name = frame.GetContext().GetLayout().GetFilename()
        if layout_name is not None:
            layout_name = os.path.split(layout_name)[-1]
        else:
            layout_name = '<unsaved layout>'

        fnt_filename = wx.Font(ScaleFont(13), wx.NORMAL, wx.NORMAL, wx.NORMAL, wx.FONTWEIGHT_BOLD)

        lbl_filename = wx.StaticText(self, -1, layout_name)
        lbl_filename.SetFont(fnt_filename)

        BTN_ICON_SIZE = 16
        bmp_discard = wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_MESSAGE_BOX, (BTN_ICON_SIZE,BTN_ICON_SIZE))
        bmp_save = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_MESSAGE_BOX, (BTN_ICON_SIZE,BTN_ICON_SIZE))
        bmp_return = wx.ArtProvider.GetBitmap(wx.ART_GO_BACK, wx.ART_MESSAGE_BOX, (BTN_ICON_SIZE,BTN_ICON_SIZE))

        STYLE = platebtn.PB_STYLE_SQUARE
        btn_discard = platebtn.PlateButton(self, wx.ID_DELETE, ' Discard Changes ', bmp_discard, style=STYLE)
        btn_discard.SetBackgroundColour((240,80,80))
        btn_save = platebtn.PlateButton(self, wx.ID_SAVE, ' Save ', bmp_save, style=STYLE)
        btn_save.SetBackgroundColour((170,170,170))
        btn_return = platebtn.PlateButton(self, wx.ID_BACKWARD, ' Back to Editing ', bmp_return, style=STYLE)
        btn_return.SetBackgroundColour((170,170,170))

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(btn_discard, 0, wx.ALL, 4)
        button_sizer.Add((50,1), 0)
        button_sizer.Add(btn_save, 0, wx.ALL, 4)
        button_sizer.Add(btn_return, 0, wx.ALL, 4)

        message_sizer = wx.BoxSizer(wx.VERTICAL)
        message_sizer.Add((1,10), 0)
        message_sizer.Add(wx.StaticText(self, -1, 'Layout has been modified since last save!'), 0, wx.ALL, 8)
        message_sizer.Add((1,1), 0)
        message_sizer.Add(lbl_filename, 0, wx.ALL, 8)
        message_sizer.Add((1,20), 0)

        bmp = wx.ArtProvider.GetBitmap(wx.ART_WARNING, wx.ART_MESSAGE_BOX, (64,64))
        upper_sizer = wx.BoxSizer(wx.HORIZONTAL)
        upper_sizer.Add(wx.StaticBitmap(self, -1, bmp, (0,0), (bmp.GetWidth(), bmp.GetHeight())), 0, wx.ALL, 10)
        upper_sizer.Add(message_sizer, 1, wx.EXPAND)

        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(upper_sizer, 1, wx.EXPAND)
        main_sizer.Add(button_sizer, 0, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(main_sizer)
        self.Layout()
        self.Fit()

        def GenAssigner(val):
            def Assigner(*args):
                self.__result = val
                self.EndModal(val)
            return Assigner

        btn_discard.Bind(wx.EVT_BUTTON, GenAssigner(wx.ID_DELETE))
        btn_save.Bind(wx.EVT_BUTTON, GenAssigner(wx.ID_SAVE))
        btn_return.Bind(wx.EVT_BUTTON, GenAssigner(wx.ID_CANCEL))
        self.Bind(wx.EVT_CLOSE, GenAssigner(wx.ID_CANCEL))

        self.__result = None
Пример #5
0
    def  __init__(self, parent, elpropsdlg):
        '''
        @param parent Parent Layout_Frame
        @param elpropsdlg Element Properties Dialog for this frame
        '''
        self.__layout_frame = parent
        self.__el_props_dlg = elpropsdlg

        self.__db = parent.GetContext().dbhandle.database
        self.__tree_dict = {}

        # Create Controls

        title = "Locations for {0}".format(self.__db.filename)
        wx.Frame.__init__(self, parent, -1, title, size = (1025, 600),
                          style = wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.CAPTION | wx.CLOSE_BOX | wx.SYSTEM_MENU)

        self.__fnt_small = wx.Font(ScaleFont(12), wx.NORMAL, wx.NORMAL, wx.NORMAL)

        static_heading = wx.StaticText(self, -1, "Showing all locations for:")
        static_filename = wx.StaticText(self, -1, self.__db.filename)
        static_filename.SetFont(self.__fnt_small)
        filter_label = wx.StaticText(self, -1, "Filter")
        self.__filter_ctrl = wx.TextCtrl(self, -1)

        # TODO style had wx.TR_EXTENDED but seemed missing from the API
        self.__tree_ctrl = wx.lib.gizmos.treelistctrl.TreeListCtrl(self,
                                                                   -1,
                                                                   size = wx.Size(-1, 100),
                                                                   style = wx.TR_DEFAULT_STYLE | wx.TR_FULL_ROW_HIGHLIGHT | \
                                                                   wx.TR_MULTIPLE | wx.TR_HIDE_ROOT)
        self.__tree_ctrl.AddColumn('Node')
        self.__tree_ctrl.SetColumnWidth(self.COL_NODE, 400)
        self.__tree_ctrl.AddColumn('Clock')
        self.__tree_ctrl.SetColumnWidth(self.COL_CLOCK, 100)
        self.__tree_ctrl.AddColumn('Full Path')
        self.__tree_ctrl.SetColumnWidth(self.COL_PATH, 500)

        tree = self.__db.location_manager.location_tree
        self.__root = self.__tree_ctrl.AddRoot('<root>')
        self.__use_filter = False
        self.__SetLocationTree(tree)
        # User preference is to NOT expand tree. ## self.__tree_ctrl.ExpandAll()

        self.__btn_create_element = wx.Button(self, -1, "Create Element(s)")
        self.__btn_create_element.SetToolTip('Creates a new element for each of the selected ' \
                                             'locations in the tree')
        self.__btn_set = wx.Button(self, -1, "Set Location to Selected")
        self.__btn_set.SetToolTip('Sets the location string for all selected elements to the ' \
                                  'selected location in the tree. Generally, the selected ' \
                                  'location should be a leaf node')

        # Prepare

        self.__UpdateButtons()

        # Bindings

        self.__tree_ctrl.Bind(wx.EVT_TREE_SEL_CHANGED, self.__OnTreeSelChanged)
        self.__btn_create_element.Bind(wx.EVT_BUTTON, self.__OnCreateElements)
        self.__btn_set.Bind(wx.EVT_BUTTON, self.__OnSetLocation)
        self.__filter_ctrl.Bind(wx.EVT_TEXT, self.__OnFilterChanged)
        self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide()) # Hide instead of closing
        self.__tree_ctrl.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.__OnExpandItem)

        # Layout

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(self.__btn_create_element, 0, wx.RIGHT, 2)
        button_sizer.Add(self.__btn_set, 0)
        button_sizer.Add((1, 1), 1, wx.EXPAND)

        sz = wx.BoxSizer(wx.VERTICAL)
        sz.Add((1, 3), 0)
        sz.Add(static_heading, 0, wx.EXPAND | wx.ALL, 2)
        sz.Add(static_filename, 0, wx.EXPAND | wx.ALL, 4)
        filter_sizer = wx.BoxSizer(wx.HORIZONTAL)
        filter_sizer.Add((2, 1), 0)
        filter_sizer.Add(filter_label, 0, wx.EXPAND | wx.ALL)
        filter_sizer.Add(self.__filter_ctrl, 0, wx.EXPAND | wx.ALL)
        sz.Add(filter_sizer, 0, wx.EXPAND | wx.ALL)
        sz.Add(self.__tree_ctrl, 2, wx.EXPAND | wx.ALL, 1)
        sz.Add(button_sizer, 0, wx.EXPAND | wx.ALL, 4)
        self.SetSizer(sz)
        self.Bind(wx.EVT_ACTIVATE, self.__OnActivate)

        # Fire a text event to load the filter for the first time
        wx.PostEvent(self.__filter_ctrl.GetEventHandler(), wx.PyCommandEvent(wx.EVT_TEXT.typeId, self.GetId()))
Пример #6
0
    def __init__(self, frame, layout):
        self.__parent = frame
        self.__layout = layout
        wx.Panel.__init__(self, self.__parent, wx.ID_ANY)

        self.__db = self.__parent.GetContext().dbhandle.database
        self.__qapi = self.__parent.GetContext().dbhandle.api

        # Vars
        # keep a list of clock instances that are referenced
        self.__referenced_clocks = self.__parent.GetContext().GetVisibleClocks(
        )
        self.__current_clock = None
        self.__is_auto_clock = False
        # Dummy to hold play rate when playing. If None, refers to the play speed spin
        # control. Otherwise indicates an actual play speed. Set when starting and pausing
        # playback
        self.__play_rate = None
        self.__last_play_tick = None  # Timestamp of last play tick
        self.__accum_play_cycle_fraction = None  # Cumulative advance time across multiple cycles
        self.__slider_hooked = False  # Is user controlling the slider (True prevents auto-updates to the slider)

        # Fonts & Colors

        self.__fnt_tiny = wx.Font(ScaleFont(12), wx.NORMAL, wx.NORMAL,
                                  wx.NORMAL)
        self.__fnt_bold_med = wx.Font(ScaleFont(12), wx.NORMAL, wx.NORMAL,
                                      wx.BOLD)
        self.__fnt = wx.Font(ScaleFont(12), wx.NORMAL, wx.NORMAL, wx.NORMAL)
        self.SetFont(self.__fnt)
        self.__med_blue = wx.Colour(0, 0, 220)

        # Controls

        self.__play_timer = wx.Timer(
            self)  # Placeholder. Will be created as needed

        self.__hl_start = hl.HyperLinkCtrl(self, wx.ID_ANY, label=' ', URL='')
        self.__hl_start.SetToolTip(
            'Jump to first cycle in the transaction database')
        self.__hl_start.AutoBrowse(False)
        self.__hl_end = hl.HyperLinkCtrl(self, wx.ID_ANY, label=' ', URL='')
        self.__hl_end.SetToolTip(
            'Jump to final cycle in the transaction database')
        self.__hl_end.AutoBrowse(False)

        self.__time_slider = wx.Slider(self, wx.ID_ANY)
        self.__time_slider.SetToolTip(
            'Displays the current position in the transaction database. Slide this bar to quickly move around'
        )
        # self.__time_slider.SetThumbLength(2)
        # self.__time_slider.SetLineSize(1)
        self.__time_slider.SetRange(0, self.TIME_SLIDER_RANGE)
        self.__time_slider.SetForegroundColour(self.__med_blue)

        clocks = self.__db.clock_manager.getClocks()
        clock_choices = ['<any clk edge>']
        self.__ALL_CLOCKS = 0
        for clk in clocks:
            clock_choices.append(clk.name)

        self.__drop_clock = wx.ComboBox(self,
                                        choices=clock_choices,
                                        size=(150, -1),
                                        style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.__drop_clock.SetToolTip('Current clock domain. This dictates the units in which this ' \
                                           'frame prints time. Playback and step controls will operate ' \
                                           'on this clock domain. Select any clock domain from the list.')
        self.__drop_clock.SetFont(self.__fnt)
        # self.__drop_clock.SetValue(ClockManager.HYPERCYCLE_CLOCK_NAME)
        self.__drop_clock.SetSelection(0)

        curtime_msg = 'Current cycle in the current clock domain. Also shows ' \
                      'the hyper-tick (common time) tick count of this frame'

        self.__static_curcycle = wx.StaticText(self, wx.ID_ANY, size=(90, -1))
        self.__static_curcycle.SetToolTip(curtime_msg)
        self.__static_curcycle.SetForegroundColour(self.__med_blue)
        self.__static_curcycle.SetFont(self.__fnt_bold_med)

        self.__static_curtick = wx.StaticText(self, wx.ID_ANY, size=(90, -1))
        self.__static_curtick.SetToolTip(curtime_msg)
        self.__static_curtick.SetForegroundColour(self.__med_blue)
        self.__static_curtick.SetFont(self.__fnt_tiny)

        PLAYBACK_BUTTON_WIDTH = 40
        self.__btn_rw_hold = ShyButton(self,
                                       wx.ID_ANY,
                                       '<<',
                                       size=(PLAYBACK_BUTTON_WIDTH, -1),
                                       style=wx.BU_EXACTFIT)
        self.__btn_rw_hold.SetToolTip('Rewinds while left-mouse is held. Using space or enter to activate ' \
                                            'this control also rewinds but rate is dictated by keyboard repeat rate')
        self.__btn_back30 = ShyButton(self,
                                      wx.ID_ANY,
                                      '-30',
                                      size=(PLAYBACK_BUTTON_WIDTH, -1),
                                      style=wx.BU_EXACTFIT)
        self.__btn_back30.SetToolTip(
            'Steps back 30 cycle in the current clock domain')
        self.__btn_back10 = ShyButton(self,
                                      wx.ID_ANY,
                                      '-10',
                                      size=(PLAYBACK_BUTTON_WIDTH, -1),
                                      style=wx.BU_EXACTFIT)
        self.__btn_back10.SetToolTip(
            'Steps back 10 cycle in the current clock domain')
        self.__btn_back3 = ShyButton(self,
                                     wx.ID_ANY,
                                     '-3',
                                     size=(PLAYBACK_BUTTON_WIDTH, -1),
                                     style=wx.BU_EXACTFIT)
        self.__btn_back3.SetToolTip(
            'Steps back 3 cycle in the current clock domain')
        self.__btn_back1 = ShyButton(self,
                                     wx.ID_ANY,
                                     '-1',
                                     size=(PLAYBACK_BUTTON_WIDTH, -1),
                                     style=wx.BU_EXACTFIT)
        self.__btn_back1.SetToolTip(
            'Steps back 1 cycle in the current clock domain')
        self.__btn_forward1 = ShyButton(self,
                                        wx.ID_ANY,
                                        '+1',
                                        size=(PLAYBACK_BUTTON_WIDTH, -1),
                                        style=wx.BU_EXACTFIT)
        self.__btn_forward1.SetToolTip(
            'Steps forward 1 cycle in the current clock domain')
        self.__btn_forward3 = ShyButton(self,
                                        wx.ID_ANY,
                                        '+3',
                                        size=(PLAYBACK_BUTTON_WIDTH, -1),
                                        style=wx.BU_EXACTFIT)
        self.__btn_forward3.SetToolTip(
            'Steps forward 3 cycle in the current clock domain')
        self.__btn_forward10 = ShyButton(self,
                                         wx.ID_ANY,
                                         '+10',
                                         size=(PLAYBACK_BUTTON_WIDTH, -1),
                                         style=wx.BU_EXACTFIT)
        self.__btn_forward10.SetToolTip(
            'Steps forward 10 cycle in the current clock domain')
        self.__btn_forward30 = ShyButton(self,
                                         wx.ID_ANY,
                                         '+30',
                                         size=(PLAYBACK_BUTTON_WIDTH, -1),
                                         style=wx.BU_EXACTFIT)
        self.__btn_forward30.SetToolTip(
            'Steps forward 30 cycle in the current clock domain')
        self.__btn_ff_hold = ShyButton(self,
                                       wx.ID_ANY,
                                       '>>',
                                       size=(PLAYBACK_BUTTON_WIDTH, -1),
                                       style=wx.BU_EXACTFIT)
        self.__btn_ff_hold.SetToolTip('Fast-Forwards while left-mouse is held. Using space or enter to activate ' \
                                            'this control also fast-forwards but rate is dictated by keyboard repeat rate')

        self.__txt_goto = wx.TextCtrl(self,
                                      wx.ID_ANY,
                                      size=(60, -1),
                                      value='+10')
        self.__txt_goto.SetToolTip('Enter an absolute or relative cycle number (in the current clock domain) ' \
                                   'to jump to. Decimal, octal (0NNN), hex (0xNNN), or binary (0bNNN) ' \
                                   'literals are all acceptable inputs. Prefixing the number with a + or ' \
                                   '- sign will result in this value being interpreted as a relative value ' \
                                   'from the current cycle. Press Enter or click "jump" to jump to the ' \
                                   'specified cycle')
        self.__btn_goto = ShyButton(self,
                                    wx.ID_ANY,
                                    'jump',
                                    size=(60, -1),
                                    style=wx.BU_EXACTFIT)
        self.__btn_goto.SetToolTip(
            'Jump to the absolute or relative cycle specified in the jump text control'
        )

        self.__static_playback_speed_units = wx.StaticText(
            self, wx.ID_ANY, 'cyc/\nsec')
        self.__spin_playback_speed = wx.SpinCtrl(self, wx.ID_ANY, '1')
        self.__spin_playback_speed.SetToolTip('Set the number of cycles (in current clock domain) to display ' \
                                                    'per second during playback. This can be positive or negative. ' \
                                                    'A value of 0 prevents playing')
        self.__spin_playback_speed.SetRange(-self.MAX_PLAY_RATE,
                                            self.MAX_PLAY_RATE)
        self.__spin_playback_speed.SetValue(1.5)
        self.__btn_playpause = ShyButton(self,
                                         wx.ID_ANY,
                                         self.LABEL_PLAY,
                                         size=(45, -1),
                                         style=wx.BU_EXACTFIT)
        self.__btn_playpause.SetToolTip('Automatically steps through cycles (in the current clock domain) at ' \
                                              'the rate (positive or negative) specified by the "cyc/sec" ' \
                                              'spin-control beside this button')

        # Event Bindings

        self.Bind(wx.EVT_TIMER, self.__OnPlayTimer, self.__play_timer)
        self.Bind(wx.EVT_CHILD_FOCUS, self.__OnChildFocus)

        self.__hl_start.Bind(hl.EVT_HYPERLINK_LEFT, self.__OnGotoStart)
        self.__hl_end.Bind(hl.EVT_HYPERLINK_LEFT, self.__OnGotoEnd)
        self.__time_slider.Bind(wx.EVT_SLIDER, self.__OnTimeSlider)
        self.__time_slider.Bind(wx.EVT_LEFT_DOWN, self.__OnTimeSliderLeftDown)
        self.__time_slider.Bind(wx.EVT_LEFT_UP, self.__OnTimeSliderLeftUp)
        self.__time_slider.Bind(wx.EVT_CHAR, self.__OnTimeSliderChar)
        self.__drop_clock.Bind(wx.EVT_COMBOBOX, self.__OnClockSelect)

        self.__btn_rw_hold.Bind(wx.EVT_LEFT_DOWN, self.__OnRWButtonDown)
        self.__btn_rw_hold.Bind(wx.EVT_LEFT_UP, self.__OnRWButtonUp)
        self.__btn_rw_hold.Bind(wx.EVT_KEY_DOWN, self.__OnRWKeyDown)
        self.__btn_back30.Bind(wx.EVT_BUTTON, self.__OnBack30)
        self.__btn_back10.Bind(wx.EVT_BUTTON, self.__OnBack10)
        self.__btn_back3.Bind(wx.EVT_BUTTON, self.__OnBack3)
        self.__btn_back1.Bind(wx.EVT_BUTTON, self.__OnBack1)
        self.__btn_back1.Bind(wx.EVT_LEFT_DOWN, self.__OnBack1LeftDown)
        self.__btn_back1.Bind(wx.EVT_LEFT_UP, self.__OnBack1LeftUp)
        self.__btn_forward1.Bind(wx.EVT_BUTTON, self.__OnForward1)
        self.__btn_forward1.Bind(wx.EVT_LEFT_DOWN, self.__OnForward1LeftDown)
        self.__btn_forward1.Bind(wx.EVT_LEFT_UP, self.__OnForward1LeftUp)
        self.__btn_forward3.Bind(wx.EVT_BUTTON, self.__OnForward3)
        self.__btn_forward10.Bind(wx.EVT_BUTTON, self.__OnForward10)
        self.__btn_forward30.Bind(wx.EVT_BUTTON, self.__OnForward30)
        self.__btn_ff_hold.Bind(wx.EVT_LEFT_DOWN, self.__OnFFButtonDown)
        self.__btn_ff_hold.Bind(wx.EVT_LEFT_UP, self.__OnFFButtonUp)
        self.__btn_ff_hold.Bind(wx.EVT_KEY_DOWN, self.__OnFFKeyDown)
        self.__txt_goto.Bind(wx.EVT_TEXT, self.__OnChangeGotoValue)
        self.__txt_goto.Bind(wx.EVT_CHAR, self.__OnGotoChar)
        self.__btn_goto.Bind(wx.EVT_BUTTON, self.__OnGoto)
        self.__spin_playback_speed.Bind(wx.EVT_SPINCTRL,
                                        self.__OnPlaySpinValChange)
        self.__spin_playback_speed.Bind(wx.EVT_CHAR, self.__OnPlaySpinValChar)
        self.__btn_playpause.Bind(wx.EVT_BUTTON, self.__OnPlayPause)

        # Layout

        curticks = wx.BoxSizer(wx.VERTICAL)
        curticks.Add(self.__static_curcycle, 1, wx.EXPAND)
        curticks.Add(self.__static_curtick, 1, wx.EXPAND)

        row1 = wx.BoxSizer(wx.HORIZONTAL)
        row1.Add(self.__hl_start, 0,
                 wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 2)
        slider_sizer = wx.BoxSizer(wx.HORIZONTAL)
        slider_sizer.Add(self.__time_slider, 1, wx.ALIGN_CENTER_VERTICAL)
        row1.Add(slider_sizer, 1, wx.EXPAND)
        row1.Add(self.__hl_end, 0,
                 wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 2)

        row2 = wx.FlexGridSizer(cols=6)
        for i in range(5):
            row2.AddGrowableCol(i)
        row2.AddGrowableRow(0)

        clock_sizer = wx.FlexGridSizer(2)
        clock_sizer.AddGrowableRow(0)
        clock_sizer.Add(self.__drop_clock, 0,
                        wx.ALIGN_CENTER_VERTICAL | wx.SHAPED)
        clock_sizer.Add(curticks, 0, wx.EXPAND | wx.LEFT, 3)
        row2.Add(clock_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)

        nav_sizer = wx.FlexGridSizer(10)
        nav_sizer.AddGrowableRow(0)
        nav_sizer.Add(self.__btn_rw_hold, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_back30, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_back10, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_back3, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_back1, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_forward1, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_forward3, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_forward10, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_forward30, 0, wx.ALIGN_CENTER_VERTICAL)
        nav_sizer.Add(self.__btn_ff_hold, 0, wx.ALIGN_CENTER_VERTICAL)
        row2.Add(nav_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)

        line_sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        line_sizer1.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.VERTICAL), 0,
                        wx.SHAPED | wx.ALIGN_CENTER_VERTICAL)
        row2.Add(line_sizer1, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)

        goto_sizer = wx.FlexGridSizer(2)
        goto_sizer.AddGrowableRow(0)
        goto_sizer.Add(self.__txt_goto, 0, wx.ALIGN_CENTER_VERTICAL)
        goto_sizer.Add(self.__btn_goto, 0, wx.ALIGN_CENTER_VERTICAL)
        row2.Add(goto_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)

        line_sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        line_sizer2.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.VERTICAL), 0,
                        wx.SHAPED | wx.ALIGN_CENTER_VERTICAL)
        row2.Add(line_sizer2, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)

        playback_sizer = wx.FlexGridSizer(2)
        playback_sizer.AddGrowableRow(0)
        playback_sizer.Add(self.__btn_playpause, 0, wx.ALIGN_CENTER_VERTICAL)
        spinner_sizer = wx.BoxSizer(wx.HORIZONTAL)
        spinner_sizer.Add(self.__static_playback_speed_units, 0,
                          wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 2)
        spinner_sizer.Add(self.__spin_playback_speed, 0,
                          wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 1)
        playback_sizer.Add(spinner_sizer, 0, wx.ALIGN_CENTER_VERTICAL)
        row2.Add(playback_sizer, 0, wx.ALIGN_RIGHT | wx.EXPAND)

        playback_controls = [
            c for c in self.GetChildren() if not isinstance(c, wx.StaticText)
            and row2.GetItem(c, recursive=True) is not None
        ]
        min_playback_height = max(c.GetBestSize().GetHeight()
                                  for c in playback_controls)
        for c in playback_controls:
            orig_width, _ = c.GetMinSize()
            c.SetMinSize((orig_width, min_playback_height))

        rows = wx.BoxSizer(wx.VERTICAL)
        rows.Add(row2, 0, wx.EXPAND | wx.TOP, 2)
        rows.Add(wx.StaticLine(self, wx.ID_ANY), 0,
                 wx.EXPAND | wx.TOP | wx.BOTTOM, 4)
        rows.Add(row1, 0, wx.EXPAND)

        self.SetSizer(rows)

        self.Fit()
        self.SetAutoLayout(1)

        # Initialization

        self.__OnChangeGotoValue()  # Validate initial GOTO value
        self.__GetSelectedClock()
        self.__Update()