2012-03-24 38 views
1

私は(作業中の)アプリケーションをttkで完成させました。それは、comport関連のコントロールを表示するための自己作成モジュールと、それにいくつかのグラフを描画するキャンバスを使用します。オブジェクトのインスタンスを作成すると、シリアル入力を処理し、これをリスト(グラフごとに1つのリスト)に追加するスレッドが開始されます。私は3-6のグラフを持っていると、アプリケーションが著しく遅くなります。また、いくつかのバグがありますが、私が一般的なコンセプトで終わったら、私はそれらを解決します。あなたは私を助けて助けてPython GUI(tkinter.ttk)アプリケーションが遅い

もの:

  • コンポートは、グラフの LabelFrameとSerial.Serial
  • から派生
  • 座標がリストの辞書に格納されている自己書かれたオブジェクトのインスタンスであります: self.graphs = {} self.graphs ['name1'] = []保存された座標数は でキャンバスの幅になりますので、グラフあたり約1000-2000です。 6つの グラフを持っている - 私はリストと アペンド(から(0)ポップ到着座標ごとに新しい6
  • を掛けてください)到着座標のそれぞれの新しいセットの新しい私は忘れてしまった
  • 座標、私はまた、店舗のタイミング私はリストを処理するためにpreiodicコール機能を使用する別のリスト
  • で :self.after私は削除(100、 FUNC = self.periodicCall)このように100ms毎に(ALL)キャンバス からと私はすべてのグラフを描きますラインのセット。私は1000のcoordsの 6 grapsで持っているのであれば、私は、だから私はアイデアが明らかであると思い、このような少数の支配者

として6000本の小さなライン

  • プラスコースのいくつかのサービス情報を描きます。私はより良いアプローチが何であるかを理解したい。私はちょうどパイソンとプログラミングで始まったばかりです。だから私はあなたの目に痛みがあるため、あなたが投稿しようとしているコードについてあなたの言い訳をお願いしています。私はプログラミングスタイルがなく、私はそれを修正したい。少なくとも少し。したがって、コード内の何かについての他のコメントは歓迎します。

    #------------------------------------------------------------------------------- 
    # Name:  dataVisualizer 
    # Purpose: 
    # 
    # Author:  dccharacter 
    # 
    # Created:  23.03.2012 
    # Copyright: (c) dccharacter 2012 
    # Licence:  <your licence> 
    #------------------------------------------------------------------------------- 
    #!/usr/bin/env python 
    
    from tkinter import * 
    from tkinter.ttk import * 
    from robowidgets.serialPortGui import * 
    import threading 
    import re 
    import atexit 
    import random 
    from datetime import datetime 
    import time 
    
    class dataVisualizer(LabelFrame): 
        def __init__(self, master, comport , cnf={}, **kw): 
         self.master = master 
         self.comport = comport 
         LabelFrame.__init__(self, *cnf, **kw) 
    
         self.messageVar = StringVar() 
         Label(self, text="Message format regexp:").pack() 
         self.messagePattern = Entry(self, width = 20, text = 234234, textvariable = self.messageVar); 
         self.messageVar.set(r'(-*\d+),(-*\d+),(-*\d+),(-*\d+),(-*\d+),(-*\d+)') 
         self.messagePattern.pack() 
         Button(self, text = "Pause", command = self.pause).pack() 
         self.pauseFlag = TRUE 
    
         self.canvWidth, self.canvHeight = 1000, 700 
         self.density = 1 ##width of pixel - the bigger, the wider graph 
         self.numOfDots = self.canvWidth//self.density 
         self.graphs = {} 
         self.graphs['name1']=[] 
         self.graphs['name2']=[] 
         self.graphs['name3']=[] 
         self.graphs['name4']=[] 
         self.graphs['name5']=[] 
         self.graphs['name6']=[] 
         self.timings = [] 
         self.zeroTiming = datetime.now() 
         self.colors = ['red', 'blue', 'green', 'orange', 'violet', 'black', 'cyan'] 
    
         self.canv = Canvas(self, width = self.canvWidth, height = self.canvHeight) 
         self.canv.pack() 
    
         self.thread = threading.Thread(target = self.workerThread) 
         self.thread.start() 
    
         self.serialData = [] 
    
         self.periodicCall() 
    
        def pause(self): 
         self.pauseFlag = ~self.pauseFlag 
    
        def redraw(self): 
         self.canv.delete(ALL) 
    
         colorIndex = 0 
         for graphName in self.graphs: 
          runningAverage = sum(self.graphs[graphName][-10:])//10 
          text = str(runningAverage) 
          self.canv.create_text(self.canvWidth-60, 20*(colorIndex+1), text = text, 
           fill = self.colors[colorIndex], anchor = W) 
          prev_xxx, prev_yyy = 0, 0 
          for yyy in self.graphs[graphName]: 
           self.canv.create_line(prev_xxx, prev_yyy, prev_xxx+self.density, self.canvHeight//2 - yyy, 
            width = 1.4, fill = self.colors[colorIndex]) 
           prev_xxx, prev_yyy = prev_xxx+self.density, self.canvHeight//2 - yyy 
          colorIndex = colorIndex + 1 
         self.drawMesh() 
    
        def drawMesh(self): 
         self.canv.create_rectangle(3, 3, self.canvWidth, 
          self.canvHeight, outline = 'black', width = 2) 
         self.canv.create_line(0, self.canvHeight/2, self.canvWidth, 
          self.canvHeight/2, fill="black", width = 1) 
    
         mouseX = self.canv.winfo_pointerx() - self.canv.winfo_rootx() 
         mouseY = self.canv.winfo_pointery() - self.canv.winfo_rooty() 
    
         if mouseY < 60: aaa = -1 
         else: aaa = 1 
         if mouseX > self.canvWidth - 200 : bbb = -12 
         else: bbb = 1 
         try: 
          self.canv.create_rectangle(mouseX + 10*bbb - 5, mouseY - 20*aaa +10, 
           mouseX + 10*bbb + 115, mouseY - 20*aaa - 30, outline = "black", 
           fill = "red") 
          self.canv.create_text(mouseX + 10*bbb, mouseY - 40*aaa, 
           text = "t="+str(self.timings[mouseX//self.density]), 
           anchor = W) 
          self.canv.create_text(mouseX + 10*bbb, mouseY - 20*aaa, 
           text = "value="+str(self.canvHeight//2 - mouseY), 
           anchor = W) 
         except IndexError: 
          pass 
         self.canv.create_line(mouseX, 0, mouseX, 
          self.canvHeight, fill="blue", dash = [4, 1, 2, 1], width = 1) 
         self.canv.create_line(0, mouseY, self.canvWidth, 
          mouseY, fill="blue", dash = [4, 1, 2, 1], width = 1) 
    
    
        def periodicCall(self): 
         self.redraw() 
         self.after(100, func=self.periodicCall) 
    
        def workerThread(self): 
    
         while (1): 
          try: 
           if self.comport.isOpen() and (self.pauseFlag == TRUE): 
            comLine = self.comport.readline() 
            if len(self.timings) == self.numOfDots: 
             self.timings.pop(0) 
            td = datetime.now() - self.zeroTiming 
    
            ## b'271;-3:-50\r\n' 
            parsedLine = re.search(self.messagePattern.get(), str(comLine)) 
            index = 1 
            if parsedLine: 
             self.timings.append(td) 
             for graphName in self.graphs: 
              if len(self.graphs[graphName]) == self.numOfDots: 
               self.graphs[graphName].pop(0) 
              try: 
               self.graphs[graphName].append(int(parsedLine.group(index))) 
              except IndexError: 
               self.graphs[graphName].append(0) 
              index = index + 1 
           else: 
            self.comport.flush(); 
            time.sleep(1) 
          except TclError: 
           self.thread._stop() 
    
    def main(): 
        root = Tk() 
        mainWindow = Frame(root) 
        mainWindow.pack() 
        port = comPortWidget(mainWindow) 
        port.pack() 
        dv = dataVisualizer(mainWindow, port) 
        dv.pack() 
        root.mainloop() 
    
    if __name__ == '__main__': 
        main() 
    

    とシリアルパート - (...私はポートが二かそこらevey再列挙するために使用される場合、遅れるために使用される)にも遅れることがあり

    #------------------------------------------------------------------------------- 
    # Name:  robowidgets 
    # Purpose: 
    # 
    # Author:  dccharacter 
    # 
    # Created:  10.03.2012 
    # Copyright: (c) dccharacter 2012 
    # Licence:  <your licence> 
    #------------------------------------------------------------------------------- 
    #!/usr/bin/env python 
    
    import serial 
    from serial.tools.list_ports_windows import comports 
    from tkinter import * 
    from tkinter.ttk import * 
    
    class comPortWidget(LabelFrame, serial.Serial): 
    
        commonComPortSpeeds = ["1200", "2400", "4800", "9600", "14400", "19200", "38400", "57600", "115200"] 
    
        def __init__(self, master=None, cnf={}, **kw): 
         """Construct a comPortWidget widget with the parent MASTER. 
    
         STANDARD OPTIONS 
    
          borderwidth, cursor, font, foreground, 
          highlightbackground, highlightcolor, 
          highlightthickness, padx, pady, relief, 
          takefocus, text, background, class, colormap, container, 
          height, labelanchor, labelwidget, 
          visual, width 
    
         WIDGET-SPECIFIC OPTIONS 
    
    
         """ 
         self.master = master 
         LabelFrame.__init__(self, master, text="Serial settings", *cnf, **kw) 
         serial.Serial.__init__(self) 
         self.parent = master 
         self.draw() 
    
        def draw(self): 
         self.strVarComPort = StringVar() 
         self.comboComport = Combobox(self, 
          textvariable=self.strVarComPort) 
    
         self.comboComport.grid(row=0, column=1) 
         self.labelComportName = Label(self, text="Com port:") 
         self.labelComportName.grid(row=0, column=0) 
    
         self.strVarComSpeed = StringVar() 
         self.comboComSpeed = Combobox(self, 
          textvariable=self.strVarComSpeed, values=self.commonComPortSpeeds) 
         self.comboComSpeed.current(len(self.commonComPortSpeeds)-1) 
         self.comboComSpeed.grid(row=1, column=1) 
         self.labelComSpeed = Label(self, text="Com speed:") 
         self.labelComSpeed.grid(row=1, column=0) 
    
         self.buttonComOpen = Button(self, text="Open port", command=self.openPort) 
         self.buttonComOpen.grid(row=0, column=2) 
         self.buttonComClose = Button(self, text="Close port", command=self.closePort) 
         self.buttonComClose.grid(row=1, column=2) 
         self.buttonRefreshPorts = Button(self, text="Re", width=3, command=self.refreshComPortsCombo) 
         ##self.buttonRefreshPorts.grid(row=0, column=2) 
    
         self.refreshComPortsCombo() 
    
        def refreshComPortsCombo(self): 
         listComs = self.enumerateComPorts() 
         if not listComs: 
          listComs.append("No com ports found") 
          self.disableControls(~self.isOpen()) 
          self.buttonComClose.configure(state=DISABLED) 
         else: 
          self.disableControls(self.isOpen()) 
         self.buttonRefreshPorts.configure(state=NORMAL) 
         self.comboComport.config(values=listComs) 
         self.comboComport.current(len(listComs)-1) 
         ##self.after(500, func=self.refreshComPortsCombo) 
    
        def enumerateComPorts(self): 
         """ 
         Returns the list ofcom port names in the system or an empty list if 
         no ports found 
         """ 
         listComs = [] 
         for port, desc, hwid in sorted(comports()): 
          listComs.append(port) 
         return listComs 
    
        def openPort(self): 
         if self.isOpen(): 
          return 
         self.port = self.comboComport.get() 
         self.baudrate = int(self.comboComSpeed.get()) 
         self.timeout = 1 
         try: 
          self.open() 
          self.disableControls(self.isOpen()) 
         except IOError: 
          pass 
    
        def closePort(self): 
         if self.isOpen(): 
          self.flush() 
          self.close() 
          self.disableControls(self.isOpen()) 
    
        def disableControls(self, isConnected): 
         if isConnected: 
          self.labelComportName.configure(state=DISABLED) 
          self.labelComSpeed.configure(state=DISABLED) 
          self.comboComport.configure(state=DISABLED) 
          self.comboComSpeed.configure(state=DISABLED) 
          self.buttonComClose.configure(state=NORMAL) 
          self.buttonComOpen.configure(state=DISABLED) 
          self.buttonRefreshPorts.configure(state=DISABLED) 
         else: 
          self.labelComportName.configure(state=NORMAL) 
          self.labelComSpeed.configure(state=NORMAL) 
          self.comboComport.configure(state=NORMAL) 
          self.comboComSpeed.configure(state=NORMAL) 
          self.buttonComClose.configure(state=DISABLED) 
          self.buttonComOpen.configure(state=NORMAL) 
          self.buttonRefreshPorts.configure(state=NORMAL) 
    
    def main(): 
        pass 
    
    if __name__ == '__main__': 
        main() 
    

    UPDATE:ブライアンがアドバイスとして私がやりました。今私は2つの画面の再描画機能があります。それらの違いは、最初にすべての行を左に移動し、右に新しい行を追加し、キャンバスから外れる行を削除することです。 2番目の行は、行を左に移動し、キャンバスから落ちる要素を(新しいものを作成せずに)再配置します。私の最初の変種に関しては、これらのどれも大きく改善されていますが、もし私がもっと多くの要素を持っていれば、肉眼と肉眼の間に大きな違いはありません。後者は、私が崖から落ちる人を追跡する必要がないので、私のアプリケーションのために特に優れています。ここで

    機能:

    def drawGraph(self): ###needed for self.updateGraph2() only as it is creates the lines 
        for graphNum in range(0, self.numOfGraphs): 
         self.graphLines.append([]) 
         self.graphData.append([0,]*self.numOfDots) 
         for iii in range(0,self.numOfDots): 
          self.graphLines[graphNum].append(
           self.canv.create_line(0,0,0,0,fill=self.colors[graphNum], 
           width=1.2, tags=('graphLines', 'graph'+str(graphNum))) 
           ) 
    
    
    def updateGraph2(self): 
        while not self.queue.empty(): 
         iTuple = self.queue.get() 
         self.canv.move('graphLines', -self.density,0) 
         for graphNum in range(0, self.numOfGraphs): 
          try: self.graphData[graphNum].append(iTuple[graphNum]) 
          except IndexError: 
           self.graphData[graphNum].append(0) 
          self.graphData[graphNum].pop(0) 
          self.graphLines[graphNum].append(self.graphLines[graphNum].pop(0)) 
          self.canv.coords(self.graphLines[graphNum][-1], 
           self.canv.winfo_width()-self.density, 
           int(int(self.graphData[graphNum][-2])+int(self.canv.winfo_height()//2)), 
           self.canv.winfo_width(), 
           int(int(self.graphData[graphNum][-1])+int(self.canv.winfo_height()//2)) 
           ) 
    
    def updateGraph(self): 
        while not self.queue.empty(): 
         self.timingIndex = self.timingIndex + 1 
         self.canv.move('graphLines', -self.density, 0) 
         iTuple = self.queue.get() 
         for iii in range(0, len(iTuple)): 
          yyy = int(iTuple[iii])+self.canv.winfo_height()//2 
          if yyy < 0: yyy = 0 
          if yyy > self.canv.winfo_height(): yyy = self.canv.winfo_height() 
          prev_yyy = int(self.prevTuple[iii])+self.canv.winfo_height()//2 
          if prev_yyy < 0: prev_yyy = 0 
          if prev_yyy > self.canv.winfo_height(): prev_yyy = self.canv.winfo_height() 
          self.canv.create_line(
           self.canv.winfo_width()-self.density, prev_yyy, 
           self.canv.winfo_width(), yyy, 
           width = 1.4, fill = self.colors[iii], tags=('graphLines','graph'+str(iii))) 
         self.prevTuple = iTuple 
    
         self.canv.addtag_overlapping('todelete',-1,-1,-3,self.canv.winfo_height()+1) 
         self.canv.dtag('preserve','todelete') 
         self.canv.delete('todelete') 
    

    答えて

    2

    キャンバスの私の理解では、低速のそれを取得、割り当てられているより多くの要素のidということです。それは何の問題もなく何千もの100を扱うことができますが、100ミリ秒ごとに6000個のアイテムを作成して削除している場合、それはおそらくあなたの問題です。アイテムを削除しても、特に毎秒60,000を作成している場合は、パフォーマンスに影響を与えます。

    100ミリ秒ごとにすべてのアイテムを削除する代わりに、アイテムを画面から移動して覚えてから、coordsメソッドを使用して再利用して、新しいグラフの座標を変更します。

    +0

    ありがとうブライアン、私はそれを試してみましょう。私はすべてのアイテムのリストを持っていなければならないと思っています。 – dccharacter

    +0

    いいえ!私はタグを学ばなければならない! – dccharacter

    +0

    私の頭はこれで終わりません。私はすべての行を処理する方法を理解できません。私が__init__すると、self.numOfDotsのリストを初期化して線を描画します。今私は多くのタグ付き行を持っています。次の更新で私ができることは、すべてのタグ付き行を移動することです。私はキャンバスのものも見つけられます。しかし、どうすればそれらの座標を一つずつ変更することができますか?私は解決策は近いと感じていますが、それをグレープできません。しかし!!!それは実際にはより速く動作するようです。 – dccharacter

    関連する問題