wxpython面板上的GUI图刷新不正确

时间:2018-07-23 14:56:22

标签: user-interface plot wxpython refresh

好,所以我的工作是创建一个非常简单的GUI(我是实习生)。任务是最终连接到机器并处理真实数据,但是现在我正在处理带有噪声的随机生成的正弦数据。我选择在Python 3.0中工作,并使用wxpython创建我的GUI组件。

我希望所有内容都显示在同一窗口中,所以我使用的是面板(因此是wx.lib.plot.PlotCanvas而不是matplotlib.pyplot之类的东西)

我的问题是随着时间的流逝,情节似乎在面板之外“扩展”。当我手动调整窗口大小时,这是暂时解决的,但是此后立即再次恢复(您需要运行代码以了解我的意思)。

Expansion over time in panel

另一个问题(自从我开始编写代码以来,一直困扰着我)是有时当我手动调整窗口大小或最小化然后再次最大化时,计时器会随机启动和停止。

我已经尝试了各种方法(更改大小调整器的填充,额外的参数,更改两次刷新之间的时间GetBestSize()),但我认为我只是不太了解wxpython来确定问题出在哪里 我真的很感激您可以就这些问题中的任何一个提供帮助(我不知道,它们甚至可能彼此关联)。

仅供参考:我不是一位经验丰富的编码员,我的代码还没有完成(我有更多的函数可以编码,但是我觉得我应该先解决这个问题)。我通过查看来自各种教程和网站(例如stackoverflow)的不同技术来构造此代码,因此我知道它的格式不正确,肯定可以提高效率。另外,为了安全起见,我删除了一些部分-没什么重要,只是消息中的字符串。

PS:如果您确实有一种更简单的方法来完成不存在此问题的整个绘图/更新操作(最好仍然在wx中),我也很高兴听到这一消息

这是我的代码:

编辑:通过使用self.p2.SetSize((W + 0,L + 0))而不是(self.p2.GetBestSize())解决了扩展问题

编辑:仅通过重新生成数据并在evt_timer函数中的现有画布上重绘数据,即可使转换更加平滑(而不是重新创建整个画布,如果您知道我的意思,它会呈现闪烁的外观)

import wx
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
import wx.lib.plot as plot
import time
import os

wildcard =  "Text File (*.txt)|*.txt|"\
            "Picture (*.png)|*.png|"\
            "All files (*.*)|*.*"#This wildcard shows the options for file endings in the "SAVE" tab - see OnSave(self,event)

wildcard2 = "Picture (*.png)|*.png|"\
            "Text File (*.txt)|*.txt|"\
            "All files (*.*)|*.*"

class PlotCanvas(plot.PlotCanvas):
    def __init__(self,parent,id,size,accepted):
        """
        This randomly generates sine data (with noise) and plots it to a panel.
        Incorporated as a separate class instead of instatiating it as a plot.PlotCanvas object 
        to overcome an issue of the size of the plot in the panel.
        """
        plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_SUNKEN,size = size)
        N = 100 # number of data points
        self.t = np.linspace(0, 4*np.pi, N)
        f = 1.15247 # Optional!! Advised not to use
        self.data = 3.0*np.sin(f*self.t+0.001) + 0.5 + np.random.randn(N) # create artificial data with noise        
        guess_mean = np.mean(self.data)
        guess_phase = 0
        guess_freq = 1
        guess_amp = 1        
        optimize_func = lambda x: x[0]*np.sin(x[1]*self.t+x[2]) + x[3] - self.data
        est_amp, est_freq, est_phase, est_mean = leastsq(optimize_func, [guess_amp, guess_freq, guess_phase, guess_mean])[0]  
        fine_t = np.arange(0,max(self.t),0.1)
        data_fit=est_amp*np.sin(est_freq*fine_t+est_phase)+est_mean

        multiplier = 1


        dataset1 = [(x,[d for d in self.data][[td for td in self.t].index(x)])for x in [td for td in self.t]]
        fitdata1 = [(x,[df for df in data_fit][[tf for tf in fine_t].index(x)]) for x in [tf for tf in fine_t]]
        dataset =[(x,y*multiplier) for (x,y) in dataset1]
        fitdata = [(x,y*multiplier) for (x,y) in fitdata1]
        self.data = dataset
        self.data2 = fitdata
        line = plot.PolyLine(self.data,legend = 'random',colour = 'light blue', width =2)
        line2 = plot.PolyLine(self.data2,legend = 'sineline',colour ='black',width =2)
        a = []
        if "D" in accepted:
            a.append(line)
        if "S" in accepted:
            a.append(line2)
        if "G" in accepted:
            pass
        if "L" in accepted:
            pass
        gc = plot.PlotGraphics(a,'Line Graph','X','Y')
        xmin = self.t[0]-0.01*(self.t[-1]-self.t[0])
        xmax = self.t[-1]+0.01*(self.t[-1]-self.t[0])
        self.Draw(gc,xAxis=(xmin,xmax),yAxis=(min([x[1] for x in dataset])-0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset])),
                            max([x[1] for x in dataset])+0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset]))))
        #self.showLegend = True
        #self.enableZoom = True
    def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
        if c == "W":
            caption = "Warning!"
            dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
        elif c == "I":
            caption = "Information"
            dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()#Destroys dialog on close

class Frame(wx.Frame):
    """
    This is the main class. In it, we declare the separate panels, canvas, menubar, buttons and sizers.

    """
    def __init__(self,parent,id,title):

        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
        self.CurrentDirectory = os.getcwd()
        self.timer=wx.Timer(self)#Instantiating the timer
        self.count=0
        self.Bind(wx.EVT_TIMER,self.evt_timer)#Binding it to itself so that it is always triggered
        self.Bind(wx.EVT_PAINT,self.paint)

        menubar = wx.MenuBar()

        fileMenu = wx.Menu() #Creating the Menubar at the top

        #Creating 3 menus: fileMenu,fit,and help

        save = wx.Menu()
        z = wx.MenuItem(save,wx.ID_ANY,'Save Raw Data\tCtrl+D')
        self.Bind(wx.EVT_MENU,self.OnSave,z)
        save.Append(z)

        z= wx.MenuItem(save,wx.ID_ANY,'Save Image\tCtrl+I')
        self.Bind(wx.EVT_MENU,self.OnSaveImage,z)
        save.Append(z)

        fileMenu.AppendSubMenu(save,'&Save')

        fileMenu.AppendSeparator()

        z = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W')
        self.Bind(wx.EVT_MENU, self.OnQuit, z)
        fileMenu.Append(z)

        fit = wx.Menu()#Making a check menu
        self.gaussian = fit.Append(wx.ID_ANY,'Gaussian',kind = wx.ITEM_CHECK)
        #self.Bind(wx.EVT_MENU,self.ToggleGaussian,self.gaussian)
        fit.Check(self.gaussian.GetId(),False)

        self.sine = fit.Append(wx.ID_ANY,'Sine',kind = wx.ITEM_CHECK)
        #self.Bind(wx.EVT_MENU,self.ToggleSine,self.sine)
        fit.Check(self.sine.GetId(),False)

        self.linear = fit.Append(wx.ID_ANY,'Linear',kind=wx.ITEM_CHECK)
        #self.Bind(wx.EVT_MENU,self.ToggleLinear,self.linear)
        fit.Check(self.linear.GetId(),False)




        help = wx.Menu()
        z = wx.MenuItem(help,wx.ID_ANY,'&About\tCtrl+H')
        self.Bind(wx.EVT_MENU,self.OnHelp,z)
        help.Append(z)

        menubar.Append(fileMenu, '&File')
        menubar.Append(fit, '&Fit')
        menubar.Append(help, '&Help')#adding menus to menubar 

        self.SetMenuBar(menubar)#formatting the frame with menubar

        self.sp = wx.SplitterWindow(self)#Splitting the window into 2 panels
        self.p1 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For buttons and user events
        self.p2 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For display of the plot
        self.sp.SplitVertically(self.p1,self.p2,300)

        sizer = wx.GridBagSizer(3, 3)#Versatile sizer for layout of first panel self.p1
        bitmappath = self.CurrentDirectory + "\\BITMAPS"

        bmp = wx.Bitmap(bitmappath+"\\SAVE.BMP",wx.BITMAP_TYPE_BMP)
        self.saveBtn = wx.BitmapButton(self.p1,wx.ID_ANY,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))
        self.Bind(wx.EVT_BUTTON,self.OnSave,self.saveBtn)
        sizer.Add(self.saveBtn, (0, 0), wx.DefaultSpan, wx.ALL,5)

        bmp = wx.Bitmap(bitmappath +"\\START.BMP",wx.BITMAP_TYPE_BMP)
        self.startBtn = wx.BitmapButton(self.p1,-1,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))# A button that starts and stops the plotting
        self.startBtn.startval = "START"
        self.Bind(wx.EVT_BUTTON,self.paint,self.startBtn)
        sizer.Add(self.startBtn, (0, 1), wx.DefaultSpan,wx.ALL,5)





        sizer1 = wx.BoxSizer(wx.VERTICAL)
        W,L = self.p2.GetSize()
        self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),["D"])

        sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
        self.p2.SetSizerAndFit(sizer1)




        self.p1.SetSizerAndFit(sizer)
        self.p2.SetSizerAndFit(sizer1)
        self.p2.SetSize(W,L)
        self.Maximize(True)
        self.Centre()
        self.Show()
        ############### event  methods ###########
    def paint(self,event):
        """
        Updates the canvas based on the value of the startbtn(not the image). Bound to self.timer.
        """
        bitmappath = self.CurrentDirectory + "\\BITMAPS"
        if self.startBtn.startval == "START":
            self.timer.Start(1)# increase the value for more time
            bmp = wx.Bitmap(bitmappath + "\\STOP.BMP",wx.BITMAP_TYPE_BMP)
            self.startBtn.SetBitmap(bmp)
            self.startBtn.startval = "STOP"
        elif self.startBtn.startval == "STOP":
            self.timer.Stop()
            bmp = wx.Bitmap(bitmappath+ "\\START.BMP",wx.BITMAP_TYPE_BMP)
            self.startBtn.SetBitmap(bmp)
            self.startBtn.startval = "START"
    def evt_timer(self,event):
        self.count +=1
        if self.count== 10:# By increasing count (or the number in self.timer.Start()) you can increase the interval between updates
            #self.p2.canvas.Clear()
            sizer1 = wx.BoxSizer(wx.VERTICAL)
            W,L = self.p2.GetSize()
            a = ["D"]
            if self.sine.IsChecked():
                a.append("S")
            elif self.linear.IsChecked():
                a.append("L")
            elif self.gaussian.IsChecked():
                a.append("G")
            self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),a)
            sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
            self.p2.SetSizerAndFit(sizer1)
            self.p2.SetSize(self.p2.GetBestSize())
            self.count=0 # reset the count
    def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
        if c == "W":
            caption = "Warning!"
            dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
        elif c == "I":
            caption = "Information"
            dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()#Destroys dialog on close
    def OnSave(self,event):#Triggered by menubar and button
        try:
            rawdata = self.p2.canvas.data
            raw_X =[x[0] for x in rawdata]
            raw_Y =[x[1] for x in rawdata]

            dlg = wx.FileDialog(#Code for this from http://www.blog.pythonlibrary.org
                self, message="Save file as ...", 
                defaultDir=self.CurrentDirectory, 
                defaultFile=str(time.ctime()), wildcard=wildcard, style=wx.FD_SAVE
                )
            if dlg.ShowModal() == wx.ID_OK:
                path = dlg.GetPath()
            dlg.Destroy()

            f = open(path+".txt","w+")

            for i in range(len(raw_X)):
                f.write(str(raw_X[i])+"\t"+str(raw_Y[i])+"\n")
            f.close()
            self.Dialog(None,"File successfully saved","I")
        except UnboundLocalError:#Catch error when user closes save window without selecting any directory or filename
            pass
    def OnSaveImage(self,event):
        try:
            rawdata = self.p2.canvas.data
            raw_X = [x[0] for x in rawdata]
            raw_Y = [x[1] for x in rawdata]
            dlg = wx.FileDialog(
                self, message="Save file as ...", 
                defaultDir=self.CurrentDirectory, 
                defaultFile=str(time.ctime()), wildcard=wildcard2, style=wx.FD_SAVE
                )
            if dlg.ShowModal() == wx.ID_OK:
                path = dlg.GetPath()
            dlg.Destroy()
            fig1 = plt.figure()

            plt.plot(raw_X,raw_Y)
            plt.title("Raw Data")
            fig1.savefig(path+".png")
            self.Dialog(None,"File successfully saved","I")
        except UnboundLocalError:
            pass
    def OnMultiply(self,e):
        try:
            factor = self.x.GetValue()
            factor = float(factor)
            self.IntegrationTime = factor

        except ValueError as e:
            self.Dialog(None,str(e),"W")

    def OnQuit(self, e):
        self.Close()
    def OnHelp(self,e):
        self.Dialog(None,"N/A","I")
    def ToggleSine(self,e):
        pass
    def ToggleLinear(self,e):
        self.Dialog(None,"Not added yet","W")
    def ToggleGaussian(self,e):
        self.Dialog(None,"Not added yet","W")


if __name__ =="__main__":
    app=wx.App()
    Frame(None,-1,"N/A")
    app.MainLoop()

0 个答案:

没有答案