使用用户输入

时间:2017-03-01 08:12:59

标签: python matplotlib tkinter

我再一次需要援助...... 我是python的新手,我试图在GUI中绘制Mandelbrot集。目前我正在开发一个函数,我可以在其中更改渲染分形的颜色。问题是我无法弄清楚如何用新的替换旧的绘图。一切都达到了需要重新绘制情节的位置(终端甚至暂停,好像它正在重新计算但不产生任何东西)。我试过在互联网建议的所有不同的地方插入fig.clf(),但我仍然无法弄明白。附件是将运行的代码的摘录。此代码的特定位置位于名为mandelbrot_image的函数和类MainPage中。提前谢谢。

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure

import tkinter as tk
from tkinter import ttk
from tkinter import *
#import tkinter.messagebox
from tkinter import messagebox
import numpy as np
from numba import jit


from matplotlib import colors



#maths and display code derived/inspired from Jean Francois Puget 
#https://www.ibm.com/developerworks/community/blogs/jfp/entry/My_Christmas_Gift?lang=en


@jit
def mandelbrot(z,maxiter,horizon,log_horizon):
    c = z
    for n in range(maxiter):
        az = abs(z)
        if az > horizon:
            return n - np.log(np.log(az))/np.log(2) + log_horizon
        z = z*z + c
    return 0

@jit
def mandelbrot_set(xmin,xmax,ymin,ymax,width,height,maxiter):
    horizon = 2.0 ** 40
    log_horizon = np.log(np.log(horizon))/np.log(2)
    r1 = np.linspace(xmin, xmax, width)
    r2 = np.linspace(ymin, ymax, height)
    n3 = np.empty((width,height))
    for i in range(width):
        for j in range(height):
            n3[i,j] = mandelbrot(r1[i] + 1j*r2[j],maxiter,horizon, log_horizon)
    return (r1,r2,n3)

def mandelbrot_image(xmin=-2.,xmax=0.5,ymin=-1.25,ymax=1.25,width=10,height=10,\
             maxiter=1000,cmap='hot',gamma=0.3): #the coords and cmap are essentially a filler for the imput in the plot() function at the bottom of the code


    dpi = 80
    img_width = dpi * width
    img_height = dpi * height
    x,y,z = mandelbrot_set(xmin,xmax,ymin,ymax,img_width,img_height,maxiter)

    fig = Figure(figsize=(width, height))

    ax = fig.add_subplot(111)

    ticks = np.arange(0,img_width,3*dpi)
    x_ticks = xmin + (xmax-xmin)*ticks/img_width
    ax.set_xticks(ticks); ax.set_xticklabels(x_ticks)
    y_ticks = ymin + (ymax-ymin)*ticks/img_width
    ax.set_yticks(ticks); ax.set_yticklabels(y_ticks)
    ax.set_title("The Mandelbrot set")
    norm = colors.PowerNorm(gamma)
    #fig.clf()
    ax.imshow(z.T,cmap=cmap,origin='lower',norm=norm)

    return fig

LARGE_FONT= ("Verdana", 12)
NORM_FONT= ("Verdana", 10)

class base(tk.Tk):

    def __init__(self, *args, **kwargs):

        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, "Mandelbrot Renderer")

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)


        self.frames = {}

        for F in (StartPage, MainPage):

            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)
        label = tk.Label(self, text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        button = ttk.Button(self, text="Lets Begin",
                        command=lambda: controller.show_frame(MainPage))
        button.pack()



class MainPage(tk.Frame):

    def var_states(self):  #this is supposed to send code to run plot() again but it doesnt do it
        print (self.combobox.get())
        print (self.colr)
        self.plot ()


    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        label = tk.Label(self, text="Graph Page!", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        values = ['jet', 'rainbow', 'ocean', 'hot', 'cubehelix','gnuplot','terrain','prism', 'pink']  
        button1 = ttk.Button(self, text="Back to Home",
                            command=lambda: controller.show_frame(StartPage))
        button1.pack()

        button2 = ttk.Button(self, text="Re-Render",
                            command=self.plot)
        button2.pack()
        self.mvar = IntVar()
        self.cbutton = ttk.Checkbutton(self, text="shadow",onvalue=0, offvalue=1, variable=self.mvar) 
        self.cbutton.pack()

        self.combobox = ttk.Combobox(self, values=values)
        self.combobox.current(0)
        self.combobox.pack(side = RIGHT)

        global colr 
        self.colr = self.combobox.get()
        self.plot ()

    def plot (self):
        colr = self.combobox.get()
        print (colr)
        #fig.clf() this does nothing and crashes
        fig = mandelbrot_image(-0.8,-0.7,0,0.1,cmap=colr) #this is calling the method with the coordinates of the plot and the color scheme
        #fig.clf()this works but just leaves the screen completely blank
        canvas = FigureCanvasTkAgg(fig, self)
        #fig.clf()this works but just leaves the screen completely blank
        canvas.show()
        #canvas.clf()
        #fig.clf() this works but just leaves the screen completely blank
        canvas.get_tk_widget().pack(side = BOTTOM, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2TkAgg(canvas, self)
        toolbar.update()
        canvas._tkcanvas.pack(side = BOTTOM, fill=tk.BOTH, expand=True)  

app = base()
app.geometry ("800x600")
app.mainloop()

2 个答案:

答案 0 :(得分:2)

如果要更新图形,则不应在多次调用的函数中创建它。相反,您可以在MainPage的init函数中创建数字,并仅更新其子图的内容。因此,绘图功能只能清除轴(不是图!)并调用要绘制的轴作为参数的mandelbrot_image函数。最后,必须使用canvas.draw()重新绘制画布,以便在GUI中显示新的绘图。

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure

import Tkinter as tk #replace with tkinter for python 3
import ttk
import numpy as np
from numba import jit
from matplotlib import colors
#maths and display code derived/inspired from Jean Francois Puget 
#https://www.ibm.com/developerworks/community/blogs/jfp/entry/My_Christmas_Gift?lang=en


@jit
def mandelbrot(z,maxiter,horizon,log_horizon):
    c = z
    for n in range(maxiter):
        az = abs(z)
        if az > horizon:
            return n - np.log(np.log(az))/np.log(2) + log_horizon
        z = z*z + c
    return 0

@jit
def mandelbrot_set(xmin,xmax,ymin,ymax,width,height,maxiter):
    horizon = 2.0 ** 40
    log_horizon = np.log(np.log(horizon))/np.log(2)
    r1 = np.linspace(xmin, xmax, width)
    r2 = np.linspace(ymin, ymax, height)
    n3 = np.empty((width,height))
    for i in range(width):
        for j in range(height):
            n3[i,j] = mandelbrot(r1[i] + 1j*r2[j],maxiter,horizon, log_horizon)
    return (r1,r2,n3)

def mandelbrot_image(ax, xmin=-2.,xmax=0.5,ymin=-1.25,ymax=1.25,width=10,height=10,\
             maxiter=1000,cmap='hot',gamma=0.3): #the coords and cmap are essentially a filler for the imput in the plot() function at the bottom of the code


    dpi = 80
    img_width = dpi * width
    img_height = dpi * height
    x,y,z = mandelbrot_set(xmin,xmax,ymin,ymax,img_width,img_height,maxiter)

    ticks = np.arange(0,img_width,3*dpi)
    x_ticks = xmin + (xmax-xmin)*ticks/img_width
    ax.set_xticks(ticks); ax.set_xticklabels(x_ticks)
    y_ticks = ymin + (ymax-ymin)*ticks/img_width
    ax.set_yticks(ticks); ax.set_yticklabels(y_ticks)
    ax.set_title("The Mandelbrot set")
    norm = colors.PowerNorm(gamma)
    ax.imshow(z.T,cmap=cmap,origin='lower',norm=norm)


LARGE_FONT= ("Verdana", 12)
NORM_FONT= ("Verdana", 10)

class base(tk.Tk):

    def __init__(self, *args, **kwargs):

        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, "Mandelbrot Renderer")

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)


        self.frames = {}

        for F in (StartPage, MainPage):

            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)
        label = tk.Label(self, text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        button = tk.Button(self, text="Lets Begin",
                        command=lambda: controller.show_frame(MainPage))
        button.pack()



class MainPage(tk.Frame):

    def var_states(self):  #this is supposed to send code to run plot() again but it doesnt do it
        print (self.combobox.get())
        print (self.colr)
        self.plot ()


    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        label = tk.Label(self, text="Graph Page!", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        values = ['jet', 'rainbow', 'ocean', 'hot', 'cubehelix','gnuplot','terrain','prism', 'pink']  
        button1 = tk.Button(self, text="Back to Home",
                            command=lambda: controller.show_frame(StartPage))
        button1.pack()

        button2 = tk.Button(self, text="Re-Render",
                            command=self.plot)
        button2.pack()
        self.mvar = tk.IntVar()
        self.cbutton = tk.Checkbutton(self, text="shadow",onvalue=0, offvalue=1, variable=self.mvar) 
        self.cbutton.pack()

        self.combobox = ttk.Combobox(self, values=values)
        self.combobox.current(0)
        self.combobox.pack(side = tk.TOP)

        self.width, self.height = 10, 10
        fig = Figure(figsize=(self.width, self.height))
        self.ax = fig.add_subplot(111)

        self.canvas = FigureCanvasTkAgg(fig, self)
        self.canvas.show()
        toolbar = NavigationToolbar2TkAgg(self.canvas, self)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side = tk.BOTTOM, fill=tk.BOTH, expand=True)

        self.plot ()

    def plot (self):
        colr = self.combobox.get()
        print (colr)
        self.ax.clear()
        mandelbrot_image(self.ax, -0.8,-0.7,0,0.1,cmap=colr)
        self.canvas.draw()


app = base()
app.geometry ("800x600")
app.mainloop()

注意:在较新版本的matplotlib中,您应使用NavigationToolbar2Tk代替NavigationToolbar2TkAgg

答案 1 :(得分:0)

当用户在GUI窗口中更改输入值时,我一直在寻找一种解决方案来更新matplotlib图。阅读了许多示例之后,我发现ImportanceOfBeingErnest的想法非常有用。他们的回答也解决了我的问题。非常感谢。