python

Простая Scada на Python (продолжение)

  • вторник, 10 октября 2017 г. в 03:13:02
https://habrahabr.ru/post/339678/
  • Python


Формирование динамических объектов мнемосхемы


Развивая тему предыдущей статьи, опишу некоторые функции для формирования объектов мнемосхемы простой SCADA программы выполненной на Python.

Объекты визуально отображают состояние аналоговых измеряемых величин из регистров
READ_INPUT_REGISTERS или READ_HOLDING_REGISTERS. Используется библиотека Tkinter.
Всего реализовано 3 объекта: горизонтальный слайдер, вертикальный слайдер и стрелочный индикатор. Отдельно описывается построение динамического тренда.

Горизонтальный слайдер


Сперва необходимо создать объект canvas и получить на него ссылку.

Размещаться этот объект будет на уже созданном объекте root.

from Tkinter import * 
#создаем родительский объект
root = Tk() 
canv = Canvas(root,width=1900,height=950,bg="black",bd=0, highlightthickness=0, relief='ridge')
canv.place(x=0, y=25)
#создаем объект горизонтального слайдера 
c = Canvas(root,width=400,height=50,bg="black")
#размещаем в координатах
c.place(x=10, y=20)

root — ссылка на родительский объект.
width=400 – ширина.
height=50 – высота.
bg=«black» – фон.

Теперь выполняется рисование элементов слайдера с помощью графических примитивов.
Но для того, чтобы выполнилось корректное рисование элементов лучше создать функцию.

def hMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

Здесь входные переменные:
nowValue — текущее измеренное значение.
x — координата Х.
y — координата Y.
widgLen — длина слайдера.
widgHigh — высота слайдера.
maxValue — максимально допустимое значение.
outerColor — цвет заполнения.
nameValue — название измеряемого значения.

hMeterC
 def hMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
     c.place(x=x, y=y)
     if(nowValue > maxValue): nowValue=maxValue-1
     devValue=float(widgLen) / float(maxValue) 
     mesureValue = devValue * nowValue 
     c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
     c.create_rectangle(2,2,int(mesureValue),widgHigh-1,fill='red',outline='red')
     c.create_line(1,widgHigh,1,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(widgLen,widgHigh,widgLen,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen/4,widgHigh,1+widgLen/4 ,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen/2,widgHigh,1+widgLen/2 ,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen-widgLen/4,widgHigh,1+widgLen-widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_text(0,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='0') 
     c.create_text(widgLen -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(maxValue))
     c.create_text(widgLen/2 -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/2)))
     c.create_text(widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/4))) 
     c.create_text(widgLen-widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue-maxValue/4)))
     c.create_text(widgLen/2 -10,widgHigh-8,font="Verdana 12",anchor="w",justify=CENTER,fill='white',text=str(int(nowValue))) 
     c.create_text(1,widgHigh+21,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=nameValue)
     return (c,'hmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)


При выполнении нижеприведенного кода, на экране в окне сформируется горизонтальный слайдер, заполненный на 25 процентов красным цветом и отображением текущего измеренного значения в середине или справа от слайдера.

Если это произошло, значит код работает без ошибок.

Код программы.
from Tkinter import *

def hMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
     c.place(x=x, y=y)
     if(nowValue > maxValue): nowValue=maxValue-1
     devValue=float(widgLen) / float(maxValue) 
     mesureValue = devValue * nowValue 
     c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
     c.create_rectangle(2,2,int(mesureValue),widgHigh-1,fill='red',outline='red')
     c.create_line(1,widgHigh,1,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(widgLen,widgHigh,widgLen,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen/4,widgHigh,1+widgLen/4 ,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen/2,widgHigh,1+widgLen/2 ,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen-widgLen/4,widgHigh,1+widgLen-widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_text(0,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='0') 
     c.create_text(widgLen -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(maxValue))
     c.create_text(widgLen/2 -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/2)))
     c.create_text(widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/4))) 
     c.create_text(widgLen-widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue-maxValue/4)))
     c.create_text(widgLen/2 -10,widgHigh-8,font="Verdana 12",anchor="w",justify=CENTER,fill='white',text=str(int(nowValue))) 
     c.create_text(1,widgHigh+21,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=nameValue)
     return (c,'hmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

root = Tk()
canv = Canvas(root,width=1900,height=950,bg="black")
canv.place(x=0, y=25)
analogFig=hMeterC(250,20,50,300,20,1000,'red','analog Meter')
root.mainloop()


Но это всего лишь статическое отображение объекта «горизонтального слайдера».
Необходимо добавить динамики. Для этого нужно использовать метод root.after и немного изменить функции отображения слайдера.

Функция hMeterC примет вид:

def hMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
     c.place(x=x, y=y)
     return (c,'hmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

То есть функция hMeterC будет возвращать массив параметров созданного объекта, а рисование графики будет осуществлять другая функция

hMeter:
def hMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
 
     if(nowValue > maxValue): nowValue=maxValue-1
     devValue=float(widgLen) / float(maxValue) 
     mesureValue = devValue * nowValue 
     c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
     c.create_rectangle(2,2,int(mesureValue),widgHigh-1,fill=outerColor,outline=outerColor)
     c.create_line(1,widgHigh,1,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(widgLen,widgHigh,widgLen,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen/4,widgHigh,1+widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen/2,widgHigh,1+widgLen/2 ,widgHigh+5,width=1,fill=outerColor) 
     c.create_line(1+widgLen-widgLen/4,widgHigh,1+widgLen-widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_text(0,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='0') 
     c.create_text(widgLen -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(maxValue))
     c.create_text(widgLen/2 -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/2)))
     c.create_text(widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/4))) 
     c.create_text(widgLen-widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue-maxValue/4)))
     c.create_text(widgLen +10,widgHigh-8,font="Verdana 12",anchor="w",justify=CENTER,fill=outerColor,text=str(int(nowValue)))
     c.create_text(1,widgHigh+21,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=nameValue) 


Необходимо добавить еще функцию цикла def jobMeter() для root.after,

def jobMeter():
    hMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)
    root.after(100, jobMeter) 

Теперь код программы немного изменился.

Код программы

import time
from Tkinter import *
import random

def hMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     if(nowValue > maxValue): nowValue=maxValue-1
     devValue=float(widgLen) / float(maxValue)
     mesureValue = devValue * nowValue
     c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
     c.create_rectangle(2,2,int(mesureValue),widgHigh-1,fill=outerColor,outline=outerColor)
     c.create_line(1,widgHigh,1,widgHigh+5,width=1,fill=outerColor)
     c.create_line(widgLen,widgHigh,widgLen,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen/4,widgHigh,1+widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen/2,widgHigh,1+widgLen/2 ,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen-widgLen/4,widgHigh,1+widgLen-widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_text(0,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='0')
     c.create_text(widgLen -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(maxValue))
     c.create_text(widgLen/2 -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/2)))
     c.create_text(widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/4)))
     c.create_text(widgLen-widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue-maxValue/4)))
     c.create_text(widgLen +10,widgHigh-8,font="Verdana 12",anchor="w",justify=CENTER,fill=outerColor,text=str(int(nowValue)))
     c.create_text(1,widgHigh+21,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=nameValue)



def hMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
     c.place(x=x, y=y)
     return (c,'hmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)


def jobMeter():
    hMeter(analogFig[0],100,analogFig[2],analogFig[3],analogFig[4],analogFig[5],analogFig[6],analogFig[7],analogFig[8])
    root.after(100, jobMeter)

root = Tk()
canv = Canvas(root,width=1900,height=950,bg="black")
canv.place(x=0, y=25)

analogFig=hMeterC(250,20,50,300,20,1000,'red','analog Meter')

root.after(100, jobMeter)
root.mainloop()



Каждую 0.1 секунды, объект слайдера будет обновляться, но нужно сказать,
что изменений никаких происходить не будет, потому что в переменной nowValue функции hMeter всегда присутствует неизменяемое значение 100.

Чтобы исправить эту ситуацию и продемонстрировать динамические свойства объекта предлагается воспользоваться библиотекой random и внести изменения в код.

import random
def jobMeter():
    hMeter(analogFig[0],random.randint(30, 800),analogFig[2],analogFig[3],analogFig[4],analogFig[5],analogFig[6],analogFig[7],analogFig[8])
    root.after(100, jobMeter)

Теперь будут очевидные изменения в положении слайдера.

Но в данном случае присутствуют два неприятных факта.

  1. Созданный скрипт по объему занимает не так много памяти, но при выполнении объем занимаемой памяти увеличивается.
  2. Цифры справа, отображающие текущее значение различить невозможно.

Это все объясняется просто.

Каждый раз при формировании нового измеренного значения на canvas происходит
перерисовка графических примитивов, но при этом старые элементы тоже сохраняются.
Происходит наложение новых элементов на предыдущие.

Чтобы избавиться от этого эффекта нужно добавить очистку canvas от старых элементов
в функцию jobMeter().

def jobMeter():
    analogFig[0].delete("all")
    hMeter(analogFig[0],100,analogFig[2],analogFig[3],analogFig[4],analogFig[5],analogFig[6],analogFig[7],analogFig[8])
    root.after(100, jobMeter)

Теперь память не увеличивается и цифры отображаются корректно.

Вертикальный слайдер


Здесь все аналогично горизонтальному, только немного меняется конфигурация рисования примитивов.

Функции.
def vMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
         c.place(x=x, y=y)
         return (c,'vmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

def vMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         if(nowValue > maxValue): nowValue=maxValue-1
         devValue=float(widgHigh) / float(maxValue)
         mesureValue = devValue * nowValue
         c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
         c.create_rectangle(widgLen-1,widgHigh,2,widgHigh-int(mesureValue),fill=outerColor,outline=outerColor)
         c.create_line(widgLen,widgHigh,widgLen+10,widgHigh,width=1,fill=outerColor)
         c.create_line(widgLen,widgHigh/4,widgLen+10,widgHigh/4,width=1,fill=outerColor)
         c.create_line(widgLen,widgHigh/2,widgLen+10,widgHigh/2,width=1,fill=outerColor)
         c.create_line(widgLen,widgHigh-widgHigh/4,widgLen+10,widgHigh-widgHigh/4,width=1,fill=outerColor)
         c.create_line(widgLen,1,widgLen+10,1,width=1,fill=outerColor)
         c.create_line(widgLen+10,widgHigh,widgLen+10 ,widgHigh,width=1,fill=outerColor)
         c.create_text(widgLen+12,widgHigh,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text='0')
         c.create_text(widgLen+12,10,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue))
         c.create_text(widgLen+12,widgHigh/2,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/2))
         c.create_text(widgLen+12,widgHigh-widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/4))
         c.create_text(widgLen+12,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue-maxValue/4))
         c.create_text(2,widgHigh+15,font="Verdana 12",anchor="w",justify=CENTER,fill=outerColor,text=str(nowValue))



Стрелочный прибор.
Для создания данного объекта придется импортировать библиотеку math, поскольку
используются математические функции sin и cos.

Функции
def aMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         c = Canvas(root,width=widgLen,height=widgHigh,bg="black",bd=0, highlightthickness=0, relief='ridge')
         c.place(x=x, y=y)
         return (c,'ameter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

def aMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         if(nowValue > maxValue): nowValue=maxValue-1
         devValue=float(180) / float(maxValue)
         mesureValue = devValue * nowValue
         x1 = widgLen/2
         y1 = widgHigh/2 + 10
         x2 = 10
         y2 = widgHigh/2 + 10
         angle = math.pi * int(mesureValue) / 180;
         newx = ((x2-x1)*math.cos(angle)-(y2-y1)*math.sin(angle)) + x1
         newy = ((x2-x1)*math.sin(angle)+(y2-y1)*math.cos(angle)) + y1
         c.create_oval(1 , 1,widgLen-1 ,widgHigh-1,width=2,fill='black',outline=outerColor)
         c.create_text(7,y1,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text='0')
         c.create_text(widgLen-30,y1,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue))
         c.create_text(widgLen/2-10,10,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/2))
         c.create_text(widgLen/8,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/4))
         c.create_text(widgLen/2+widgLen/4,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue-maxValue/4))
         c.create_text(widgLen/2-20,widgHigh-40,font="Verdana 14",anchor="w",justify=CENTER,fill=outerColor,text=str(nowValue))
         c.create_rectangle(0,widgHigh/2+18,widgLen ,widgHigh,fill='black',outline='black')
         c.create_text(widgLen/2-20,widgHigh-40,font="Verdana 14",anchor="w",justify=CENTER,fill=outerColor,text=str(nowValue))
         c.create_text(6,widgHigh-20,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(nameValue))
         c.create_oval(x1 - 10, y1 - 10, x1+ 10,y1 + 10,fill=outerColor,outline=outerColor)
         c.create_line(x1,y1,newx,newy,width=5,fill=outerColor)


Динамический график


Последнее, о чем хотелось бы сказать так это построение динамического графика. Существует библиотека matplotlib, в которой реализовано много удобных инструментов для построения графиков, а также большое количество документации. Но я опишу простой способ создания динамического графика с помощью библиотеки tkinter.

Создание объекта выполняется аналогично предыдущих примеров, но вот показания должны передаваться в виде массива, где каждый новый элемент добавляется в конец методом append.
После этого происходит перерисовка объекта.

Допустим на отметке времени 0 имеется массив с последним измеренным значением (100),
при формировании новой метки времени будет добавлен новый элемент массива и в него записано новое измеренное значение, пусть это будет (110).

Получается приблизительно такая таблица.

0.0 с — (100)
0.1 с — (100,110)
0.2 с — (100,110,90)
0.3 с — (100,110,90,120)
0.4 с — (100,110,90,120,100)
и т.д.

При достижении количества элементов массива заданной величины,
массив обнуляется и процесс повторяется снова.

if(len(mesureValue) == 80):
         mesureValue=None
         mesureValue=[] 

Таким образом кривая показаний доходит до конечной границы canvas объекта графика,
после этого значения обнуляются и процесс накопления данных в массиве повторяется.
Ниже приведен простой пример программы демонстрирующей работу всех вышеописанных объектов мнемосхемы.

Код программы
import time
from Tkinter import *
import random
import math

def hMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     if(nowValue > maxValue): nowValue=maxValue-1
     devValue=float(widgLen) / float(maxValue)
     mesureValue = devValue * nowValue
     c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
     c.create_rectangle(2,2,int(mesureValue),widgHigh-1,fill=outerColor,outline=outerColor)
     c.create_line(1,widgHigh,1,widgHigh+5,width=1,fill=outerColor)
     c.create_line(widgLen,widgHigh,widgLen,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen/4,widgHigh,1+widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen/2,widgHigh,1+widgLen/2 ,widgHigh+5,width=1,fill=outerColor)
     c.create_line(1+widgLen-widgLen/4,widgHigh,1+widgLen-widgLen/4 ,widgHigh+5,width=1,fill=outerColor)
     c.create_text(0,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='0')
     c.create_text(widgLen -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(maxValue))
     c.create_text(widgLen/2 -10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/2)))
     c.create_text(widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/4)))
     c.create_text(widgLen-widgLen/4-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue-maxValue/4)))
     c.create_text(widgLen +10,widgHigh-8,font="Verdana 12",anchor="w",justify=CENTER,fill=outerColor,text=str(int(nowValue)))
     c.create_text(1,widgHigh+21,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=nameValue)



def hMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
     c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
     c.place(x=x, y=y)
     return (c,'hmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)



def vMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
         c.place(x=x, y=y)
         return (c,'vmeter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

def vMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         if(nowValue > maxValue): nowValue=maxValue-1
         devValue=float(widgHigh) / float(maxValue)
         mesureValue = devValue * nowValue
         c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
         c.create_rectangle(widgLen-1,widgHigh,2,widgHigh-int(mesureValue),fill=outerColor,outline=outerColor)
         c.create_line(widgLen,widgHigh,widgLen+10,widgHigh,width=1,fill=outerColor)
         c.create_line(widgLen,widgHigh/4,widgLen+10,widgHigh/4,width=1,fill=outerColor)
         c.create_line(widgLen,widgHigh/2,widgLen+10,widgHigh/2,width=1,fill=outerColor)
         c.create_line(widgLen,widgHigh-widgHigh/4,widgLen+10,widgHigh-widgHigh/4,width=1,fill=outerColor)
         c.create_line(widgLen,1,widgLen+10,1,width=1,fill=outerColor)
         c.create_line(widgLen+10,widgHigh,widgLen+10 ,widgHigh,width=1,fill=outerColor)
         c.create_text(widgLen+12,widgHigh,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text='0')
         c.create_text(widgLen+12,10,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue))
         c.create_text(widgLen+12,widgHigh/2,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/2))
         c.create_text(widgLen+12,widgHigh-widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/4))
         c.create_text(widgLen+12,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue-maxValue/4))
         c.create_text(2,widgHigh+15,font="Verdana 12",anchor="w",justify=CENTER,fill=outerColor,text=str(nowValue))





def aMeterC(nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         c = Canvas(root,width=widgLen,height=widgHigh,bg="black",bd=0, highlightthickness=0, relief='ridge')
         c.place(x=x, y=y)
         return (c,'ameter',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue)

def aMeter(c,nowValue,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue):
         if(nowValue > maxValue): nowValue=maxValue-1
         devValue=float(180) / float(maxValue)
         mesureValue = devValue * nowValue
         x1 = widgLen/2
         y1 = widgHigh/2 + 10
         x2 = 10
         y2 = widgHigh/2 + 10
         angle = math.pi * int(mesureValue) / 180;
         newx = ((x2-x1)*math.cos(angle)-(y2-y1)*math.sin(angle)) + x1
         newy = ((x2-x1)*math.sin(angle)+(y2-y1)*math.cos(angle)) + y1
         c.create_oval(1 , 1,widgLen-1 ,widgHigh-1,width=2,fill='black',outline=outerColor)
         c.create_text(7,y1,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text='0')
         c.create_text(widgLen-30,y1,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue))
         c.create_text(widgLen/2-10,10,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/2))
         c.create_text(widgLen/8,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue/4))
         c.create_text(widgLen/2+widgLen/4,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(maxValue-maxValue/4))
         c.create_text(widgLen/2-20,widgHigh-40,font="Verdana 14",anchor="w",justify=CENTER,fill=outerColor,text=str(nowValue))
         c.create_rectangle(0,widgHigh/2+18,widgLen ,widgHigh,fill='black',outline='black')
         c.create_text(widgLen/2-20,widgHigh-40,font="Verdana 14",anchor="w",justify=CENTER,fill=outerColor,text=str(nowValue))
         c.create_text(6,widgHigh-20,font="Verdana 10",anchor="w",justify=CENTER,fill=outerColor,text=str(nameValue))
         c.create_oval(x1 - 10, y1 - 10, x1+ 10,y1 + 10,fill=outerColor,outline=outerColor)
         c.create_line(x1,y1,newx,newy,width=5,fill=outerColor)

def hTrendC(x,y,widgLen,widgHigh,maxValue,outerColor,nameValue,trendKoef):
         c = Canvas(root,width=widgLen+50,height=widgHigh+40,bg="black",bd=0, highlightthickness=0, relief='ridge')
         c.place(x=x, y=y)
         return (c,'htrend',x,y,widgLen,widgHigh,maxValue,outerColor,nameValue,trendKoef)

def hTrend(arrayData,arrayValue):
         c,markErr,x,y,widgLen,widgHigh,maxValue,outerColor,nameValue,trendKoef=arrayData
         c.create_rectangle(1,1,widgLen,widgHigh,fill='black',outline=outerColor)
         c.create_line(50,widgHigh/2,widgLen-5,widgHigh/2,width=0.1,fill='white',dash=(4, 2))
         c.create_line(50,widgHigh/4,widgLen-5,widgHigh/4,width=0.1,fill='white',dash=(4, 2))
         c.create_line(50,widgHigh - widgHigh/4,widgLen-5,widgHigh -widgHigh/4,width=0.2,fill='white',dash=(4, 2))
         c.create_text(10,widgHigh-10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=0)
         c.create_text(10,12,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(maxValue))
         c.create_text(10,widgHigh/2,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/2)))
         c.create_text(10,widgHigh/4,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue-maxValue/4)))
         c.create_text(10,widgHigh - widgHigh/4 ,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(int(maxValue/4)))
         c.create_text(1,widgHigh+25,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=nameValue)
         c.create_text(widgLen/10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='1')
         c.create_text((widgLen/10)*2,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='2')
         c.create_text((widgLen/10)*3,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='3')
         c.create_text((widgLen/10)*4,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='4')
         c.create_text((widgLen/10)*5,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='5')
         c.create_text((widgLen/10)*6,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='6')
         c.create_text((widgLen/10)*7,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='7')
         c.create_text((widgLen/10)*8,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='8')
         c.create_text((widgLen/10)*9,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='9')
         c.create_text(widgLen-10,widgHigh+10,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text='100')

         oldy=widgHigh - float(widgHigh)/float(maxValue) * arrayValue[0] * int(trendKoef)
         oldx=5
         xval=0

         for counter in range(0,len(arrayValue)):

             val=arrayValue[counter]
             yval=widgHigh - float(widgHigh)/float(maxValue) * val * int(trendKoef)
             xval+=10
             c.create_line(oldx,oldy,xval,yval,width=1.5,fill='green')

             oldy=yval
             oldx=xval
         mesureValue = arrayValue[len(arrayValue)-1 ] * int(trendKoef)
         c.create_line(xval,widgHigh-10,xval,0,width=0.5,fill='white')
         c.create_text(xval+10,yval,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=str(mesureValue))
         c.create_text(xval+10,yval+20,font="Verdana 10",anchor="w",justify=CENTER,fill='white',text=time.strftime('%H:%M:%S'))


def jobMeter():
     global mesureValue
     analogFig[0].delete("all")
     analogVertical[0].delete("all")
     analogString[0].delete("all")

     hMeter(analogFig[0],random.randint(30, 800),analogFig[2],analogFig[3],analogFig[4],analogFig[5],analogFig[6],analogFig[7],analogFig[8])
     vMeter(analogVertical[0],random.randint(30, 800),analogVertical[2],analogVertical[3],analogVertical[4],analogVertical[5],analogVertical[6],analogVertical[7],analogVertical[8])
     aMeter(analogString[0],random.randint(30, 800),analogString[2],analogString[3],analogString[4],analogString[5],analogString[6],analogString[7],analogString[8])

     lenVal = len(mesureValue)+1
     mesureValue.append(lenVal)
     mesureValue[lenVal-1] = random.randint(30, 800)
     trend[0].delete("all")
     hTrend(trend,mesureValue)

     if(len(mesureValue) == 80):
         mesureValue=None
         mesureValue=[]




     root.after(100, jobMeter)

root = Tk()
canv = Canvas(root,width=1900,height=950,bg="black")
canv.place(x=0, y=25)


analogFig=hMeterC(250,20,50,300,20,1000,'red','analog Meter')
analogVertical=vMeterC(250,42,150,30,200,1000,'red','analog Meter')
analogString=aMeterC(250,200,150,150,150,1000,'green','analog Meter')

global mesureValue
mesureValue=[]
trend=hTrendC(450,28,800,400,1000,'green','analog', '1')



root.after(1, jobMeter)
root.mainloop()


Больше примеров можно посмотреть здесь