Введите имя строки графически, прямо на рисунке через matplotlib

У меня есть простая идея, которую я хотел бы реализовать. Из некоторых данных на одномерном графике извлеките разные сегменты, выбрав начальную и конечную точки. При этом для каждого выделенного сегмента укажите соответствующее (строковое) имя этой линии. Эти три значения (имя, начальный индекс, конечный индекс) затем сохраняются для дальнейшего использования.

Я включил минимальный рабочий пример. Это работает, но я предпочитаю, чтобы ввод «имени» выполнялся на фигуре, а не на терминале. Другими словами, используйте что-то вроде TextBox, а не input. Проблема в том, что я не могу воспроизвести поведение input из примера TextBox. В основном я не могу заставить программу «ждать, пока пользователь введет имя строки, прежде чем продолжить работу с программой». Вместо этого он продолжает принимать входные данные.

В любом случае, ниже MWE. Можете ли вы помочь мне найти способ воспроизвести функцию input графически? Спасибо и надеюсь, что это окажется полезным для будущих читателей в их начинаниях :)

import numpy as np
from matplotlib import pyplot as plt


def main():

    # Predefine a fixed number of segments, for demonstration.
    nSegment = 3

    # Counter for the number of points selected.
    nPointPressed = 0   

    # Default initial starting point index.
    I0 = 0

    # Initialize segment variable.
    segment = []

    def onpick(event):
        # Non-local and global variables.
        nonlocal nPointPressed
        nonlocal I0

        # Get the index of the point chosen.
        ind = event.ind

        # Points selected must be one.
        if len(ind) != 1:
            return
    
        # Illustrate graphically the selected segment.
        ax.plot(x[ind], y[ind], 'o', markerfacecolor='none', color='red', linewidth=2, markersize=5)
        plt.draw() 

        # Increment the number of points pressed.
        nPointPressed += 1
        
        # Distinguish between starting/ending indices. Odd values are starting indices, even ones are ending indices.
        if nPointPressed % 2 == 0:
        
            # An overkill, but for demonstration, explicitly define the interval.
            ij = np.linspace( I0, ind, dtype=int )

            # Plot the connecting line between the two indices.
            plt.plot(x[ij], y[ij], color='green')
            
            # Ask the user for an explicit segment name.
            name = input("Enter a name for current line segment: ")

            # Book-keep the current segment information: ["name", iStart, iEnd].
            segment.append([name, ij[0], ij[-1]])

            # Extract the plots from the figure and remove the starting and ending points.
            pts = ax.get_lines()
            pts[-3].remove() # start point
            pts[-2].remove() # end   point


            # Change the color of the most recent connecting line.
            pts[-1].set_color("green")
            
        else:
            # Save the starting index.
            I0 = ind

       
        # Expected segments are complete, close the figure.
        if len(segment) == nSegment:
            plt.close()


    # Some data.
    x = np.linspace( 0, 2*np.pi, 20 , dtype=float )
    y = np.sin( x ) 
   
    # Plot original data and enable interaction.
    fig, ax = plt.subplots()
    ax.plot(x, y, '-^', picker=True)
    fig.canvas.mpl_connect("pick_event", onpick)
    plt.show()
 
    # For demonstration, print the resulting segment information.
    for i in range( len(segment) ):
        print( segment[i] )


if __name__ == '__main__':
    main()

РЕДАКТИРОВАНИЕ: Вот фрагмент того, что я рассматривал с помощью виджета TextBox, но не сработало. Нижеприведенная строка заменяет строку name = input... выше (конечно, после импорта виджета TextBox).

name = []
def submit(expression):
    nonlocal name
    name = expression

axbox = fig.add_axes([0.1, 0.05, 0.8, 0.075])
text_box = TextBox(axbox, "Segment name: ", textalignment = "center")
text_box.on_submit(submit)
text_box.set_val("Unknown") 

@Vitalizzare Я попробовал это, но именно на это я имел в виду, когда сказал, что не могу воспроизвести такое же поведение «ввода» через TextBox. Например, я удалил функцию input и заменил ее на указанную выше; см. раздел кода выше, который я только что добавил внизу, чтобы проиллюстрировать изменения, которые я рассмотрел. (Я не знаю, как форматировать код в комментариях).

debronee101 19.06.2024 20:34
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Воспроизводя ваш код, я столкнулся с двумя основными проблемами:

  • set_val() на самом деле запускает on_submit(), отправляя непреднамеренный Unknown вместо ожидания ввода пользователя. То, что вы (вероятно) ищете, то есть значение по умолчанию для текста в вашем текстовом поле, может быть достигнуто с помощью аргумента initial при создании Textbox или путем включения и выключения флага eventson, чтобы использование set_val() не вызывало on_submit().
  • Я не могу видеть, что печатаю. Чтобы добиться этого, вам нужно вызывать перерисовку изображения (в идеале) при каждом нажатии клавиши или, как я сделал, в цикле, пока не сработает on_submit.

Найдите здесь фрагмент, реализующий то, что я описал:

name = []
done = False    # flag to stop updating the plot when the user hits Enter
def on_submit(expression):
       nonlocal name, done
       name = expression
       done = True

axbox = fig.add_axes([0.1, 0.05, 0.8, 0.075])
            
initial_string = 'Unknown' # the default string in the TextBox
text_box = TextBox(axbox, "Segment name: ", 
                   textalignment = "center",
                   initial=initial_string)
text_box.on_submit(on_submit)
# The plot has to be re-drawn at each keystroke for the typing process to be seen
while not done:
    plt.draw()
    plt.pause(0.01)
            
# Reset the box to the initial value without triggering `on_pick`        
text_box.eventson = False
text_box.set_val(initial_string)
text_box.eventson = True

Надеюсь, это вам поможет!

Огромное спасибо за развернутый ответ и время, потраченное на его публикацию! Я поигрался с несколькими изменениями в соответствии с вашими предложениями, но ваш ответ гораздо точнее, чем то, что я пытался.

debronee101 21.06.2024 08:30

Другие вопросы по теме