Динамическое связывание 3 виджетов Tkinter Scale, так что их сумма всегда 1 - PullRequest
0 голосов
/ 08 апреля 2020

Итак, я новичок в кодировании, но я пытаюсь сделать свое первое полное приложение. Моя цель состоит в том, чтобы три виджета шкалы Tkinter представляли вероятность / вес каждой соответствующей ноты (либо взятой ноты, явной тишины или устойчивой ноты), отображаемой в произвольно сгенерированном ритме. Тем не менее, модуль NumPy Random требует, чтобы все три вероятности были равны единице - однако я все еще хочу сохранить начальное значение по умолчанию.

Я пытался использовать .get() и * Методы 1004 *, а также переменная Scale, все безрезультатно. Прошло несколько дней, и мой мозг болит. Я не могу заставить кого-либо из них динамически связываться. Нужен ли мне более OOP подход, может быть?

Я лишил все функциональные возможности и ненужные элементы 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()

Ответы [ 2 ]

1 голос
/ 09 апреля 2020

Этот код решает проблему с запуском notes_comp при перемещении Scale, но не решает проблему сохранения суммы всех значений равной 1.

Во всех ситуациях вы должны использовать переменные, а не строки "var_1" и "notes_comp"

variable=var_1, command=notes_comp

Для каждого Scale я использую два DoubleVar - current_var и previous_var, чтобы я мог сравнить, как это изменилось, и попытаться использовать разницу current_var - previous_var для изменения других значений. Но это проблема, потому что я не могу добавить (current_var - previous_var)/2 к обоим, потому что один из них может иметь максимальное значение. Другая проблема заключается в том, что (current_var - previous_var)/2 может давать значение 0.05, но Scale имеет resolution=".1", поэтому не может отображать 0.05, 0.15, 0.25 и т.д. c. Если бы я изменил resolution=".05", то (current_var - previous_var)/2 дал бы значение 0.025, а Scale не отобразил бы 0.025, 0.075, 0.125, et c.

Другая проблема делает (как обычно) float значения, которые иногда дают результаты, отличные от ожидаемых - ie. 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()
0 голосов
/ 10 апреля 2020

Поскольку функции обратного вызова команды виджета Scale вызываются всякий раз, когда виджеты Scale изменяются, обратные вызовы должны быть «отключены», поскольку виджеты установлены, чтобы избежать бесконечной последовательности вызовов функций обратного вызова, поскольку изменение виджета 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...