Я хотел построить график некоторых данных, которые я получил с помощью простого приложения для опроса. Раньше я возился с pyplot, но не пробовал создавать что-либо с нуля. К счастью, он очень популярен, и на StackOverflow и других сайтах можно найти массу примеров.
Я выполнил поиск и начал с этого SO-ответа, связанного с обновлением графика с течением времени.
import matplotlib.pyplot as plt import numpy as np # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() x = np.linspace(0, 6*np.pi, 100) y = np.sin(x) fig = plt.figure() ax = fig.add_subplot(111) line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma for phase in np.linspace(0, 10*np.pi, 500): line1.set_ydata(np.sin(x phase)) fig.canvas.draw() fig.canvas.flush_events()
Этот код анимирует изменение фазы синусоидальной волны.
Первые две строки импортируют библиотеки, которые я хочу использовать: matplotlib.pyplot выполняет построение графиков и обработку графического интерфейса.
Метод ion(), насколько я понимаю (хотя может и нет), заставляет pyplot управлять графическим интерфейсом. Вы также можете использовать его внутри программы tkinter или использовать для создания статических изображений, но в нашем случае имеет смысл позволить ему обрабатывать графический интерфейс графика за нас. (Это то, что позже делает вызовlush_events(): разрешает интерактивность с окном рисунка.)
В этом примере используется метод numpy linspace() для создания значений x. Он возвращает массив numpy, который представляет собой необычный список Python.
Причина использования np.sin вместо math.sin — широковещательная рассылка. Это числовой термин для применения функции к каждому элементу списка. На самом деле мне пришло в голову, что того же самого можно было бы достичь и без numpy, используя карту:
map(lambda n: math.sin(n), x)
Но numpy-трансляция удобна и проста в использовании.
Теперь наступает настройка pyplot. Сначала создадим новую «фигуру» (рис). Добавьте к этому рисунку подграфик (топор) — их может быть много. 111 имеет довольно эзотерическую интерпретацию: «создайте сетку 1x1 и поместите этот подграфик в первую ячейку».
В этот подграфик (или набор осей) строится линия с использованием переданных значений x и y. (Точки соединяются прямыми линиями и отображаются непрерывно.) «r-» — это сокращенный способ обозначения сплошной красной линии. Мы могли бы указать несколько строк, поэтому методplot() возвращает кортеж; приведенный выше код использует распаковку кортежа для извлечения нужного нам значения.
Это хорошее начало, но мне нужно со временем расширить ось X. Этот код также не обновляет границы оси Y при необходимости — он привязан к любым границам, которые он рассчитывает для первого графика. Еще немного поисков привели меня к этому ТАК-ответу. Цитирую их:
Вам потребуется обновить dataLim осей, а затем обновить viewLim осей на основе dataLim. Соответствующими методами являются axes.relim() и ax.autoscale_view().
Конечно, звучит хорошо. На основе их примера я создал демонстрационный график, который растет как по x, так и по y.
import matplotlib.pyplot as plt import numpy as np from threading import Thread from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Returns a tuple of line objects growth = 0 while True: x.append(x_next_max / 10) x_next_max = 1 line1.set_xdata(x) line1.set_ydata(np.sin(x) np.sin(np.divide(x, 100)) np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
Теперь я чего-то добиваюсь. Но это блокирующий цикл, и мне нужно, чтобы мои данные время от времени обновлялись. Если бы у меня было несколько потоков, мне пришлось бы беспокоиться о потокобезопасности при обновлении переменных. В этом случае я могу лениться, потому что знаю, что переменная обновляется только раз в 5 минут (или как часто запускается функция опроса); нет опасности перезаписи переменной в середине строки кода.
import matplotlib.pyplot as plt import numpy as np from threading import Timer from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Plot returns a tuple of line objects growth = 0 new_x = None dT = 1 def grow(): global new_x, x_next_max while True: new_x = x [x_next_max / 10] x_next_max = 1 sleep(dT) # grow every dT seconds t = Thread(target=grow) t.start() while True: if new_x: x = new_x new_x = None line1.set_xdata(x) line1.set_ydata(np.sin(x) np.sin(np.divide(x, 100)) np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
График обновляется только тогда, когда поток роста присваивает значение new_x. Обратите внимание, что вызовlush_events() находится за пределами оператора if, поэтому он вызывается часто.
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3