def add_file_info_box(self, mainframe, name, labeldict, btncallback, fvar):
        """
        Create and add a infobox containing the info about a loaded
        savegame.
        """
        title = {'source':'Copy face from source file:',
                 'target':'To target file:'}
        frame = LabelFrame(mainframe, text=title[name])
        frame.pack(anchor=N, fill=X, expand=1, side=TOP, padx=0, pady=0)
        frame.columnconfigure(1, weight=1)

        btn = Button(frame, text='Browse', command=btncallback)
        btn.grid(column=0, row=0, padx=2, pady=2)

        field = Entry(frame, width=50, textvariable=fvar)
        field.grid(column=1, row=0, columnspan=2, padx=2, pady=2, sticky=W+E)

        l = ('name','gender','level','race','location','save number','playing time')
        for n, (i, j) in enumerate([(x.capitalize()+':', x) for x in l]):
            Label(frame, text=i, state=DISABLED).grid(column=0, row=n+1, padx=4,
                                                      pady=3, sticky=E)
            labeldict[j] = StringVar()
            Label(frame, textvariable=labeldict[j]).grid(column=1, row=n+1,
                                                     padx=4, pady=3, sticky=W)
        self.screenshot[name] = Label(frame)
        self.screenshot[name].grid(column=2, row=1, rowspan=len(l),
                                   padx=4, pady=4)
    def create_widgets(self):
        ''' Creates all widgets.
        '''
        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)

        validate_btn = Button(self,
                              text='Validate weight restrictions',
                              command=self.on_validate_weights)
        validate_btn.grid(row=0, column=0, padx=10, pady=5, sticky=N + W)

        panel = LabelFrame(self, text='Weight restrictions')
        panel.columnconfigure(0, weight=1)
        panel.rowconfigure(0, weight=1)
        panel.grid(row=1, column=0, padx=5, pady=5, sticky=E + W + S + N)

        weight_tab_main = VerticalScrolledFrame(panel)
        weight_tab_main.grid(row=0, column=0, sticky=E + W + S + N)
        weights_tab = weight_tab_main.interior

        self.abs_weights = TextForWeights(weights_tab, 'absolute',
                                          'Input1 <= 0.02',
                                          self.current_categories, self.params,
                                          'ABS_WEIGHT_RESTRICTIONS')
        self.abs_weights.grid(row=0, column=0, padx=10, pady=5, sticky=N + W)
        self.virtual_weights = TextForWeights(weights_tab, 'virtual',
                                              'Input1 >= 0.34',
                                              self.current_categories,
                                              self.params,
                                              'VIRTUAL_WEIGHT_RESTRICTIONS')
        self.virtual_weights.grid(row=1,
                                  column=0,
                                  padx=10,
                                  pady=5,
                                  sticky=N + W)
        self.price_ratio_weights = TextForWeights(weights_tab, 'price ratio',
                                                  'Input1/Input2 <= 5',
                                                  self.current_categories,
                                                  self.params,
                                                  'PRICE_RATIO_RESTRICTIONS',
                                                  True)
        self.price_ratio_weights.grid(row=2,
                                      column=0,
                                      padx=10,
                                      pady=5,
                                      sticky=N + W)
    def create_widgets(self):
        ''' Creates all widgets.
        '''
        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)

        validate_btn = Button(self, text='Validate weight restrictions',
                              command=self.on_validate_weights)
        validate_btn.grid(row=0, column=0, padx=10, pady=5, sticky=N+W)

        panel = LabelFrame(self, text='Weight restrictions')
        panel.columnconfigure(0, weight=1)
        panel.rowconfigure(0, weight=1)
        panel.grid(row=1, column=0,
                   padx=5, pady=5, sticky=E+W+S+N)

        weight_tab_main = VerticalScrolledFrame(panel)
        weight_tab_main.grid(row=0, column=0, sticky=E+W+S+N)
        weights_tab = weight_tab_main.interior

        self.abs_weights = TextForWeights(weights_tab, 'absolute',
                                          'Input1 <= 0.02',
                                          self.current_categories, self.params,
                                          'ABS_WEIGHT_RESTRICTIONS')
        self.abs_weights.grid(row=0, column=0, padx=10, pady=5, sticky=N+W)
        self.virtual_weights = TextForWeights(weights_tab, 'virtual',
                                              'Input1 >= 0.34',
                                              self.current_categories,
                                              self.params,
                                              'VIRTUAL_WEIGHT_RESTRICTIONS')
        self.virtual_weights.grid(row=1, column=0, padx=10, pady=5, sticky=N+W)
        self.price_ratio_weights = TextForWeights(weights_tab, 'price ratio',
                                                  'Input1/Input2 <= 5',
                                                  self.current_categories,
                                                  self.params,
                                                  'PRICE_RATIO_RESTRICTIONS',
                                                  True)
        self.price_ratio_weights.grid(row=2, column=0, padx=10, pady=5,
                                      sticky=N+W)
Exemple #4
0
    def add_file_info_box(self, mainframe, name, labeldict, btncallback, fvar):
        """
        Create and add a infobox containing the info about a loaded
        savegame.
        """
        title = {
            'source': 'Copy face from source file:',
            'target': 'To target file:'
        }
        frame = LabelFrame(mainframe, text=title[name])
        frame.pack(anchor=N, fill=X, expand=1, side=TOP, padx=0, pady=0)
        frame.columnconfigure(1, weight=1)

        btn = Button(frame, text='Browse', command=btncallback)
        btn.grid(column=0, row=0, padx=2, pady=2)

        field = Entry(frame, width=50, textvariable=fvar)
        field.grid(column=1, row=0, columnspan=2, padx=2, pady=2, sticky=W + E)

        l = ('name', 'gender', 'level', 'race', 'location', 'save number',
             'playing time')
        for n, (i, j) in enumerate([(x.capitalize() + ':', x) for x in l]):
            Label(frame, text=i, state=DISABLED).grid(column=0,
                                                      row=n + 1,
                                                      padx=4,
                                                      pady=3,
                                                      sticky=E)
            labeldict[j] = StringVar()
            Label(frame, textvariable=labeldict[j]).grid(column=1,
                                                         row=n + 1,
                                                         padx=4,
                                                         pady=3,
                                                         sticky=W)
        self.screenshot[name] = Label(frame)
        self.screenshot[name].grid(column=2,
                                   row=1,
                                   rowspan=len(l),
                                   padx=4,
                                   pady=4)
Exemple #5
0
def make_widgets(frame: ttk.LabelFrame, pane: SubPane) -> SelectorWin:
    """Generate the UI components, and return the base window."""

    def for_channel(channel: MusicChannel) -> List[SelItem]:
        """Get the items needed for a specific channel."""
        music_list = []
        for music in Music.all():
            if music.provides_channel(channel):
                selitem = SEL_ITEMS[music.id].copy()
                selitem.snd_sample = music.get_sample(channel)
                music_list.append(selitem)
        return music_list

    # This gets overwritten when making windows.
    last_selected = {
        channel: GEN_OPTS.get_val(
            'Last_Selected',
            'music_' + channel.name.casefold(),
            '<NONE>',
        ) for channel in MusicChannel
    }

    base_win = WINDOWS[MusicChannel.BASE] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.BASE),
        title=_('Select Background Music - Base'),
        desc=_('This controls the background music used for a map. Expand '
               'the dropdown to set tracks for specific test elements.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Add no music to the map at all. Testing Element-specific '
                    'music may still be added.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.BASE],
        attributes=[
            SelAttr.bool('SPEED', _('Propulsion Gel SFX')),
            SelAttr.bool('BOUNCE', _('Repulsion Gel SFX')),
            SelAttr.bool('TBEAM', _('Excursion Funnel Music')),
            SelAttr.bool('TBEAM_SYNC', _('Synced Funnel Music')),
        ],
    )

    WINDOWS[MusicChannel.TBEAM] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.TBEAM),
        title=_('Select Excursion Funnel Music'),
        desc=_('Set the music used while inside Excursion Funnels.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Have no music playing when inside funnels.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.TBEAM],
        attributes=[
            SelAttr.bool('TBEAM_SYNC', _('Synced Funnel Music')),
        ],
    )

    WINDOWS[MusicChannel.BOUNCE] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.BOUNCE),
        title=_('Select Repulsion Gel Music'),
        desc=_('Select the music played when players jump on Repulsion Gel.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Add no music when jumping on Repulsion Gel.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.BOUNCE],
    )

    WINDOWS[MusicChannel.SPEED] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.SPEED),
        title=_('Select Propulsion Gel Music'),
        desc=_('Select music played when players have large amounts of horizontal velocity.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Add no music while running fast.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.SPEED],
    )

    assert set(WINDOWS.keys()) == set(MusicChannel), "Extra channels?"

    # Widgets we want to remove when collapsing.
    exp_widgets = []  # type: List[tkinter.Widget]

    def toggle_btn_enter(event=None):
        toggle_btn['text'] = BTN_EXPAND_HOVER if is_collapsed else BTN_CONTRACT_HOVER

    def toggle_btn_exit(event=None):
        toggle_btn['text'] = BTN_EXPAND if is_collapsed else BTN_CONTRACT

    def set_collapsed():
        """Configure for the collapsed state."""
        global is_collapsed
        is_collapsed = True
        GEN_OPTS['Last_Selected']['music_collapsed'] = '1'
        base_lbl['text'] = _('Music: ')
        toggle_btn_exit()

        # Set all music to the children - so those are used.
        set_suggested(WINDOWS[MusicChannel.BASE].chosen_id, sel_item=True)

        for wid in exp_widgets:
            wid.grid_remove()

    def set_expanded():
        """Configure for the expanded state."""
        global is_collapsed
        is_collapsed = False
        GEN_OPTS['Last_Selected']['music_collapsed'] = '0'
        base_lbl['text'] = _('Base: ')
        toggle_btn_exit()
        for wid in exp_widgets:
            wid.grid()
        pane.update_idletasks()
        pane.move()

    def toggle(event=None):
        if is_collapsed:
            set_expanded()
        else:
            set_collapsed()
        pane.update_idletasks()
        pane.move()

    frame.columnconfigure(2, weight=1)

    base_lbl = ttk.Label(frame)
    base_lbl.grid(row=0, column=1)

    toggle_btn = ttk.Label(frame, text=' ')
    toggle_btn.bind('<Enter>', toggle_btn_enter)
    toggle_btn.bind('<Leave>', toggle_btn_exit)
    toggle_btn.bind('<ButtonPress-1>', toggle)
    toggle_btn.grid(row=0, column=0)

    for row, channel in enumerate(MusicChannel):
        btn = WINDOWS[channel].widget(frame)
        if row:
            exp_widgets.append(btn)
        btn.grid(row=row, column=2, sticky='EW')

    for row, text in enumerate([
        _('Funnel:'),
        _('Bounce:'),
        _('Speed:'),
    ], start=1):
        label = ttk.Label(frame, text=text)
        exp_widgets.append(label)
        label.grid(row=row, column=1, sticky='EW')

    if GEN_OPTS.get_bool('Last_Selected', 'music_collapsed', True):
        set_collapsed()
    else:
        set_expanded()

    for channel, win in WINDOWS.items():
        win.sel_item_id(last_selected[channel])

    return base_win
Exemple #6
0
    def build(self):
        """widget construction"""

        lf1 = LabelFrame(self.fr0, text='rgb')
        lf1.grid(column=0, row=0, sticky='new')
        lf1.columnconfigure(1, weight=1)
        vcmdsb = root.register(sb_okay)

        self.rgbcans = []
        rgbsboxes = []
        rgbcomps = ['r', 'g', 'b', 'a']
        rgbnames = ['red  ', 'green', 'blue ', 'alpha']
        rgbvars = [self.rvar, self.gvar, self.bvar, self.avar]
        rgbhandles = [
            self.rgbhandle, self.rgbhandle, self.rgbhandle, self.ahandle
        ]
        binds = [self.checksb, self.checksb, self.checksb, self.checksba]

        for ix, comp in enumerate(rgbcomps):
            Label(lf1, text=rgbnames[ix]).grid(row=3 * ix, column=0)
            Label(lf1).grid(row=2 + 3 * ix, column=2)
            self.rgbcans.append(
                Canvas(lf1,
                       height=self.canvas_h,
                       width=self.canvas_w,
                       bd=0,
                       highlightthickness=0))
            self.rgbcans[ix].grid(row=3 * ix,
                                  column=1,
                                  sticky='ew',
                                  padx=self.sliderlength // 2)
            self.rgbcans[ix].bind("<Configure>",
                                  partial(self.resize, can=self.rgbcans[ix]))

            TtkScale(lf1,
                     self.scale_l,
                     from_=0,
                     to=255,
                     variable=rgbvars[ix],
                     orient='horizontal',
                     command=rgbhandles[ix],
                     tickinterval=20).grid(row=1 + 3 * ix,
                                           column=1,
                                           sticky='new')
            rgbsboxes.append(
                Spinbox(lf1,
                        from_=0,
                        to=255,
                        textvariable=rgbvars[ix],
                        validate='key',
                        validatecommand=(vcmdsb, '%d', '%P', '%S', 0, 255),
                        command=rgbhandles[ix],
                        width=5))
            rgbsboxes[ix].grid(row=1 + 3 * ix, column=2, sticky='nw')
            rgbsboxes[ix].bind('<KeyRelease>', binds[ix])

        lf3 = LabelFrame(self.fr0, text='colour mix')
        lf3.grid(column=1, row=0, sticky='nw')

        self.cmcan = cmcan = Canvas(lf3,
                                    width=30,
                                    height=30,
                                    bd=0,
                                    highlightthickness=0)
        cmcan.grid(column=0, row=0, sticky='n', columnspan=2)
        cmcan.grid_propagate(0)
        vdraw_gradient(self.cmcan, (255, 0, 0), alpha=255)

        cml = Label(lf3, text='hash\nvalue')
        cml.grid(column=0, row=1)

        vcmd = root.register(is_okay)
        self.en = en = Entry(lf3,
                             width=8,
                             validate='key',
                             validatecommand=(vcmd, '%i', '%P', '%S'),
                             textvariable=self.evar)
        en.grid(column=1, row=1)
        en.bind('<KeyRelease>', self.checkhash)

        lf5 = LabelFrame(lf3,
                         text='related colours')  # style='Width.Tlabelframe'
        lf5.grid(column=0, row=2, sticky='nw', columnspan=2)

        self.rcls = []
        self.rccans = []
        relateds = [0, 20, 40, 60, 80, 100]
        rtexts = ['0% Y', '20% Y', '40% Y', '60% Y', '80% Y', '100% Y']

        for ix, rel in enumerate(relateds):
            Label(lf5, text=rtexts[ix]).grid(row=1 + 2 * ix,
                                             column=0,
                                             sticky='n')
            self.rcls.append(Label(lf5))
            self.rcls[ix].grid(row=1 + 2 * ix, column=1, sticky='n')
            self.rccans.append(
                Canvas(lf5, width=30, height=30, bd=0, highlightthickness=0))
            self.rccans[ix].grid(row=2 * ix,
                                 column=0,
                                 sticky='n',
                                 columnspan=2)

        self.cccan = Canvas(lf5,
                            width=30,
                            height=30,
                            bd=0,
                            highlightthickness=0)
        self.cccan.grid(column=0, row=12, sticky='n', columnspan=2)

        self.ccla = Label(lf5, text="comp'y")
        self.ccla.grid(column=0, row=13, sticky='n')

        self.ccl = Label(lf5, text="")
        self.ccl.grid(column=1, row=13, sticky='n')

        draw_gradient(self.rgbcans[0], (0, 0, 0), (255, 0, 0),
                      width=self.canvas_w)
        draw_gradient(self.rgbcans[1], (255, 0, 0), (255, 255, 0),
                      width=self.canvas_w)
        draw_gradient(self.rgbcans[2], (255, 0, 0), (255, 0, 255),
                      width=self.canvas_w)
        draw_agradient(self.rgbcans[3], (127, 127, 127), (255, 0, 0),
                       width=self.canvas_w)

        lf4 = LabelFrame(self.fr0, text='yiq')
        lf4.grid(column=2, row=0, sticky='ew')
        lf4.columnconfigure(1, weight=1)
        vcmdyiq = root.register(yiq_okay)

        self.cans = []
        sboxes = []
        comps = ['y', 'i', 'q']
        names = ['luma', 'i hue', 'q hue']
        tkvars = [self.yvar, self.ivar, self.qvar]
        handles = [self.yhandle, self.iqhandle, self.iqhandle]
        froms = [0, -100, -100]
        ticks = [10, 20, 20]

        for ix, comp in enumerate(comps):
            Label(lf4, text=names[ix]).grid(row=3 * ix, column=0)
            Label(lf4).grid(row=2 + 3 * ix, column=2)
            self.cans.append(
                Canvas(lf4,
                       width=self.canvas_w,
                       height=self.canvas_h,
                       bd=0,
                       highlightthickness=0))
            self.cans[ix].grid(row=3 * ix,
                               column=1,
                               sticky='ew',
                               padx=self.sliderlength // 2)
            self.cans[ix].bind("<Configure>",
                               partial(self.resize_yiq, can=self.cans[ix]))
            TtkScale(lf4,
                     from_=froms[ix],
                     to=100,
                     variable=tkvars[ix],
                     orient='horizontal',
                     length=self.scale_l,
                     command=handles[ix],
                     tickinterval=ticks[ix]).grid(row=1 + 3 * ix,
                                                  column=1,
                                                  sticky='new')
            sboxes.append(
                Spinbox(lf4,
                        from_=froms[ix],
                        to=100,
                        textvariable=tkvars[ix],
                        validatecommand=(vcmdyiq, '%d', '%P', '%S', froms[ix],
                                         100),
                        validate='key',
                        command=handles[ix],
                        width=5,
                        increment=1))
            sboxes[ix].grid(row=1 + 3 * ix, column=2, sticky='nw')
            sboxes[ix].bind('<KeyRelease>', self.checksyiq)

        # assume initial setting 30,100.0,40.56 yiq
        to_colour = yiq_to_rgb(*(30, 100.0, 40.56))
        # print(self.canvas_w)
        draw_gradient(self.cans[0],
                      yiq_to_rgb(0.0, 100.0, 40.56),
                      yiq_to_rgb(100, 100, 40.56),
                      width=self.canvas_w)
        draw_gradient(self.cans[1],
                      yiq_to_rgb(30, -100.0, 40.56),
                      to_colour,
                      width=self.canvas_w)
        draw_gradient(self.cans[2],
                      yiq_to_rgb(30, 100, -100),
                      yiq_to_rgb(30, 100, 100),
                      width=self.canvas_w)

        self.related(30, 100.0, 40.56)

        self.canYiq = canYiq = Canvas(lf4, width=self.space, height=self.space)
        canYiq.grid(column=0, row=9, columnspan=3, pady=25, sticky='n')
        self.yiqGamut = PhotoImage(file='../../figures/colour_space.png')
        canYiq.create_image(0, 0, anchor='nw', image=self.yiqGamut)
        self.ring = circle(canYiq,
                           300.0,
                           210.84,
                           self.ring_radius,
                           width=self.ring_width,
                           activeoutline='#555555',
                           tags='ring')

        canYiq.bind('<Button-1>', self.move_ring)
        canYiq.tag_bind('ring', '<B1-Motion>', self.move_ring)
Exemple #7
0
    def build(self):
        """Widget construction."""

        lf1 = LabelFrame(self.fr0, text='rgb')
        lf1.grid(column=0, row=0, sticky='new')
        lf1.columnconfigure(1, weight=1)

        rl0 = Label(lf1, text='red  ')
        rl0.grid(column=0, row=0, sticky='s')

        self.rcan = Canvas(lf1,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.rcan.grid(column=1,
                       row=0,
                       sticky='sew',
                       padx=self.sliderlength // 2)
        self.rcan.bind("<Configure>", self.resize_rcan)

        rsc = TtkScale(lf1,
                       self.scale_l,
                       from_=0,
                       to=255,
                       variable=self.rvar,
                       orient='horizontal',
                       command=self.rhandle,
                       tickinterval=20)
        rsc.grid(column=1, row=1, sticky='new')

        vcmdsb = root.register(sb_okay)

        rsb = Spinbox(lf1,
                      from_=0,
                      to=255,
                      textvariable=self.rvar,
                      validate='key',
                      validatecommand=(vcmdsb, '%d', '%P', '%S', 255),
                      command=self.rhandle,
                      width=5)
        rsb.grid(column=2, row=1, sticky='nw')
        rsb.bind('<KeyRelease>', self.checksb)

        rel = Label(lf1)
        rel.grid(column=2, row=2)

        gl0 = Label(lf1, text='green')
        gl0.grid(column=0, row=3)

        self.gcan = Canvas(lf1,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.gcan.grid(column=1,
                       row=3,
                       sticky='sew',
                       padx=self.sliderlength // 2)
        self.gcan.bind("<Configure>", self.resize_gcan)

        gsc = TtkScale(lf1,
                       self.scale_l,
                       from_=0,
                       to=255,
                       variable=self.gvar,
                       orient='horizontal',
                       command=self.ghandle,
                       tickinterval=20)
        gsc.grid(column=1, row=4, sticky='new')

        gsb = Spinbox(lf1,
                      from_=0,
                      to=255,
                      textvariable=self.gvar,
                      validate='key',
                      validatecommand=(vcmdsb, '%d', '%P', '%S', 255),
                      command=self.ghandle,
                      width=5)
        gsb.grid(column=2, row=4, sticky='nw')
        gsb.bind('<KeyRelease>', self.checksb)

        gel = Label(lf1)
        gel.grid(column=2, row=5)

        bl0 = Label(lf1, text='blue ')
        bl0.grid(column=0, row=6, sticky='s')

        self.bcan = Canvas(lf1,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.bcan.grid(column=1,
                       row=6,
                       sticky='new',
                       padx=self.sliderlength // 2)
        self.bcan.bind("<Configure>", self.resize_bcan)

        bsc = TtkScale(lf1,
                       self.scale_l,
                       from_=0,
                       to=255,
                       variable=self.bvar,
                       orient='horizontal',
                       command=self.bhandle,
                       tickinterval=20)
        bsc.grid(column=1, row=7, sticky='new')

        bsb = Spinbox(lf1,
                      from_=0,
                      to=255,
                      textvariable=self.bvar,
                      validate='key',
                      validatecommand=(vcmdsb, '%d', '%P', '%S', 255),
                      command=self.bhandle,
                      width=5)
        bsb.grid(column=2, row=7, sticky='nw')
        bsb.bind('<KeyRelease>', self.checksb)

        bel = Label(lf1)
        bel.grid(column=2, row=8)

        lf3 = LabelFrame(self.fr0, text='colour mix')
        lf3.grid(column=1, row=0, sticky='nw')

        self.cmcan = cmcan = Canvas(lf3,
                                    width=30,
                                    height=30,
                                    bd=0,
                                    highlightthickness=0)
        cmcan.grid(column=0, row=0, sticky='n', columnspan=2)
        cmcan.grid_propagate(0)
        vdraw_gradient(self.cmcan, (255, 0, 0), alpha=255)

        cml = Label(lf3, text='hash\nvalue')
        cml.grid(column=0, row=1)

        vcmd = root.register(is_okay)
        self.ent0 = ent0 = Entry(lf3,
                                 width=8,
                                 validate='key',
                                 validatecommand=(vcmd, '%i', '%P', '%S'),
                                 textvariable=self.evar)
        ent0.grid(column=1, row=1)
        ent0.bind('<KeyRelease>', self.checkhash)

        lf5 = LabelFrame(lf3, text='related colours', style='Wide.TLabelframe')
        lf5.grid(column=0, row=2, sticky='nw', columnspan=2)

        self.srcls = []
        self.vrcls = []
        self.srccans = []
        self.vrccans = []
        relateds = [25, 50, 75, 100]
        stexts = ['25% sat', '50% sat', '75% sat', '100% sat']
        vtexts = ['25% val', '50% val', '75% val', '100% val']

        for ix, rel in enumerate(relateds):
            Label(lf5, text=stexts[ix]).grid(row=1 + 2 * ix,
                                             column=0,
                                             sticky='n')
            self.srcls.append(Label(lf5))
            self.srcls[ix].grid(row=1 + 2 * ix, column=1, sticky='n')
            self.srccans.append(
                Canvas(lf5, width=30, height=30, bd=0, highlightthickness=0))
            self.srccans[ix].grid(row=2 * ix,
                                  column=0,
                                  sticky='n',
                                  columnspan=2)
            Label(lf5, text=vtexts[ix]).grid(row=9 + 2 * ix,
                                             column=0,
                                             sticky='n')
            self.vrcls.append(Label(lf5))
            self.vrcls[ix].grid(row=9 + 2 * ix, column=1, sticky='n')
            self.vrccans.append(
                Canvas(lf5, width=30, height=30, bd=0, highlightthickness=0))
            self.vrccans[ix].grid(row=8 + 2 * ix,
                                  column=0,
                                  sticky='n',
                                  columnspan=2)

        self.cccan = Canvas(lf5,
                            width=30,
                            height=30,
                            bd=0,
                            highlightthickness=0)
        self.cccan.grid(column=0, row=17, sticky='n', columnspan=2)

        self.ccla = Label(lf5, text="comp'y")
        self.ccla.grid(column=0, row=18, sticky='n')

        self.ccl = Label(lf5, text="")
        self.ccl.grid(column=1, row=18, sticky='n')

        al0 = Label(lf1, text='alpha')
        al0.grid(column=0, row=10, sticky='s')

        self.acan = Canvas(lf1,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.acan.grid(column=1,
                       row=10,
                       sticky='new',
                       padx=self.sliderlength // 2)
        self.acan.bind("<Configure>", self.resize_acan)

        asc = TtkScale(lf1,
                       self.scale_l,
                       from_=0,
                       to=255,
                       variable=self.avar,
                       orient='horizontal',
                       command=self.ahandle,
                       tickinterval=20)
        asc.grid(column=1, row=11, sticky='new')

        asb = Spinbox(lf1,
                      from_=0,
                      to=255,
                      textvariable=self.avar,
                      validate='key',
                      validatecommand=(vcmdsb, '%d', '%P', '%S', 255),
                      command=self.ahandle,
                      width=5)
        asb.grid(column=2, row=11, sticky='nw')
        asb.bind('<KeyRelease>', self.checksba)

        ael = Label(lf1, text=' ')
        ael.grid(column=2, row=12, sticky='s')

        draw_gradient(self.rcan, (0, 0, 0), (255, 0, 0), width=self.canvas_w)
        draw_gradient(self.gcan, (255, 0, 0), (255, 0, 255),
                      width=self.canvas_w)
        draw_gradient(self.bcan, (255, 0, 0), (255, 255, 0),
                      width=self.canvas_w)
        draw_agradient(self.acan, (127, 127, 127), (255, 0, 0),
                       width=self.canvas_w)

        lf4 = LabelFrame(self.fr0, text='hsv')
        lf4.grid(column=2, row=0, sticky='news')
        lf4.columnconfigure(1, weight=1)

        hl0 = Label(lf4, text='hue  ')
        hl0.grid(column=0, row=0, sticky='s')

        self.hcan = Canvas(lf4,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.hcan.grid(column=1,
                       row=0,
                       sticky='sew',
                       padx=self.sliderlength // 2)
        self.hcan.bind("<Configure>", self.resize_hcan)

        hsc = TtkScale(lf4,
                       self.scale_l,
                       from_=0,
                       to=360,
                       variable=self.hvar,
                       orient='horizontal',
                       command=self.hhandle,
                       tickinterval=30)
        hsc.grid(column=1, row=1, sticky='new')

        vcmdsb = root.register(sb_okay)

        hsb = Spinbox(lf4,
                      from_=0,
                      to=360,
                      textvariable=self.hvar,
                      validate='key',
                      validatecommand=(vcmdsb, '%P', '%S', 360),
                      command=self.hhandle,
                      width=5)
        hsb.grid(column=2, row=1, sticky='nw')
        hsb.bind('<KeyRelease>', self.checksbh)

        hel = Label(lf4)
        hel.grid(column=2, row=2)

        sl0 = Label(lf4, text='sat  ')
        sl0.grid(column=0, row=3)

        self.scan = Canvas(lf4,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.scan.grid(column=1,
                       row=3,
                       sticky='sew',
                       padx=self.sliderlength // 2)
        self.scan.bind("<Configure>", self.resize_scan)

        ssc = TtkScale(lf4,
                       self.scale_l,
                       from_=0,
                       to=100,
                       variable=self.svar,
                       orient='horizontal',
                       command=self.shandle,
                       tickinterval=10)
        ssc.grid(column=1, row=4, sticky='new')

        ssb = Spinbox(lf4,
                      from_=0,
                      to=100,
                      textvariable=self.svar,
                      validate='key',
                      validatecommand=(vcmdsb, '%P', '%S', 100),
                      command=self.shandle,
                      width=5)
        ssb.grid(column=2, row=4, sticky='nw')
        ssb.bind('<KeyRelease>', self.checksb100)

        sel = Label(lf4)
        sel.grid(column=2, row=5)

        vl0 = Label(lf4, text='value')
        vl0.grid(column=0, row=6, sticky='s')

        self.vcan = Canvas(lf4,
                           width=self.canvas_w,
                           height=self.canvas_h,
                           bd=0,
                           highlightthickness=0)
        self.vcan.grid(column=1,
                       row=6,
                       sticky='new',
                       padx=self.sliderlength // 2)
        self.vcan.bind("<Configure>", self.resize_vcan)

        vsc = TtkScale(lf4,
                       self.scale_l,
                       from_=0,
                       to=100,
                       variable=self.vvar,
                       orient='horizontal',
                       command=self.vhandle,
                       tickinterval=10)
        vsc.grid(column=1, row=7, sticky='new')

        vsb = Spinbox(lf4,
                      from_=0,
                      to=100,
                      textvariable=self.vvar,
                      validate='key',
                      validatecommand=(vcmdsb, '%P', '%S', 100),
                      command=self.vhandle,
                      width=5)
        vsb.grid(column=2, row=7, sticky='nw')
        vsb.bind('<KeyRelease>', self.checksb100)

        vel = Label(lf4)
        vel.grid(column=2, row=8)

        # assume initial setting 0,100,100 hsv
        to_colour = hsv_to_rgb(*(0, 100, 100))

        hue_gradient(self.hcan, width=self.canvas_w)
        draw_gradient(self.scan, (255, 255, 255),
                      to_colour,
                      width=self.canvas_w)
        draw_gradient(self.vcan, (0, 0, 0), to_colour, width=self.canvas_w)

        self.can_hsv = can_hsv = Canvas(lf4,
                                        width=self.wheel_w,
                                        height=self.wheel_w,
                                        bg='#d9d9d9')
        can_hsv.grid(column=0, row=9, columnspan=3, pady=25, sticky='n')
        self.hsv_gamut = PhotoImage(file='../figures/colour_wheel.png')
        can_hsv.create_image(0, 0, anchor='nw', image=self.hsv_gamut)
        self.ring = circle(can_hsv,
                           307,
                           158,
                           self.ring_radius,
                           width=self.ring_width,
                           outline='#555555',
                           activeoutline='black',
                           tags='ring')

        can_hsv.bind('<Button-1>', self.click_ring)
        can_hsv.tag_bind('ring', '<B1-Motion>', self.drag_ring)

        self.related(0, 100, 100, 255, 0, 0)
Exemple #8
0
class Histogram(Frame):
    def __init__(self, parent, master):
        super(Histogram, self).__init__(parent)
        self.window = master  #type:VisAnaWindow
        self.parent = parent

        self.param_y = None
        self.param_x = None

        self.ds = self.window.ds  #type:DataSource

        self.columnconfigure(1, weight=1)
        self.rowconfigure(1, weight=1)

        self.tframe = LabelFrame(self, text="Tooltip")
        #self.tframe.grid(column=0, row=1, sticky=(S,W,N,E))
        self.tframe.rowconfigure(0, weight=1)
        self.tframe.columnconfigure(0, weight=1)
        self.tooltip = StringVar(self.tframe)
        self.tlabel = Label(self.tframe,
                            textvariable=self.tooltip,
                            justify="left",
                            anchor="nw",
                            wraplength=200)
        self.tlabel.grid(column=0, row=0, sticky=(W, N))

        self.select_rect = None

        if self.ds is None:
            self.settings = Label(
                self, text="No data, please open a file via File -> Open")
            self.settings.grid(column=1, row=1, sticky=(S, W, N, E))
            return

        self.settings = HControls(self, self.ds.base().get_attr_names())
        self.settings.grid(column=0, row=0, sticky=(S, W, N, E))
        self.apply_settings()

    ###################
    # PLOT-EVENT HANDLER

    ## is called by the plot to confirm if the mouseevent was inside/on a plotted line or a marker
    def handle_pick(self, line, mouseevent):
        if mouseevent.button == 1:
            return self.handle_mouse_event(mouseevent)
        else:
            return False, dict()

    ## is called to to do something when the mouse hovers over the plot and has changed its position.
    ## if no mousebutton is pressed and no points were selected, a hover-tooltip is shown.
    ## if the left button is pressed, (re-)draw the selection indicator
    def handle_hover(self, mouseevent):
        if not mouseevent.button in [1, 3] and self.select_rect is None:
            isover, props = self.handle_mouse_event(mouseevent)

            if isover:
                self.draw_tooltip(mouseevent, props["ind"])

        elif mouseevent.button in [1, 3]:
            ## handle case if mouse is outside the canvas
            if mouseevent.xdata == None:
                xmin = self.mouse_pressed[0]
                xmax = self.mouse_pressed[0]
                ymin = self.mouse_pressed[1]
                ymax = self.mouse_pressed[1]
            else:
                xmin = min(mouseevent.xdata, self.mouse_pressed[0])
                xmax = max(mouseevent.xdata, self.mouse_pressed[0])
                ymin = min(mouseevent.ydata, self.mouse_pressed[1])
                ymax = max(mouseevent.ydata, self.mouse_pressed[1])
            bbox = (xmin, ymin, xmax, ymax)
            self.clean_tooltip(True, emit=False)
            bbox2 = self.ax.transData.transform(bbox)
            c_height = self.canvas.figure.bbox.height
            bbox3 = (bbox2[0], c_height - bbox2[1], bbox2[2],
                     c_height - bbox2[3])
            self.select_rect = self.canvas.get_tk_widget().create_rectangle(
                bbox3, dash=".", outline=self.fgcol)

    ## is called whenever a mousebutton is clicked while the mouse is over the plot.
    ##  if the left button is pushed, we begin to draw a selection area
    def handle_mouse_down(self, mouseevent):
        if mouseevent.button in [1, 3]:
            self.clean_tooltip(True)
            self.mouse_pressed = (mouseevent.xdata, mouseevent.ydata)

    ## is called whenever a mouse button is released while hovering over the plot
    ## if the left button was pressed and there are points within the selection area, select those points and show a
    ##  tooltip containing information about those selected points. If not, clean up.
    def handle_mouse_up(self, mouseevent):
        if mouseevent.button in [1, 3]:
            ## handle case if mouse is outside the canvas
            if mouseevent.xdata == None:
                xmin = self.mouse_pressed[0]
                xmax = self.mouse_pressed[0]
                ymin = self.mouse_pressed[1]
                ymax = self.mouse_pressed[1]
            else:
                xmin = min(mouseevent.xdata, self.mouse_pressed[0])
                xmax = max(mouseevent.xdata, self.mouse_pressed[0])
                ymin = min(mouseevent.ydata, self.mouse_pressed[1])
                ymax = max(mouseevent.ydata, self.mouse_pressed[1])
            if xmin == xmax and ymin == ymax:
                self.clean_tooltip(True)
            else:
                if mouseevent.button == 1:
                    if self.param_x == self.ds.get_time_colname():
                        xmin = mdates.num2date(xmin)
                        xmax = mdates.num2date(xmax)
                    if self.param_y == self.ds.get_time_colname():
                        ymin = mdates.num2date(ymin)
                        ymax = mdates.num2date(ymax)
                    self.ds.select("ss_selected", self.param_x, xmin, xmax,
                                   "ss_show")
                    self.ds.select("ss_selected", self.param_y, ymin, ymax,
                                   "ss_selected")
                    ind = self.ds.df("ss_selected").index.values
                    if len(ind) > 0:
                        text = "Selected area from ({:.1f}; {:.1f})\n\t to ({:.1f}; {:.1f})"\
                                                        .format(xmin,ymin,xmax,ymax)
                        self.draw_tooltip(mouseevent, ind, True)
                        self.window.history.add(text)
                    else:
                        self.clean_tooltip(True)
                else:
                    self.clean_tooltip(True, emit=False)
                    self.ax.set_xlim((xmin, xmax), emit=False)
                    self.ax.set_ylim((ymin, ymax))
                    self.canvas.draw()

    ## handle any mouse event where it has to be clarified whether there's a marker under the mouse or not. If so,
    ##  return all index values of the concerning markers.
    def handle_mouse_event(self, mouseevent, radius=5):
        """
        find the points within a certain radius from the mouse in
        data coords and attach the index numbers of the found elements
        which are the data points that were picked
        """
        self.clean_tooltip()

        # print("PICKER")
        # print(mouseevent, vars(mouseevent))
        if self.param_x == self.ds.get_time_colname(
        ) or self.param_y == self.ds.get_time_colname():
            return False, dict()
        xydata = self.ax.transData.transform(
            self.ds.df("ss_show")[[self.param_x, self.param_y]]).transpose()
        try:
            mxy = self.ax.transData.transform(
                [mouseevent.xdata, mouseevent.ydata])
        except ValueError:
            return False, dict()
        xdata = xydata[0]
        ydata = xydata[1]
        mousex = mxy[0]
        mousey = mxy[1]
        if mouseevent.xdata is None:
            return False, dict()

        d = pd.np.sqrt((xdata - mousex)**2. + (ydata - mousey)**2.)
        ind = self.ds.df("ss_show").index.values[pd.np.nonzero(
            pd.np.less_equal(d, radius))[0]]

        if len(ind) > 0:
            props = dict(ind=ind)
            return True, props
        else:
            return False, dict()

    ## draws a tooltip and generates the information it contains from an event with/and a list of index values
    def draw_tooltip(self, event, ind=None, selected=False):
        if ind is None:
            ind = event.ind
            #event = event.mouseevent

        # Generate the Tooltip-String
        selstr = ""
        if selected:
            selstr = "selected "

        if len(ind) is 1:
            text = selstr + "value:"
            self.ds.select_ids("ss_selected", ind, "ss_show")
            for col, cdata in self.ds.df("ss_selected").iteritems():
                text += '\n{}: {}'.format(col, cdata[ind[0]])
        else:
            text = selstr + "%s values:" % len(ind)
            if not selected:
                self.ds.select_ids("ss_selected", ind, "ss_show")
            self.ds.aggregate("ss_sel_aggremin", "MIN", in_table="ss_selected")
            self.ds.aggregate("ss_sel_aggremax", "MAX", in_table="ss_selected")

            for col, cdata in self.ds.df("ss_sel_aggremin").iteritems():
                #text += '\n{}:\n  min:\t{}\n  max:\t{}'.format(col, cdata[0], self.ds.df("ss_sel_aggremax")[col][0])
                text += '\n{}: {} to {}'.format(
                    col, cdata[0],
                    self.ds.df("ss_sel_aggremax")[col][0])

        # write the tooltip
        self.tooltip.set(text)

        # # Draw the box and write the string on it
        #
        # c_height = self.canvas.figure.bbox.height
        # c_width = self.canvas.figure.bbox.width
        # y = c_height - event.y
        # x = event.x
        #
        # # get bounding box of a possible tooltip
        # self.plot_tooltip = self.canvas.get_tk_widget().create_text(x + 2, y, anchor=tk.NW, text=text)
        # bbox = self.canvas.get_tk_widget().bbox(self.plot_tooltip)
        # self.canvas.get_tk_widget().delete(self.plot_tooltip)
        #
        # # print("bbox:", bbox)
        #
        # # make sure the tooltip is within bounds
        # if bbox[2] > c_width:
        #     adj = -2
        #     if bbox[3] > c_height:
        #         anchor = tk.SE
        #     else:
        #         anchor = tk.NE
        # else:
        #     adj = 2
        #     if bbox[3] > c_height:
        #         anchor = tk.SW
        #     else:
        #         anchor = tk.NW
        # # get the new bounding box
        # if anchor is not tk.NW:  # =^= the anchor had to be modified
        #     self.plot_tooltip = self.canvas.get_tk_widget().create_text(x + adj, y, anchor=anchor, text=text)
        #     bbox = self.canvas.get_tk_widget().bbox(self.plot_tooltip)
        #     self.canvas.get_tk_widget().delete(self.plot_tooltip)
        #
        # self.plot_tooltip_rect = self.canvas.get_tk_widget().create_rectangle(bbox, fill="yellow")
        # self.plot_tooltip = self.canvas.get_tk_widget().create_text(x + adj, y, anchor=anchor, text=text)

    ## remove the tooltip if shown
    def clean_tooltip(self, with_select_rect=False, emit=True):
        self.tooltip.set("")
        if with_select_rect and self.select_rect is not None:
            self.canvas.get_tk_widget().delete(self.select_rect)
            self.select_rect = None
            #if emit:
            #self.action_str = None
            #TO-DO
            #self.trigger_update(self.TIMELINE_SELECTION)

    #### Handle Signals from Outside

    def apply_settings(self, ev=None):
        self.lgvar = self.settings.doLog()

        self.redraw_plot()

    def redraw_plot(self):
        self.window.status.set("Redraw Histogram...")
        self.draw_plot()
        self.window.status.set("")

    # the underlying data changed, called by VisAnaWindow.openFile
    def ds_changed(self):
        olds = self.ds
        self.ds = self.window.ds
        if olds is None:
            self.settings.destroy()
            newcols = self.window.calc.get_all_columns(with_time=True,
                                                       with_custom=False)
            self.settings = HControls(self, newcols)
            self.settings.grid(column=0, row=0, sticky=(S, W, N, E))

    # a cluster recalculation was processed, called by VisAnaWindow.redo_plots
    def cluster_changed(self, in_table):
        self.ds.link("h_show", in_table)

        self.apply_settings()

    def draw_plot(self):
        self.clean_tooltip(True)

        self.fig = Figure(figsize=(5, 5), dpi=100)  #type:Figure
        self.ax = self.fig.add_subplot(111)  #type:Axes

        self.ax.clear()
        self.ax.grid(True)
        tabl = self.ds.get_data("h_show")
        d = tabl.df()
        if tabl.centroids is not None:

            k = len(tabl.centroids)
            cluster_params = self.window.calc.cluster_params
            print("hist ", cluster_params)

            # subplot_num = 0
            # print(self.centroids)
            y_pos = pd.np.arange(len(cluster_params))
            # print(y_pos)
            width = 0.95 / k
            # colors = ["#d62728", "blue", "green", "brown"]
            max_y_val = 0
            for c in range(0, k):
                # subplot_num += 1
                ystdev = []
                one_value_cluster = d.loc[d['_cluster'] == c]

                # for i in range(0, len(datasource.GRAIN_COLS)):
                for i in range(0, len(cluster_params)):
                    col = one_value_cluster[cluster_params[i]]
                    stdev = pd.np.std(col)
                    ystdev.append(stdev)

                cen = [
                    tabl.centroids[c][i]
                    for i in range(0, len(cluster_params))
                ]
                ## cluster label for legend
                c_label = "Cluster " + str(c)
                self.ax.bar(
                    y_pos + width * (c - (k / 2.3)),
                    cen,
                    width,
                    align="center",
                    log=self.lgvar,  #alpha=0.75,
                    color=COLORS[c],
                    ecolor="black",
                    yerr=ystdev,
                    label=c_label)
                for i in range(0, len(cen)):
                    y_val = cen[i] + ystdev[i]
                    #print("y_val:",str(y_val))
                    if y_val > max_y_val:
                        max_y_val = y_val
                        #print("new max_y_val:",str(max_y_val))

                #print("cen:",str(cen))
                #print("ysdtev:",str(ystdev))
                #print("ypos:",str(y_pos))

            self.ax.grid(True)
            # self.ax.set_xticklabels
            # self.ax.set_ylim(0, 1, emit=False)
            #max_y_val = max(map(max, tabl.centroids))
            self.ax.set_ylim(0, max_y_val * 1.05, emit=False)

            self.ax.set_xticks(y_pos + width / 4)
            self.ax.set_xticklabels(cluster_params)

            #        self.ax.callbacks.connect('xlim_changed', self.handle_view_change)
            #        self.ax.callbacks.connect('ylim_changed', self.handle_view_change)

            ## add legend
            self.ax.legend(loc="upper right", shadow=True)
        else:
            cluster_params = [
                col
                for col in self.window.calc.get_all_columns(after_calc=True)
                if col not in ["OutdoorTemp", "RelHumidity", "Daytime"]
            ]
            print("hist ", cluster_params)

            # subplot_num = 0
            # print(self.centroids)
            y_pos = pd.np.arange(len(cluster_params))
            # print(y_pos)
            width = 0.95 / 1
            # colors = ["#d62728", "blue", "green", "brown"]
            # subplot_num += 1
            ystdev = []
            one_value_cluster = d

            # for i in range(0, len(datasource.GRAIN_COLS)):
            for i in range(0, len(cluster_params)):
                col = one_value_cluster[cluster_params[i]]
                stdev = pd.np.std(col)
                ystdev.append(stdev)

            cen = d[cluster_params].mean(axis=0)
            print(type(cen), type(cen.max()), cen.max())
            ## cluster label for legend
            self.ax.bar(
                y_pos + width * (0 - (1 / 2.3)),
                cen,
                width,
                align="center",
                log=self.lgvar,  # alpha=0.75,
                color="blue",
                ecolor="black",
                yerr=ystdev)
            self.ax.grid(True)
            # self.ax.set_xticklabels
            # self.ax.set_ylim(0, 1, emit=False)
            max_y_val = cen.max()
            self.ax.set_ylim(0, max_y_val * 1.1, emit=False)

            self.ax.set_xticks(y_pos + width / 4)
            self.ax.set_xticklabels(cluster_params)

        ## add legend
#            self.ax.legend(loc="upper right", shadow=True)

        self.canvas = FigureCanvasTkAgg(self.fig,
                                        self)  #type:FigureCanvasTkAgg

        self.canvas.get_tk_widget().grid(column=1,
                                         row=0,
                                         sticky=(N, E, W, S),
                                         rowspan=2)
Exemple #9
0
    def create_widgets(self):
        """Populate object and its widgets."""
        self.variable = {}
        self.label = {}
        self.widget = {}
        self.tab = {}
        self.group = {}
        self.notebook = Notebook(self)
        self.notebook.pack(fill="both", expand=True)

        if self.fields is None:
            return

        for i, (name, desc) in enumerate(self.fields.items()):
            if "tab" not in desc:
                desc["tab"] = "main"

            if desc["tab"] not in self.tab:
                parent = Frame(self.notebook)
                parent.columnconfigure([0, 1],
                                       weight=1,
                                       minsize=self.column_minsize)
                self.notebook.add(parent, text=desc["tab"].capitalize())
                self.tab[desc["tab"]] = parent
            else:
                parent = self.tab[desc["tab"]]

            if "group" in desc:
                if desc["group"] not in self.group:
                    group = LabelFrame(parent, text=desc["group"].capitalize())
                    group.columnconfigure([0, 1],
                                          weight=1,
                                          minsize=self.column_minsize)
                    group.grid(
                        row=i,
                        column=0,
                        columnspan=2,
                        sticky="ew",
                        padx=self.padx,
                        pady=9 * self.pady,
                    )
                    self.group[desc["group"]] = group
                else:
                    group = self.group[desc["group"]]
                parent = group

            if "values" in desc:
                values = list(desc["values"])

            if "type" not in desc:
                # if no type is given, first guess it based on a default value,
                # or infer from the first valid value.
                if "default" in desc and desc["default"] is not None:
                    desc["type"] = type(desc["default"])
                elif "values" in desc:
                    desc["type"] = type([v for v in values
                                         if v is not None][0])
                else:
                    raise ValueError(
                        f"could not infer type, please specify: {desc}")

            if "default" not in desc:
                # if no default is given, use the first value (even if None),
                # or infer from type.
                if "values" in desc:
                    desc["default"] = [v for v in values][0]
                elif "type" in desc:
                    desc["default"] = desc["type"]()
                else:
                    raise ValueError(
                        f"could not infer default, please specify: {desc}")

            if desc["type"] is int or desc["type"] is np.int64:
                self.variable[name] = IntVar(self)
            elif desc["type"] is bool:
                self.variable[name] = BooleanVar(self)
            elif desc["type"] is str:
                self.variable[name] = StringVar(self)
            elif desc["type"] is float:
                self.variable[name] = DoubleVar(self)
                if "values" in desc:
                    values = [np.round(v, 2) for v in values]
            else:
                raise ValueError(f"unknown type '{desc['type']}' for '{name}'")

            if "text" in desc:
                text = desc["text"]
            else:
                text = name.capitalize()

            if "widget" not in desc:
                # TODO(schneiderfelipe): should this be default?
                desc["widget"] = Combobox

            if desc["widget"] is Checkbutton:
                self.widget[name] = desc["widget"](
                    parent, variable=self.variable[name], text=text)
            elif "values" in desc:
                self.widget[name] = desc["widget"](
                    parent, textvariable=self.variable[name], values=values)
            else:
                self.widget[name] = desc["widget"](
                    parent, textvariable=self.variable[name])
            self.widget[name].grid(row=i,
                                   column=1,
                                   sticky="ew",
                                   padx=self.padx,
                                   pady=self.pady)

            if "help" in desc:
                create_tooltip(self.widget[name], desc["help"])

            if desc["widget"] is not Checkbutton:
                self.label[name] = Label(parent, text=text + ":")
                self.label[name].grid(
                    row=i,
                    column=0,
                    sticky="ew",
                    padx=self.padx,
                    pady=self.pady,
                )

            if "visible" not in desc:
                desc["visible"] = True

        self.init_widgets()
Exemple #10
0
class GameTab(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self._main_window = self.master.master
        self._land_land_roll = False
        self._heading = Frame(self)
        self._inventory_display = Frame(self)
        self._die_field = Frame(self)
        self._score_field = LabelFrame(self)
        self._turn_order = Frame(self)
        self.die = None
        if self._main_window.is_game_in_progress.get():
            self._load_content()

    @property
    def _current_player(self):
        return self._main_window.engine.aktivjatekos

    def reset_content(self):
        self._heading.destroy()
        self._heading = Frame(self)
        self._inventory_display.destroy()
        self._inventory_display = Frame(self)
        self._die_field.destroy()
        self._die_field = Frame(self)
        self._score_field.destroy()
        self._score_field = LabelFrame(self)
        self._turn_order.destroy()
        self._turn_order = Frame(self)
        if self.die:
            self.die.destroy()
        self.die = None
        self._load_content()

    def _load_content(self):
        self.master.tab(
            1, state=NORMAL)  # TODO fix it when window is being resized.
        self._build_heading(0)
        self._build_inventory_display(1)
        self._build_score_field(2)
        self._build_state_display(3)
        self._horizontal_line(4)
        self._build_die_field(5)
        self._horizontal_line(6)
        self._build_turn_order(7)

    def _build_heading(self, position):
        flag = Gallery.get(
            f"flag_{self._current_player.empire.adjective.lower()}")
        Label(self._heading, text=self._current_player.name).grid(row=0,
                                                                  column=0)
        Label(self._heading, image=flag).grid(row=1, column=0)
        self._heading.grid(row=position, column=0, pady=5)

    def _build_inventory_display(self, position):
        gold_frame = LabelFrame(self._inventory_display,
                                text=s.language.treasure)
        Label(gold_frame, image=Gallery.get("penz-d2")).grid(row=0, column=0)
        Label(gold_frame,
              textvariable=self._current_player.gold).grid(row=0, column=1)
        gold_frame.grid(row=0, column=0, sticky=N + E + W + S, padx=5)
        crew_frame = LabelFrame(self._inventory_display, text=s.language.crew)
        Label(crew_frame, image=Gallery.get("crew")).grid(row=0, column=0)
        Label(crew_frame,
              textvariable=self._current_player.crew).grid(row=0, column=1)
        crew_frame.grid(row=0, column=1, sticky=N + E + W + S, padx=5)
        self._inventory_display.grid(row=position, column=0)
        self._inventory_display.columnconfigure(
            ALL, minsize=(self.master.width - 20) / 2)

    def _horizontal_line(self, position):
        Separator(self, orient=HORIZONTAL).grid(row=position,
                                                column=0,
                                                sticky=E + W,
                                                padx=5,
                                                pady=5)

    def _build_score_field(self, position):
        self._score_field.config(text=s.language.scores)
        score_fields = {}
        target_empires = [empire.value for empire in Empires]
        target_empires.remove(self._current_player.empire)
        for index, empire in enumerate(target_empires):
            score_fields[empire.adjective] = Frame(self._score_field)
            flag = Gallery.get(f"flag_{empire.adjective.lower()}")
            Label(score_fields[empire.adjective], image=flag).grid(row=0,
                                                                   column=0)
            Label(score_fields[empire.adjective], text=':').grid(row=0,
                                                                 column=1)
            Label(score_fields[empire.adjective],
                  textvariable=self._current_player.scores[
                      empire.adjective]).grid(row=0, column=2)
            score_fields[empire.adjective].grid(row=int((index / 2) % 2),
                                                column=index % 2,
                                                sticky=E + W)
        self._score_field.grid(row=position, column=0)
        self._score_field.columnconfigure(ALL,
                                          minsize=(self.master.width - 34) / 2)

    def _build_die_field(self, position):
        self._die_field.columnconfigure(0, weight=1)
        self._die_field.rowconfigure(0, weight=1)
        self._die_field.grid(row=position, column=0, ipady=5, ipadx=5)
        should_miss_turn = self._current_player.turns_to_miss.get()
        if should_miss_turn > 0:
            self._build_miss_turn_button(should_miss_turn)
        else:
            self._build_die()

    def _build_miss_turn_button(self, should_miss_turn):
        if should_miss_turn > 1:
            message = s.language.miss_turn % should_miss_turn
        else:
            message = s.language.miss_turn_last_time
        Button(self._die_field,
               text=message,
               command=self._main_window.engine.kimaradas).pack()
        if "leviathan" in self._current_player.states:
            command = self._main_window.engine.leviathan_kijatszasa
            Button(self._die_field,
                   text=s.language.play_leviathan,
                   command=command).pack()

    def _build_die(self):
        self._die_field.config(relief=RAISED, bd=2)
        self.die = Die(self._die_field, self.master.width / 4,
                       self._current_player.color,
                       self._current_player.secondary_color,
                       self._current_player.last_roll)
        castaway_tiles = self._main_window.game_board.locations["castaways"]
        player_is_on_castaway_island = self._current_player.coordinates in castaway_tiles
        player_has_no_crew = not self._current_player.crew.get()
        if player_is_on_castaway_island and player_has_no_crew:
            self.die.bind('<Button-1>', self._main_window.engine.szamuzottek)
        else:
            self.die.bind('<Button-1>', self._roll_die)
        self.die.grid(row=0, column=0)

    def _build_turn_order(self, position):
        players = []
        turn_order_label = Label(self._turn_order, text=s.language.turn_order)
        turn_order_label.grid(row=0, column=0, sticky=W)
        for index, player_name in enumerate(self._main_window.player_order):
            player = self._main_window.players[player_name]
            players.append(
                Label(self._turn_order,
                      text=str(index + 1) + '. ' + player.name,
                      bg=player.color,
                      fg=player.secondary_color))
            players[-1].grid(row=index + 1, column=0, sticky=W, padx=10)
        self._turn_order.grid(row=position, column=0, sticky=W, padx=5)

    def _build_state_display(self, position):
        state_field = LabelFrame(self,
                                 text=s.language.cards,
                                 relief=RAISED,
                                 width=self.master.width - 31)
        state_slots_per_row = int((self.master.width - 31) / 32)
        state_slot_height = 24 + (
            (int(len(self._current_player.states) / state_slots_per_row) + 1) *
            32)
        if self._current_player.states:
            for index, state in enumerate(self._current_player.states):
                if state not in self._main_window.engine.nemKartyaStatusz:
                    if state in self._main_window.engine.eventszotar.keys():
                        origin = self._main_window.engine.eventszotar
                        prefix = "event"
                    else:
                        origin = self._main_window.engine.kincsszotar
                        prefix = "treasure"
                    icon = f"{prefix}_{self._main_window.engine.eventszotar[state].kep}_i"
                    icon = icon[(icon.find('_') + 1):]
                    button = Button(
                        state_field,
                        image=Gallery.get(icon),
                        command=lambda s=state: origin[s].megjelenik(1))
                    button.grid(row=int(index / state_slots_per_row),
                                column=index % state_slots_per_row)
            state_field.config(height=state_slot_height)
            if state_field.winfo_children():
                state_field.grid(row=position, column=0)
            state_field.grid_propagate(False)

    def _roll_die(self, event):
        if self._land_land_roll:
            self._main_window.game_board.turn_off_blinker()
            self._main_window.engine.dobasMegtortent.set(False)
        if not self._main_window.engine.dobasMegtortent.get():
            dobas = self.die.roll()
            if "landland" in self._current_player.states:
                debug("New roll is possible because of Land, land! bonus.")
                self._main_window.status_bar.log(s.language.land_log)
                self._current_player.remove_state("landland")
                self._land_land_roll = True
            else:
                self._main_window.status_bar.log('')
                self._die_field.config(relief=SUNKEN)
            self._main_window.engine.set_dobasMegtortent()
            self._main_window.is_turn_in_progress.set(1)
            self._main_window.engine.aktivjatekos.last_roll = dobas
            self._main_window.engine.mozgas(dobas, 1)

    def disable_additional_roll(self):
        if self._land_land_roll is False:
            self._main_window.status_bar.log('')
            self._die_field.config(relief=SUNKEN)
            self._land_land_roll = False
    def crea_GUI(self):
        root = self.root
        #Menús
        self.menubar = Menu(root, tearoff=0)

        m_archivo = Menu(self.menubar, tearoff=0)
        m_archivo.add_command(label='Abrir',
                              command=self.abrir_dataset,
                              accelerator='Ctrl+O')
        m_archivo.add_separator()
        m_archivo.add_command(label='Salir', command=self.cerrar_aplicacion)
        self.menubar.add_cascade(label='Archivo', menu=m_archivo)

        m_proyecto = Menu(self.menubar, tearoff=0)
        m_proyecto.add_command(label='Abrir',
                               command=self.abrir_proyecto,
                               state="disabled")
        m_proyecto.add_separator()
        m_proyecto.add_checkbutton(label='Clase al final',
                                   onvalue=True,
                                   offvalue=False,
                                   variable=self.clase_al_final)
        self.menubar.add_cascade(label='Proyecto', menu=m_proyecto)

        self.m_configuracion = Menu(self.menubar, tearoff=0)
        self.m_configuracion.add_command(
            label='Ruta datasets', command=lambda: self.rutas('datasets'))
        self.m_configuracion.add_command(
            label='Ruta resultados', command=lambda: self.rutas('resultados'))
        self.m_configuracion.add_checkbutton(label='Rutas relativas',
                                             onvalue=True,
                                             offvalue=False,
                                             variable=self.rutas_relativas,
                                             command=self.cambia_rutas)
        self.m_configuracion.add_separator()
        #TODO Revisar self.v_tamanyo_muestra, no la uso
        #        self.v_tamanyo_muestra = StringVar(root, 'Tamaño muestra ({:,})'.\
        #                                           format(self._tamanyo_muestra))
        self.m_cfg_tamanyo_muestra = \
            self.m_configuracion.add_command(label='Tamaño muestra ({:,})'.\
                                           format(self._tamanyo_muestra),
                                        command=lambda: self.tamanyo_muestra(\
                                                        self._tamanyo_muestra))
        self.m_configuracion.add_separator()
        self.m_configuracion.add_checkbutton(label='Utiliza sha1',
                                             onvalue=True,
                                             offvalue=False,
                                             variable=self.usa_sha1)
        self.menubar.add_cascade(label='Configuración',
                                 menu=self.m_configuracion)

        m_ver = Menu(self.menubar, tearoff=0)
        self.v_tipo_dataset = StringVar(self.root, 'Dataset original')
        m_ver.add_radiobutton(label='Dataset original',
                              value='Dataset original',
                              variable=self.v_tipo_dataset,
                              command=self.muestra_atributos_y_clase)
        m_ver.add_radiobutton(label='Dataset sin evidencias incompletas',
                              value='Dataset sin evidencias incompletas',
                              variable=self.v_tipo_dataset,
                              command=self.muestra_atributos_y_clase)
        m_ver.add_radiobutton(label='Dataset sin atributos constantes',
                              value='Dataset sin atributos constantes',
                              variable=self.v_tipo_dataset,
                              command=self.muestra_atributos_y_clase)
        m_ver.add_radiobutton(label='Catálogo',
                              value='Catálogo',
                              variable=self.v_tipo_dataset,
                              command=self.muestra_atributos_y_clase)
        m_ver.add_radiobutton(label='Catálogo Robusto',
                              value='Catálogo Robusto',
                              variable=self.v_tipo_dataset,
                              command=self.muestra_atributos_y_clase)
        m_ver.add_separator()
        m_ver.add_checkbutton(label='Log del proceso',
                              onvalue=True,
                              offvalue=False,
                              variable=self.mostrar_proceso,
                              state='disabled')
        self.menubar.add_cascade(label='Ver', menu=m_ver)

        root.config(menu=self.menubar)

        #Dataset de clasificación
        lf_dataset = LabelFrame(root, text='Dataset de Clasificación')
        lf_dataset.pack(fill='both', expand=True, padx=5, pady=5)

        Label(lf_dataset, text='Nombre:').grid(row=0, column=0, sticky='e')
        self.v_nombre_dataset = StringVar(root, '-------')
        self.l_nombre_dataset = Label(lf_dataset,
                                      textvariable=self.v_nombre_dataset)
        self.l_nombre_dataset.grid(row=0, column=1, sticky='w')

        Label(lf_dataset, text='Tamaño:').grid(row=0, column=2, sticky='e')
        self.v_tamanyo_dataset = StringVar(root, '-------')
        Label(lf_dataset, textvariable=self.v_tamanyo_dataset).grid(row=0,
                                                                    column=3,
                                                                    sticky='w')

        Label(lf_dataset, text='Ubicación:').grid(row=1, column=0, sticky='e')
        self.v_ruta_dataset = StringVar(root, '-------------------------')
        #TODO Expandir en columnas 1-3, puede ser muy larga
        Label(lf_dataset, textvariable=self.v_ruta_dataset).grid(row=1,
                                                                 column=1,
                                                                 sticky='w',
                                                                 columnspan=3)

        #Dataset de clasificación / Muestra
        lf_dataset_muestra = LabelFrame(lf_dataset, text='Muestra')
        lf_dataset_muestra.grid(row=2,
                                column=0,
                                sticky='nsew',
                                columnspan=4,
                                padx=5,
                                pady=5)

        self.sb_v_t_muestra = Scrollbar(lf_dataset_muestra)
        self.sb_v_t_muestra.grid(row=0, column=1, sticky='sn')

        self.sb_h_t_muestra = Scrollbar(lf_dataset_muestra,
                                        orient='horizontal')
        self.sb_h_t_muestra.grid(row=1, column=0, sticky='ew')

        self.t_muestra = Text(lf_dataset_muestra,
                              yscrollcommand=self.sb_v_t_muestra.set,
                              xscrollcommand=self.sb_h_t_muestra.set,
                              bd=0,
                              wrap='none',
                              state='disabled',
                              height=8)
        self.t_muestra.grid(row=0, column=0, sticky='nswe')

        self.sb_v_t_muestra.config(command=self.t_muestra.yview)
        self.sb_h_t_muestra.config(command=self.t_muestra.xview)

        lf_dataset_muestra.rowconfigure(0, weight=1)
        lf_dataset_muestra.columnconfigure(0, weight=1)

        lf_dataset.rowconfigure(2, weight=3)
        lf_dataset.columnconfigure(1, weight=1)
        lf_dataset.columnconfigure(3, weight=1)

        #Dataset de clasificación / Evidencias
        lf_dataset_evidencias = LabelFrame(lf_dataset, text='Evidencias')
        lf_dataset_evidencias.grid(row=3,
                                   column=0,
                                   sticky='nsew',
                                   padx=5,
                                   pady=5)

        Label(lf_dataset_evidencias, text='Total:').grid(row=0,
                                                         column=0,
                                                         sticky='e')
        self.v_evidencias_total = StringVar(root, '-------')
        Label(lf_dataset_evidencias, textvariable=self.v_evidencias_total).\
              grid(row=0, column=1, sticky='w')
        Label(lf_dataset_evidencias, text='Completas:').grid(row=1,
                                                             column=0,
                                                             sticky='e')
        self.v_evidencias_completas = StringVar(root, '-------')
        Label(lf_dataset_evidencias, textvariable=self.v_evidencias_completas).\
              grid(row=1, column=1, sticky='w')
        Label(lf_dataset_evidencias, text='Únicas:').grid(row=2,
                                                          column=0,
                                                          sticky='e')
        self.v_evidencias_catalogo = StringVar(root, '-------')
        Label(lf_dataset_evidencias, textvariable=self.v_evidencias_catalogo).\
              grid(row=2, column=1, sticky='w')
        Label(lf_dataset_evidencias, text='Robustas:').grid(row=3,
                                                            column=0,
                                                            sticky='e')
        self.v_evidencias_robustas = StringVar(root, '-------')
        Label(lf_dataset_evidencias, textvariable=self.v_evidencias_robustas).\
              grid(row=3, column=1, sticky='w')

        #Dataset de clasificación / Atributos
        lf_dataset_clase_y_atributos = LabelFrame(lf_dataset,
                                                  text='Clase y atributos')
        lf_dataset_clase_y_atributos.grid(row=3,
                                          column=1,
                                          sticky='nsew',
                                          columnspan=3,
                                          padx=5,
                                          pady=5)

        PROPIEDADES_ATRIBUTOS = ('Nombre', 'count', 'unique', 'top', 'freq',
                                 'mean', 'std', 'min', '25%', '50%', '75%',
                                 'max')

        self.sb_h_tv_clase = Scrollbar(lf_dataset_clase_y_atributos,
                                       orient='horizontal')
        self.sb_h_tv_clase.grid(row=1, column=0, sticky='ew')
        self.tv_clase = Treeview(lf_dataset_clase_y_atributos,
                                 columns=PROPIEDADES_ATRIBUTOS,
                                 height=1,
                                 xscrollcommand=self.sb_h_tv_clase.set)
        self.tv_clase.grid(row=0, column=0, sticky='ew')
        self.sb_h_tv_clase.config(command=self.tv_clase.xview)
        self.tv_clase.heading("#0", text="#")
        self.tv_clase.column("#0", minwidth=30, width=40, stretch=False)

        self.sb_v_tv_atributos = Scrollbar(lf_dataset_clase_y_atributos)
        self.sb_v_tv_atributos.grid(row=2, column=1, sticky='sn')
        self.sb_h_tv_atributos = Scrollbar(lf_dataset_clase_y_atributos,
                                           orient='horizontal')
        self.sb_h_tv_atributos.grid(row=3, column=0, sticky='ew')
        self.tv_atributos = Treeview(lf_dataset_clase_y_atributos,
                                     columns=PROPIEDADES_ATRIBUTOS,
                                     yscrollcommand=self.sb_v_tv_atributos.set,
                                     xscrollcommand=self.sb_h_tv_atributos.set)
        self.tv_atributos.grid(row=2, column=0, sticky='nsew')
        self.tv_atributos.bind('<ButtonRelease-1>', self.selectItem)
        self.sb_v_tv_atributos.config(command=self.tv_atributos.yview)
        self.sb_h_tv_atributos.config(command=self.tv_atributos.xview)
        self.tv_atributos.heading("#0", text="#")
        self.tv_atributos.column("#0", minwidth=30, width=40, stretch=False)

        for i in PROPIEDADES_ATRIBUTOS:
            self.tv_clase.heading(i, text=i)
            self.tv_clase.column(i, minwidth=50, width=50, stretch=False)
            self.tv_atributos.heading(i, text=i)
            self.tv_atributos.column(i, minwidth=50, width=50, stretch=False)

        lf_dataset_clase_y_atributos.rowconfigure(2, weight=1)
        lf_dataset_clase_y_atributos.columnconfigure(0, weight=1)

        lf_dataset.rowconfigure(3, weight=1)
Exemple #12
0
def make_widgets(frame: ttk.LabelFrame, pane: SubPane) -> SelectorWin:
    """Generate the UI components, and return the base window."""

    def for_channel(channel: MusicChannel) -> List[SelItem]:
        """Get the items needed for a specific channel."""
        music_list = []
        for music in Music.all():
            if music.provides_channel(channel):
                selitem = SEL_ITEMS[music.id].copy()
                selitem.snd_sample = music.get_sample(channel)
                music_list.append(selitem)
        return music_list

    # This gets overwritten when making windows.
    last_selected = {
        channel: GEN_OPTS.get_val(
            'Last_Selected',
            'music_' + channel.name.casefold(),
            '<NONE>',
        ) for channel in MusicChannel
    }

    base_win = WINDOWS[MusicChannel.BASE] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.BASE),
        title=_('Select Background Music - Base'),
        desc=_('This controls the background music used for a map. Expand '
               'the dropdown to set tracks for specific test elements.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Add no music to the map at all. Testing Element-specific '
                    'music may still be added.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.BASE],
        attributes=[
            SelAttr.bool('SPEED', _('Propulsion Gel SFX')),
            SelAttr.bool('BOUNCE', _('Repulsion Gel SFX')),
            SelAttr.bool('TBEAM', _('Excursion Funnel Music')),
            SelAttr.bool('TBEAM_SYNC', _('Synced Funnel Music')),
        ],
    )

    WINDOWS[MusicChannel.TBEAM] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.TBEAM),
        title=_('Select Excursion Funnel Music'),
        desc=_('Set the music used while inside Excursion Funnels.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Have no music playing when inside funnels.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.TBEAM],
        attributes=[
            SelAttr.bool('TBEAM_SYNC', _('Synced Funnel Music')),
        ],
    )

    WINDOWS[MusicChannel.BOUNCE] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.BOUNCE),
        title=_('Select Repulsion Gel Music'),
        desc=_('Select the music played when players jump on Repulsion Gel.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Add no music when jumping on Repulsion Gel.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.BOUNCE],
    )

    WINDOWS[MusicChannel.SPEED] = SelectorWin(
        TK_ROOT,
        for_channel(MusicChannel.SPEED),
        title=_('Select Propulsion Gel Music'),
        desc=_('Select music played when players have large amounts of horizontal velocity.'),
        has_none=True,
        sound_sys=filesystem,
        none_desc=_('Add no music while running fast.'),
        callback=selwin_callback,
        callback_params=[MusicChannel.SPEED],
    )

    assert set(WINDOWS.keys()) == set(MusicChannel), "Extra channels?"

    # Widgets we want to remove when collapsing.
    exp_widgets = []  # type: List[tkinter.Widget]

    def toggle_btn_enter(event=None):
        toggle_btn['text'] = BTN_EXPAND_HOVER if is_collapsed else BTN_CONTRACT_HOVER

    def toggle_btn_exit(event=None):
        toggle_btn['text'] = BTN_EXPAND if is_collapsed else BTN_CONTRACT

    def set_collapsed():
        """Configure for the collapsed state."""
        global is_collapsed
        is_collapsed = True
        GEN_OPTS['Last_Selected']['music_collapsed'] = '1'
        base_lbl['text'] = _('Music: ')
        toggle_btn_exit()

        # Set all music to the children - so those are used.
        set_suggested(WINDOWS[MusicChannel.BASE].chosen_id, sel_item=True)

        for wid in exp_widgets:
            wid.grid_remove()

    def set_expanded():
        """Configure for the expanded state."""
        global is_collapsed
        is_collapsed = False
        GEN_OPTS['Last_Selected']['music_collapsed'] = '0'
        base_lbl['text'] = _('Base: ')
        toggle_btn_exit()
        for wid in exp_widgets:
            wid.grid()
        pane.update_idletasks()
        pane.move()

    def toggle(event=None):
        if is_collapsed:
            set_expanded()
        else:
            set_collapsed()
        pane.update_idletasks()
        pane.move()

    frame.columnconfigure(2, weight=1)

    base_lbl = ttk.Label(frame)
    base_lbl.grid(row=0, column=1)

    toggle_btn = ttk.Label(frame, text=' ')
    toggle_btn.bind('<Enter>', toggle_btn_enter)
    toggle_btn.bind('<Leave>', toggle_btn_exit)
    toggle_btn.bind('<ButtonPress-1>', toggle)
    toggle_btn.grid(row=0, column=0)

    for row, channel in enumerate(MusicChannel):
        btn = WINDOWS[channel].widget(frame)
        if row:
            exp_widgets.append(btn)
        btn.grid(row=row, column=2, sticky='EW')

    for row, text in enumerate([
        _('Funnel:'),
        _('Bounce:'),
        _('Speed:'),
    ], start=1):
        label = ttk.Label(frame, text=text)
        exp_widgets.append(label)
        label.grid(row=row, column=1, sticky='EW')

    if GEN_OPTS.get_bool('Last_Selected', 'music_collapsed', True):
        set_collapsed()
    else:
        set_expanded()

    for channel, win in WINDOWS.items():
        win.sel_item_id(last_selected[channel])

    return base_win