动态链接3个Tkinter Scale小部件,使其总和始终为1

2024-09-25 02:31:05 发布

您现在位置:Python中文网/ 问答频道 /正文

所以我对编码还不熟悉,但我正在尝试制作我的第一个完整应用程序。我的目标是让三个Tkinter scale小部件以随机生成的节奏显示每个对应音符(拨动音符、明确沉默或持续音符)的概率/权重。这就是说,NumPy的随机模块要求所有三个概率都必须等于一——然而,我仍然希望保留默认的起始值

我尝试过广泛使用.get().set()方法以及Scale变量,但都没有效果。 几天过去了,我的大脑很痛。我无法让他们中的任何人动态链接。也许我需要一个更面向对象的方法吗

我剥离了所有功能和不必要的GUI元素,以帮助隔离问题:

from tkinter import *

# def notes_comp():
#   summ = (float(silence_weight.get()))+(float(sustain_weight.get()))
#   diff = 1 - (float(note_weight.get())+summ)
#   if float(note1_weight.get())+summ != 1:
#       silence_weight.set(silence_weight.get()+diff/2)
#       sustain_weight.set(sustain_weight.get()+diff/2)

def notes_comp(value):
    notes_weight = var_1.set("value")
    silence_set = var_2.get()
    sustain_set = var_3.get()
    for i in range(1 - notes_weight):
        silence_set += .5*(1 - var_1.get())
        sustain_set += .5*(1 - var_1.get())


# tkinter GUI
root = Tk(className=" Rhythm Generator")
# setting color variables
bg_color = "#1c1c1c"
rollover_color = "#d9d9d9"
var_1 = DoubleVar()
var_2 = DoubleVar()
var_3 = DoubleVar()
root.configure(bg=bg_color)

# creating master frames
top_frame = Frame(root, bg=bg_color)
top_frame.grid(row="0")

# creating sliders
note1_weight = Scale(top_frame, from_="1", to="0", resolution=".1", variable="var_1", command="notes_comp")
note1_weight.grid(column="2", row="0")
silence_weight = Scale(top_frame, from_="1", to="0", resolution=".1", variable="var_2")
silence_weight.grid(column="3", row="0")
sustain_weight = Scale(top_frame, from_="1", to="0", resolution=".1", variable="var_3")
sustain_weight.grid(column="4", row="0")
note1_weight.set(.1)
silence_weight.set(.1)
sustain_weight.set(.8)

root.mainloop()

Tags: fromgetvartopfloatframecolornotes
2条回答

这段代码解决了在移动Scale时运行notes_comp的一些问题,但它并不能解决所有值之和等于1的问题

在所有情况下都必须使用变量,而不是字符串"var_1""notes_comp"

variable=var_1, command=notes_comp

对于每个Scale,我使用两个DoubleVar-current_varprevious_var,这样我可以比较它是如何变化的,并尝试使用差异current_var - previous_var来改变其他值。但这是一个问题,因为我不能将(current_var - previous_var)/2添加到两者中,因为其中一个可能已经具有最大值。另一个问题是(current_var - previous_var)/2可能给出值0.05,但是Scaleresolution=".1",所以它不能显示0.050.150.25等等。如果我改变resolution=".05",那么(current_var - previous_var)/2将给出值0.025Scale将不会播放0.0250.0750.125,等等

另一个问题是(通常情况下)float值,这些值有时给出不同的结果,然后我们可以期望-即0.1 + 0.2 == 0.3给出False,但我们可以期望True-因为计算机只保留一些近似值

from tkinter import *

# def notes_comp():
#   summ = (float(silence_weight.get()))+(float(sustain_weight.get()))
#   diff = 1 - (float(note_weight.get())+summ)
#   if float(note1_weight.get())+summ != 1:
#       silence_weight.set(silence_weight.get()+diff/2)
#       sustain_weight.set(sustain_weight.get()+diff/2)

def notes_comp(value):
    #
    # this calculations doesn't work correctly
    #

    print('running notes_comp:')

    # compare difference - minimal value `0.1`
    diff = current_var_1.get() - previous_var_1.get()

    # sometimes `half1` can be `0.0` and then `half2 has to be `0.1` instead of `0.05`
    half1 = round(diff/2, 1)
    half2 = round(diff-half1, 1)
    #print(previous_var_1.get(), current_var_1.get(), half1, half2)

    # use it to change other valus
    current_var_2.set(current_var_2.get() - half1)
    current_var_3.set(current_var_3.get() - half2)

    # remember current value to use it next time
    previous_var_1.set(current_var_1.get())


# tkinter GUI
root = Tk(className=" Rhythm Generator")
# setting color variables
bg_color = "#1c1c1c"
rollover_color = "#d9d9d9"

current_var_1 = DoubleVar()
current_var_2 = DoubleVar()
current_var_3 = DoubleVar()

previous_var_1 = DoubleVar()
previous_var_2 = DoubleVar()
previous_var_3 = DoubleVar()

root.configure(bg=bg_color)

# creating master frames
top_frame = Frame(root, bg=bg_color)
top_frame.grid(row="0")

# creating sliders
note1_weight = Scale(top_frame, from_="1", to="0", resolution=".1", variable=current_var_1, command=notes_comp)
note1_weight.grid(column="2", row="0")

silence_weight = Scale(top_frame, from_="1", to="0", resolution=".1", variable=current_var_2)
silence_weight.grid(column="3", row="0")

sustain_weight = Scale(top_frame, from_="1", to="0", resolution=".1", variable=current_var_3)
sustain_weight.grid(column="4", row="0")

# start values
current_var_1.set(.1)
previous_var_1.set(.1)

current_var_2.set(.1)
previous_var_2.set(.1)

current_var_3.set(.8)
previous_var_3.set(.8)


root.mainloop()

由于缩放小部件命令回调函数在缩放小部件发生更改时被调用,因此回调必须“关闭”,因为小部件被设置为避免调用回调函数的无限序列,因为小部件1的更改会影响小部件2&;3.

import tkinter as tk

root = tk.Tk()
top_frame = tk.Frame(root, padx = 10, pady = 10 ).grid()
root.title('3 Scales')

top_frame = tk.Frame(root, padx = 10, pady = 10 ).grid()

# Column headers
for col, txt in enumerate([ 'Note 1', 'Silence', 'Sustain' ]):
    tk.Label( text = txt).grid( row = 0, column = col )

# Create & grid Scale widgets
note1_weight = tk.Scale(top_frame, from_= 1, to= 0, resolution = 0.01 )
note1_weight.grid(column=0, row=2)

silence_weight = tk.Scale(top_frame, from_= 1, to= 0, resolution = 0.01 )
silence_weight.grid(column=1, row=2)

sustain_weight = tk.Scale(top_frame, from_= 1, to= 0, resolution= 0.01 )
sustain_weight.grid(column=2, row=2)

# Initialise the widgets
note1_weight.set(0.34)
silence_weight.set(0.33)
sustain_weight.set(0.33)

# Container for the command functions
# Filled after make_command is defined.
command_func = [ 0 ] * 3

def commands_activate():
    """ makes all the command functions active """
    note1_weight.config( command = command_func[0] )
    silence_weight.config( command = command_func[1] )
    sustain_weight.config( command = command_func[2] )

def commands_deactivate():
    """ Stops all the command functions being called """
    note1_weight.config( command = "" )
    silence_weight.config( command = "" )
    sustain_weight.config( command = "" )

def make_command( other0, other1 ):
    """ Generates a command function thar updates the 'other' two Scale widgets.
        Using the closure avoids writing 3 almost identcal functions
    """
    def cmd( pos ):
        """ A command callback function which updates widgets
            other0 and other1 as the current widget changes.
        """
        commands_deactivate()           
        # Stop the .set(..) in this function triggering further calls to cmd

        ########################################################################
        # This section of code performs the sum == 1.0 calculations
        # It can be changed to allocate the diff as required.
        # Here it prorates the difference from 1.0 across the other two widgets

        diff = 1.0 - float(pos)
        v0 = max( other0.get(), 0.0001)  # If both v0 and v1 == 0.0 there's no prorating
        v1 = max( other1.get(), 0.0001)  # so ensure v0 and v1 are never precisely zero.
        fact = diff / ( v0 + v1 )
        other0.set( v0 * fact )
        other1.set( v1 * fact )
        ########################################################################

        commands_activate()
        # Then reactivate the commands

    return cmd

# Generate the three command functions
command_func[0] = make_command( silence_weight, sustain_weight )
command_func[1] = make_command( note1_weight, sustain_weight )
command_func[2] = make_command( note1_weight, silence_weight )

# Activate the command functions before mainloop
commands_activate()

root.mainloop()

相关问题 更多 >