Âm mưu theo cách không chặn với Matplotlib


138

Tôi đã chơi với Numpy và matplotlib trong vài ngày qua. Tôi đang gặp vấn đề khi cố gắng biến matplotlib thành một hàm mà không chặn thực thi. Tôi biết đã có nhiều chủ đề ở đây trên SO hỏi những câu hỏi tương tự, và tôi đã googled khá nhiều nhưng không quản lý để làm cho công việc này.

Tôi đã thử sử dụng show (block = false) như một số người đề xuất, nhưng tất cả những gì tôi nhận được là một cửa sổ đóng băng. Nếu tôi chỉ đơn giản gọi show (), kết quả được vẽ đúng nhưng thực thi bị chặn cho đến khi cửa sổ được đóng lại. Từ các chủ đề khác mà tôi đã đọc, tôi nghi ngờ rằng liệu chương trình (khối = Sai) có hoạt động hay không phụ thuộc vào phụ trợ. Điều này có đúng không? Mặt sau của tôi là Qt4Agg. Bạn có thể xem mã của tôi và cho tôi biết nếu bạn thấy có gì đó không đúng? Đây là mã của tôi. Cảm ơn vì bất kì sự giúp đỡ.

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

Tái bút Tôi quên nói rằng tôi muốn cập nhật cửa sổ hiện có mỗi khi tôi vẽ một cái gì đó, thay vì tạo một cái mới.


1
Bạn đã thử chế độ tương tác matplotlib với plt.ion()trước đây plt.show()chưa? Sau đó, nó sẽ không bị chặn vì mỗi âm mưu được sinh ra thành một chuỗi con.
Anzel

@Anzel Tôi chỉ thử nó, nhưng dường như không có gì khác biệt.
opetroch

3
Làm thế nào bạn đang chạy tập lệnh của bạn? Nếu tôi chạy mã ví dụ của bạn từ dấu nhắc thiết bị đầu cuối / lệnh, nó có vẻ hoạt động tốt, nhưng tôi nghĩ rằng tôi đã gặp rắc rối trong quá khứ khi cố gắng làm những việc như thế này từ IPython QtConsole hoặc IDE.
Marius

1
@Marius Aha !! Bạn đúng rồi. Quả thực tôi đang chạy nó từ bảng điều khiển IDE (PyCharm) của tôi. Khi chạy nó từ dấu nhắc cmd, plt.show (block = false), hoạt động tốt! Tôi sẽ hỏi quá nhiều nếu tôi hỏi bạn nếu bạn đã tìm thấy bất kỳ ý tưởng / giải pháp cho điều đó? Cảm ơn rất nhiều!
opetroch

Tôi thực sự không biết xin lỗi. Tôi thực sự không hiểu chi tiết về cách matplotlib tương tác với bàn điều khiển, vì vậy tôi thường chỉ chuyển sang chạy từ dấu nhắc lệnh nếu tôi cần thực hiện công cụ này matplotlib.
Marius

Câu trả lời:


167

Tôi đã dành một thời gian dài để tìm giải pháp, và tìm thấy câu trả lời này .

Có vẻ như, để có được những gì bạn (và tôi) muốn, bạn cần sự kết hợp của plt.ion(), plt.show()(không phải với block=False) và quan trọng nhất là plt.pause(.001)(hoặc bất cứ lúc nào bạn muốn). Việc tạm dừng là cần thiết vì các sự kiện GUI xảy ra trong khi mã chính đang ngủ, bao gồm cả bản vẽ. Có thể điều này được thực hiện bằng cách lấy thời gian từ một sợi ngủ, vì vậy có thể các IDE gây rối với điều đó mà tôi không biết.

Đây là một triển khai phù hợp với tôi trên python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()

3
Câu trả lời của bạn đã giúp tôi rất nhiều trong việc giải quyết một vấn đề tương tự tôi đang gặp phải. Trước đây, tôi đã làm plt.drawtheo plt.show(block = False)nhưng sau đó nó ngừng hoạt động: Hình không phản hồi, đóng nó bị sập iPython. Giải pháp của tôi là loại bỏ mọi trường hợp plt.draw()và thay thế nó bằng plt.pause(0.001). Thay vì có nó theo sau plt.show(block = False)như plt.drawtrước đây, nó đã đi trước plt.ion()plt.show(). Bây giờ tôi có một MatplotlibDeprecationWarningnhưng nó cho phép tôi vẽ các số liệu của mình, vì vậy tôi hài lòng với giải pháp này.
blue_chip

3
Lưu ý rằng trong python 2.7, bạn có cần sử dụng raw_inputkhông input. Xem tại đây
Chris

Cách giải quyết thực sự hữu ích khi phương pháp "animate" phản ứng là không thể! Bất cứ ai biết làm thế nào để thoát khỏi cảnh báo khấu hao?
Frederic Fortier

Xin vui lòng ai đó có thể cho tôi biết lý do tại sao tôi nhận được một dấu nhắc lệnh freezen khi tôi cố gắng thêm plt.ion trước plt.show?
Gabriel Augusto

@GabrielAugusto Tôi không chắc điều gì có thể gây ra điều đó và tôi không chắc ý của bạn là gì. Tôi vừa thử nghiệm ví dụ này trong Python 3.6 và nó vẫn hoạt động. Nếu bạn đã sử dụng cùng một mẫu và nó bị đóng băng, có thể có lỗi gì đó với cài đặt của bạn. Bạn nên kiểm tra nếu âm mưu bình thường hoạt động đầu tiên. Nếu bạn đã thử một cái gì đó khác nhau, sẽ không có nhiều điều để làm về nó trong các bình luận. Trong cả hai trường hợp, bạn có thể xem xét hỏi một câu hỏi riêng biệt.
krs013

23

Một mẹo đơn giản phù hợp với tôi là:

  1. Sử dụng đối số block = false bên trong show: plt.show (block = false)
  2. Sử dụng một plt.show () khác ở cuối tập lệnh .py.

Ví dụ :

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Lưu ý : plt.show()là dòng cuối cùng của tập lệnh của tôi.


7
Điều này tạo ra (đối với tôi, trên Linux, Anaconda, Python 2.7, phụ trợ mặc định) một cửa sổ trống vẫn trống cho đến khi kết thúc thực thi, khi cuối cùng nó được điền vào. Không hữu ích cho việc cập nhật một âm mưu ở giữa thực thi. :-(
sh37211

@ sh37211 Không chắc mục tiêu của bạn là gì. Trong một số trường hợp bạn cố gắng vẽ một cái gì đó nhưng sau lệnh vẽ bạn có các lệnh khác, thì điều này rất hữu ích vì nó cho phép bạn vẽ và thực hiện các lệnh khác. Xem bài đăng này để biết thêm về điều này: stackoverflow.com/questions/458209/ trên . Nếu bạn muốn cập nhật một cốt truyện thì nó nên là một cách khác.
seralouk

17

Bạn có thể tránh chặn thực thi bằng cách viết cốt truyện vào một mảng, sau đó hiển thị mảng trong một luồng khác. Dưới đây là một ví dụ về việc tạo và hiển thị các ô đồng thời bằng cách sử dụng pf.screen từ pyform Formula 0.2.8 :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Kết quả:

Hoạt hình sin

Tuyên bố miễn trừ trách nhiệm: Tôi là người duy trì các hình thức.

Tham khảo: Matplotlib: lưu cốt truyện vào mảng numpy


9

Rất nhiều câu trả lời là siêu phồng và từ những gì tôi có thể tìm thấy, câu trả lời không quá khó hiểu.

Bạn có thể sử dụng plt.ion()nếu bạn muốn, nhưng tôi thấy sử dụng plt.draw()cũng hiệu quả

Đối với dự án cụ thể của tôi, tôi đang âm mưu hình ảnh, nhưng bạn có thể sử dụng plot()hoặc scatter()hoặc bất cứ điều gì thay vì figimage(), nó không quan trọng.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Hoặc là

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

Nếu bạn đang sử dụng một con số thực tế.
Tôi đã sử dụng các câu trả lời của @ krs013 và @Default Picture để tìm hiểu
điều này


3

Âm mưu sống

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

nếu lượng dữ liệu quá nhiều, bạn có thể hạ thấp tốc độ cập nhật bằng một bộ đếm đơn giản

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Giữ lô sau khi thoát khỏi chương trình

Đây là vấn đề thực sự của tôi mà không thể tìm thấy câu trả lời thỏa đáng, tôi muốn âm mưu không đóng sau khi kịch bản kết thúc (như MATLAB),

Nếu bạn nghĩ về nó, sau khi kịch bản kết thúc, chương trình bị chấm dứt và không có cách logic nào để giữ cốt truyện theo cách này, vì vậy có hai tùy chọn

  1. chặn tập lệnh thoát khỏi (đó là plt.show () và không phải những gì tôi muốn)
  2. chạy cốt truyện trên một luồng riêng biệt (quá phức tạp)

điều này là không thỏa đáng với tôi vì vậy tôi đã tìm thấy một giải pháp khác bên ngoài chiếc hộp

SaveToFile và View trong trình xem bên ngoài

Đối với điều này, việc lưu và xem phải nhanh và người xem không nên khóa tệp và nên tự động cập nhật nội dung

Chọn định dạng để lưu

định dạng dựa trên vector là cả nhỏ và nhanh

  • SVG là tốt nhưng không tìm thấy trình xem tốt cho nó ngoại trừ trình duyệt web mặc định cần làm mới thủ công
  • PDF có thể hỗ trợ các định dạng vector và có những trình xem nhẹ hỗ trợ cập nhật trực tiếp

Trình xem nhẹ nhanh với Cập nhật trực tiếp

Đối với PDF, có một số tùy chọn tốt

  • Trên Windows tôi sử dụng SumatraPDF miễn phí, nhanh và nhẹ (chỉ sử dụng 1,8 MB RAM cho trường hợp của tôi)

  • Trên Linux, có một số tùy chọn như Evince (Gnome) và Ocular (KDE)

Mã mẫu & kết quả

Mã mẫu để xuất âm mưu cho một tệp

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

Sau lần chạy đầu tiên, hãy mở tệp đầu ra ở một trong những người xem được đề cập ở trên và thưởng thức.

Dưới đây là ảnh chụp màn hình của VSCode cùng với SumatraPDF, quá trình này đủ nhanh để có tốc độ cập nhật bán trực tiếp (tôi có thể đạt gần 10Hz trong thiết lập của mình chỉ cần sử dụng time.sleep()giữa các khoảng thời gian) pyPlot, không chặn


2

Câu trả lời của Iggy là cách dễ nhất để tôi làm theo, nhưng tôi đã gặp phải lỗi sau khi thực hiện một subplotlệnh tiếp theo không có ở đó khi tôi chỉ đang làm show:

MatplotlibDeprecationWarning: Thêm một trục sử dụng các đối số tương tự như các trục trước hiện đang sử dụng lại phiên bản trước đó. Trong phiên bản tương lai, một phiên bản mới sẽ luôn được tạo và trả lại. Trong khi đó, cảnh báo này có thể được loại bỏ và hành vi trong tương lai được đảm bảo, bằng cách chuyển một nhãn duy nhất cho mỗi trường hợp trục.

Để tránh lỗi này, nó giúp đóng (hoặc xóa ) cốt truyện sau khi người dùng nhấn enter.

Đây là mã làm việc cho tôi:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()

0

Gói Python vẽ cho phép cập nhật một âm mưu trong thời gian thực theo cách không chặn.
Nó cũng hoạt động với webcam và OpenCV, ví dụ để vẽ các số đo cho từng khung.
Xem bài gốc .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.