У меня есть простая идея, которую я хотел бы реализовать. Из некоторых данных на одномерном графике извлеките разные сегменты, выбрав начальную и конечную точки. При этом для каждого выделенного сегмента укажите соответствующее (строковое) имя этой линии. Эти три значения (имя, начальный индекс, конечный индекс) затем сохраняются для дальнейшего использования.
Я включил минимальный рабочий пример. Это работает, но я предпочитаю, чтобы ввод «имени» выполнялся на фигуре, а не на терминале. Другими словами, используйте что-то вроде 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
и заменил ее на указанную выше; см. раздел кода выше, который я только что добавил внизу, чтобы проиллюстрировать изменения, которые я рассмотрел. (Я не знаю, как форматировать код в комментариях).
Воспроизводя ваш код, я столкнулся с двумя основными проблемами:
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
Надеюсь, это вам поможет!
Огромное спасибо за развернутый ответ и время, потраченное на его публикацию! Я поигрался с несколькими изменениями в соответствии с вашими предложениями, но ваш ответ гораздо точнее, чем то, что я пытался.
См. matplotlib.widgets.TextBox . Как вариант ищите другие примеры использования