Что нового

Программа рисующая график

Genics

Чайник
Сообщения
28
Репутация
0
Версия AutoIt: v3.3.10.2
ОС: Windows7 x64

Описание:

Доброго дня!

Нужна программка которая в реальном времени (можно даже через каждые 5 сек ) читает денные с текстового файла File_Grafika.dat и рисует в окне динамический график как в Excel (пример окна с графиком вложен). Динамический нужен чтобы значения не выходили за пределы окна, а автоматом подстраивала шкалу по Y (вертикали). По X не обязательно. Если динамику сделать сложно, то необязательно.

Описание что где в файле File_Grafika.dat во вложенном файле File_Grafika_шаблон.dat. Там в скобках указано где X, а где Y.

Размер окна ширина:1200, высота:750
Можно чтобы окно меняло размер, но тогда надо чтобы график менял свой масштаб, если это сложно то необязательно.
 

Вложения

ra4o

Скриптер
Сообщения
985
Репутация
180
Рисовалка Вашего графика (пока без динамики)
Код:
#include <array.au3>
#include "GraphGDIPlus.au3"

Opt("GUIOnEventMode", 1)

Global $aArray
Global $aRes[0][2]
Global $Ymax
Global $Ymin
Global $Xmax
Global $Xmin

$GUI = GUICreate("", 1300, 850)
GUISetOnEvent(-3, "_Exit")
GUISetState()

$sPath_File = @ScriptDir & '\File_Grafika.dat'
$aArray = FileReadToArray($sPath_File)

_CreateArray2D()
_MinMaxX()
_MinMaxY()

$Graph = _GraphGDIPlus_Create($GUI, 40, 30, 1200, 750, 0xFF000000, 0xFF88B3DD)

_GraphGDIPlus_Set_RangeX($Graph, $Xmin, $Xmax, 10, 1, 1)
_GraphGDIPlus_Set_RangeY($Graph, $Ymin, $Ymax, 10, 1, 1)

_GraphGDIPlus_Set_GridX($Graph, 1, 0xFF6993BE)
_GraphGDIPlus_Set_GridY($Graph, 1, 0xFF6993BE)

_Draw_Graph()

While 1
	Sleep(100)
WEnd

Func _MinMaxX()
	$Xmin = $aRes[0][0]
	$Xmax = $aRes[0][0]
	For $i = 1 To UBound($aRes) - 1
		If $aRes[$i][0] - $Xmax > 0 Then $Xmax = $aRes[$i][0]
		If $aRes[$i][0] - $Xmin < 0 Then $Xmin = $aRes[$i][0]
	Next
EndFunc   ;==>_MinMaxX

Func _MinMaxY()
	$Ymin = $aRes[0][1]
	$Ymax = $aRes[0][1]
	For $i = 1 To UBound($aRes) - 1
		If $aRes[$i][1] - $Ymax > 0 Then $Ymax = $aRes[$i][1]
		If $aRes[$i][1] - $Ymin < 0 Then $Ymin = $aRes[$i][1]
	Next
EndFunc   ;==>_MinMaxY


Func _CreateArray2D()

	;Удалить пустые строки в массиве и убрать номера строк
	For $i = 0 To UBound($aArray) - 1
		If $aArray[$i] <> '' Then
			$Dt = StringSplit($aArray[$i], ' ')
			$aArray[$i] = $Dt[2]
		Else
			_ArrayDelete($aArray, $i)
		EndIf

	Next

	;Разбивка массива - первая половина Х, вторая У
	ReDim $aRes[UBound($aArray) / 2][2]
	;X
	For $i = 0 To UBound($aArray) / 2 - 1
		$aRes[$i][0] = $aArray[$i]
	Next

	;Y
	$n = 0
	For $y = UBound($aArray) / 2 To UBound($aArray) - 1
		$aRes[$n][1] = $aArray[$y]
		$n += 1
	Next

EndFunc   ;==>_CreateArray2D


Func _Draw_Graph()

	_GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
	_GraphGDIPlus_Set_PenSize($Graph, 2)

	$First = True

	For $i = 0 To UBound($aRes) - 1

		$x = $aRes[$i][0]
		$y = $aRes[$i][1]
		If $First = True Then _GraphGDIPlus_Plot_Start($Graph, $x, $y)
		$First = False
		_GraphGDIPlus_Plot_Line($Graph, $x, $y)
		_GraphGDIPlus_Refresh($Graph)

	Next

EndFunc   ;==>_Draw_Graph


Func _Exit()
	_GraphGDIPlus_Delete($GUI, $Graph)
	Exit
EndFunc   ;==>_Exit
Библиотека "GraphGDIPlus.au3" во вложении
 

Вложения

Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
Приветствую тебя ra4o!
Опять ты взялся за решение моей задачи :smile: . То что ты предложил работает и это мне нравиться, только смотрится он не очень симпатично.
В моей ситуации, график должен не только рисоваться но и быть читабельным и красивым.
Поэтому надо поработать над дизайном :smile: . В принципе для понимания как он должен выглядеть ничего сложного нет, я вкладывал картинку образец который был нарисован в Excel по тем же самым точкам x,y.

Дальше распишу мои пожелания в виде пунктов:

1. Если пока делаем без динамики то координата Y должна быть 2 1 0 -1 -2 Ноль обязательно по середине. Линии X,Y более жирные чем сетка.

2. Линия Y располагается по середине и делится на цифры всегда статично (без динамики) на 0; 10; 20; 30; 40; 50; 60; 70; 80; 90;100. Дробные числа здесь не нужны. Эти цифры должны располагаться прямо на линии (которая по середине) а не внизу графика.

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

4. Линия самого графика должна сопровождаться маркерами точек по которым рисуется график. Как в примере там красные ромбики, но можно сделать и черные кружочки.

5. Если я меняю значение в файле File_Grafika.dat и сохраняю то программа должна автоматом перерисовывать график. Просто работать в бесконечном цикле (читает-рисует) через каждые 5 сек.

Я не знаю что здесь можно реализовать а что нет, жду твоих комментариев.
 

ra4o

Скриптер
Сообщения
985
Репутация
180
Так ?
Код:
#include <array.au3>
#include "GraphGDIPlus.au3"

Opt("GUIOnEventMode", 1)

AdlibRegister("_Go", 5000)

Global $aArray
Global $aRes[0][2]
Global $Ymax
Global $Ymin
Global $Xmax
Global $Xmin

$GUI = GUICreate("", 1300, 850)
GUISetOnEvent(-3, "_Exit")
GUISetState()

$sPath_File = @ScriptDir & '\File_Grafika.dat'

_CreateArray2D()

$Graph = _GraphGDIPlus_Create($GUI, 40, 30, 1200, 750, 0xFF000000, 0xFFB0C4DE)

_GraphGDIPlus_Set_RangeX($Graph, 0, 100, 10, 1, 0)
_GraphGDIPlus_Set_RangeY($Graph, -2, 2, 4, 1, 0)

_GraphGDIPlus_Set_GridX($Graph, 1, 0xFF6993BE)
_GraphGDIPlus_Set_GridY($Graph, 1, 0xFF6993BE)

_Draw_Graph()

While 1
	Sleep(100)
WEnd

Func _Go()

	_CreateArray2D()
	_GraphGDIPlus_Clear($Graph)

	_GraphGDIPlus_Set_GridX($Graph, 1, 0xFF6993BE)
	_GraphGDIPlus_Set_GridY($Graph, 1, 0xFF6993BE)

	_Draw_Graph()

EndFunc   ;==>_Go

Func _CreateArray2D()
	; Чтение файла в массив
	$aArray = FileReadToArray($sPath_File)

	;Удалить пустые строки в массиве и убрать номера строк
	For $i = 0 To UBound($aArray) - 1
		If $aArray[$i] <> '' Then
			$Dt = StringSplit($aArray[$i], ' ')
			$aArray[$i] = $Dt[2]
		Else
			_ArrayDelete($aArray, $i)
		EndIf

	Next

	;Разбивка массива - первая половина Х, вторая У
	ReDim $aRes[UBound($aArray) / 2][2]
	;X
	For $i = 0 To UBound($aArray) / 2 - 1
		$aRes[$i][0] = $aArray[$i]
	Next

	;Y
	$n = 0
	For $y = UBound($aArray) / 2 To UBound($aArray) - 1
		$aRes[$n][1] = $aArray[$y]
		$n += 1
	Next

EndFunc   ;==>_CreateArray2D


Func _Draw_Graph()

	_GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
	_GraphGDIPlus_Set_PenSize($Graph, 2)

	$First = True

	For $i = 0 To UBound($aRes) - 1

		$x = $aRes[$i][0]
		$y = $aRes[$i][1]
		If $First = True Then _GraphGDIPlus_Plot_Start($Graph, $x, $y)
		$First = False

		_GraphGDIPlus_Plot_Line($Graph, $x, $y)
		;Цвет для точки
		_GraphGDIPlus_Set_PenColor($Graph, 0xFFFFFF00)
		_GraphGDIPlus_Set_PenSize($Graph, 5)
		;Рисуем точку
		_GraphGDIPlus_Plot_Point($Graph, $x, $y)
		;Цвет для линии
		_GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
		_GraphGDIPlus_Set_PenSize($Graph, 2)

		_GraphGDIPlus_Refresh($Graph)
	Next

EndFunc   ;==>_Draw_Graph


Func _Exit()
	_GraphGDIPlus_Delete($GUI, $Graph)
	Exit
EndFunc   ;==>_Exit
 

joiner

Модератор
Локальный модератор
Сообщения
3 012
Репутация
502
OffTopic:
Genics [?]
Поэтому надо поработать над
Поэтому можно не стесняться и предложить человеку денежку за работу. тогда работа будет идти веселее и все будет красивее.
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
О да, это то что нужно! Только я не думал что график так будет противно мелькать при перерисовке.
ra4o если можно, пусть график перерисовывается только в том случае если произошли изменения значений в каких то точках, и желательно не весь а только там где были изменения.

Сетку надо немного исправить, слишком много полосок, надо только на самих цифрах и одну между ними (образец во вложенном файле)
 

Вложения

ra4o

Скриптер
Сообщения
985
Репутация
180
Пробуйте
Код:
#include <array.au3>
#include "GraphGDIPlus.au3"

Opt("GUIOnEventMode", 1)


Global $time
Global $NewTime
Global $aArray
Global $aRes[0][2]
Global $Ymax
Global $Ymin
Global $Xmax
Global $Xmin

$GUI = GUICreate("", 1300, 650)
GUISetOnEvent(-3, "_Exit")
GUISetState()

$sPath_File = @ScriptDir & '\File_Grafika.dat'

$NewTime = 0

$Graph = _GraphGDIPlus_Create($GUI, 40, 30, 1200, 550, 0xFF000000, 0xFFB0C4DE)
_GraphGDIPlus_Set_RangeX($Graph, 0, 100, 10, 1, 0)
_GraphGDIPlus_Set_RangeY($Graph, -2, 2, 4, 1, 0)

While 1
	$time = FileGetTime($sPath_File, 0, 1)
	If $time <> $NewTime Then
		$NewTime = $time
		_Go()
	EndIf

	Sleep(100)

WEnd

Func _Go()

	_CreateArray2D()
	_GraphGDIPlus_Clear($Graph)

	_GraphGDIPlus_Set_GridX($Graph, 5, 0xFF6993BE)
	_GraphGDIPlus_Set_GridY($Graph, 0.5, 0xFF6993BE)

	_Draw_Graph()

EndFunc   ;==>_Go

Func _CreateArray2D()
	; Чтение файла в массив
	$aArray = FileReadToArray($sPath_File)

	;Удалить пустые строки в массиве и убрать номера строк
	For $i = 0 To UBound($aArray) - 1
		If $aArray[$i] <> '' Then
			$Dt = StringSplit($aArray[$i], ' ')
			$aArray[$i] = $Dt[2]
		Else
			_ArrayDelete($aArray, $i)
		EndIf

	Next

	;Разбивка массива - первая половина Х, вторая У
	ReDim $aRes[UBound($aArray) / 2][2]
	;X
	For $i = 0 To UBound($aArray) / 2 - 1
		$aRes[$i][0] = $aArray[$i]
	Next

	;Y
	$n = 0
	For $y = UBound($aArray) / 2 To UBound($aArray) - 1
		$aRes[$n][1] = $aArray[$y]
		$n += 1
	Next

EndFunc   ;==>_CreateArray2D


Func _Draw_Graph()

	_GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
	_GraphGDIPlus_Set_PenSize($Graph, 2)

	$First = True

	For $i = 0 To UBound($aRes) - 1

		$x = $aRes[$i][0]
		$y = $aRes[$i][1]
		If $First = True Then _GraphGDIPlus_Plot_Start($Graph, $x, $y)
		$First = False

		_GraphGDIPlus_Plot_Line($Graph, $x, $y)
		;Цвет для точки
		_GraphGDIPlus_Set_PenColor($Graph, 0xFFFFFF00)
		_GraphGDIPlus_Set_PenSize($Graph, 5)
		;Рисуем точку
		_GraphGDIPlus_Plot_Point($Graph, $x, $y)
		;Цвет для линии
		_GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
		_GraphGDIPlus_Set_PenSize($Graph, 2)

		_GraphGDIPlus_Refresh($Graph)
	Next

EndFunc   ;==>_Draw_Graph


Func _Exit()
	_GraphGDIPlus_Delete($GUI, $Graph)
	Exit
EndFunc   ;==>_Exit
Мерцать при перерисовке будет, но только при изменении файла с координатами.
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
ra4o супер :smile: Сейчас внешний вид почти такой как я хотел. Ну да ладно все круто.

ra4o я правильно понял, что при изменении одного! значения x,y программа будет рисовать график заново пробегая по всем точкам?

Еще заметил то, что если не менять значения а просто пересохранить файл File_Grafika.dat нажав Ctrl-S, график все равно перерисуется! А этого он делать не должен, так как значения в файле не изменились.

Я хотел такой алгоритм работы:
1. При старте: читает файл; рисует график.
2. Через 5 сек: читает файл; сравнивает значения на графике и в файле; если они равны. ничего делать не надо, иначе (если изменились) рисуем график по новым данным.
3. Бесконечно повторяем пункт 2.
 

ra4o

Скриптер
Сообщения
985
Репутация
180
ra4o я правильно понял, что при изменении одного! значения x,y программа будет рисовать график заново пробегая по всем точкам?
Да, всё верно !
если не менять значения а просто пересохранить файл File_Grafika.dat нажав Ctrl-S, график все равно перерисуется!
я не сравниваю значения , а сравниваю дату и время изменения файла - файл изменили - значит перерисовываем !
я проверяю каждые 100 мс , замените в бесконечном цикле на " Sleep(5000)" и будет Вам 5 сек.
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
я не сравниваю значения , а сравниваю дату и время изменения файла - файл изменили - значит перерисовываем !
Дело в том что файл у меня будет переписываться каждую секунду, при этом значения точек могут не меняться. Sleep(5000) я сделал, получается график опять будет мерцать каждые 5 сек :( Все таки надо чтобы значения сравнивал.
 

ra4o

Скриптер
Сообщения
985
Репутация
180
Все таки надо чтобы значения сравнивал.
Тогда, можно так :
Код:
#include <array.au3>
#include "GraphGDIPlus.au3"

Opt("GUIOnEventMode", 1)

Global $time
Global $NewTime
Global $aArray
Global $sOld_Array
Global $aRes[0][2]

$GUI = GUICreate("", 1300, 650)
GUISetOnEvent(-3, "_Exit")
GUISetState()

$sPath_File = @ScriptDir & '\File_Grafika.dat'

$sOld_Array = ''

$Graph = _GraphGDIPlus_Create($GUI, 40, 30, 1200, 550, 0xFF000000, 0xFFB0C4DE)

While 1

    ; Чтение файла в массив
    $aArray = FileReadToArray($sPath_File)
    $sNew_Array = _ArrayToString($aArray, '|')
    If $sNew_Array <> $sOld_Array Then
        $sOld_Array = $sNew_Array
        _Go()
    EndIf

    Sleep(5000)

WEnd

Func _Go()
    Local $Point, $Grid

    _CreateArray2D()
    _GraphGDIPlus_Clear($Graph)
    _GraphGDIPlus_Set_RangeX($Graph, 0, 100, 10, 1, 0)

    $MaxY = _MaxY()
	If $MaxY=1 Then
		$Point=20
		$Grid=0.1
	Else
		$point=$MaxY * 2
		$Grid=0.5
	EndIf


    _GraphGDIPlus_Set_RangeY($Graph, -$MaxY, $MaxY, $Point, 1, 1)
    _GraphGDIPlus_Set_GridX($Graph, 5, 0xFF6993BE)
    _GraphGDIPlus_Set_GridY($Graph, $Grid, 0xFF6993BE)

    _Draw_Graph()

EndFunc   ;==>_Go


Func _MaxY()

    $Ymax = Abs($aRes[0][1])
    For $i = 1 To UBound($aRes) - 1
        If Abs($aRes[$i][1]) - $Ymax > 0 Then $Ymax = Abs($aRes[$i][1])
    Next

    $Ymax = Ceiling($Ymax + $Ymax / 20) ;Плюс 5% к макс. значению У

    Return $Ymax

EndFunc   ;==>_MaxY


Func _CreateArray2D()


    ;Удалить пустые строки в массиве и убрать номера строк
    For $i = 0 To UBound($aArray) - 1
        If $aArray[$i] <> '' Then
            $Dt = StringSplit($aArray[$i], ' ')
            $aArray[$i] = $Dt[2]
        Else
            _ArrayDelete($aArray, $i)
        EndIf

    Next

    ;Разбивка массива - первая половина Х, вторая У
    ReDim $aRes[UBound($aArray) / 2][2]
    ;X
    For $i = 0 To UBound($aArray) / 2 - 1
        $aRes[$i][0] = $aArray[$i]
    Next

    ;Y
    $n = 0
    For $y = UBound($aArray) / 2 To UBound($aArray) - 1
        $aRes[$n][1] = $aArray[$y]
        $n += 1
    Next

EndFunc   ;==>_CreateArray2D


Func _Draw_Graph()

    _GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
    _GraphGDIPlus_Set_PenSize($Graph, 2)

    $First = True

    For $i = 0 To UBound($aRes) - 1

        $x = $aRes[$i][0]
        $y = $aRes[$i][1]

        If $x <> 0 And $y <> 0 Then

            If $First = True Then _GraphGDIPlus_Plot_Start($Graph, $x, $y)
            $First = False

            _GraphGDIPlus_Plot_Line($Graph, $x, $y)
            ;Цвет для точки
            _GraphGDIPlus_Set_PenColor($Graph, 0xFFFFFF00)
            _GraphGDIPlus_Set_PenSize($Graph, 5)
            ;Рисуем точку
            _GraphGDIPlus_Plot_Point($Graph, $x, $y)
            ;Цвет для линии
            _GraphGDIPlus_Set_PenColor($Graph, 0xFFB22222)
            _GraphGDIPlus_Set_PenSize($Graph, 2)

            _GraphGDIPlus_Refresh($Graph)

        EndIf

    Next

EndFunc   ;==>_Draw_Graph


Func _Exit()
    _GraphGDIPlus_Delete($GUI, $Graph)
    Exit
EndFunc   ;==>_Exit
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
Да так лучше :smile:

ra4o есть еще проблема. Если на точке (или на нескольких) значения по x,y равны нулю график все равно рисует линию. В результате получается искажение графика. Можно сделать так чтобы, если значения по x или y равны нулю, то в этом месте ничего не рисовать. Как бы, если НОЛЬ то точки не существует мы ее пропускаем.
 

ra4o

Скриптер
Сообщения
985
Репутация
180
Исправил в предыдущем посте.
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
Отлично, работает! Спасибо ra4o :smile:
Для полного счастья осталось динамику масштаба по шкале Y реализовать.
 

ra4o

Скриптер
Сообщения
985
Репутация
180
По шкале подстраивал в первом варианте - и по Х и по У , но там не очень красиво получалось... Могу предложить такой вариант : Ищем максимальное значение У и растягиваем шкалу дискретно , например - мах У 0.5 шкала будет от 0 до 1 , мах значение 1.8 - шкала от 0 до 2 итд, только Вы укажите возможные знания У. Можно, конечно ещё вариант - к макс. значению У прибавить , например 10% (что-бы точка на край графика не ложилась) , округлить в большую сторону и получить мах значение шкалы . Выбирайте
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
Можно, конечно ещё вариант - к макс. значению У прибавить , например 10% (что-бы точка на край графика не ложилась) , округлить в большую сторону и получить мах значение шкалы .
Да попробуй так сделать.
 

ra4o

Скриптер
Сообщения
985
Репутация
180
Исправил в ответе №10 .
 
Автор
G

Genics

Чайник
Сообщения
28
Репутация
0
Сейчас динамика красиво работает только по целым числам, и все выглядит нормально и сетка тоже когда значения по Y равны 2,3,4 и т. д. Но когда максимальное значение шкалы по Y равно 1 надо шкалу и сетку побольше поделить, иначе вид пустой получается. Лучше на 10 линий сетки и чтобы на шкале было: 0; 0,1; 0,2; 0,3; 0,4; 0,5; 0,6; 0,7; 0,8; 0,9; 1;

Еще заметил, что максимальное значение шкалы меньше чем 1 не падает. У меня в файле первое значение по Y равно 0,45, значит было бы лучше если в этом случае мах значение шкалы стало 0,5. Тогда тоже чтобы не было пустоты надо шкалу поделить на 5 линий сетки и шкалу так: 0; 0,1; 0,2; 0,3; 0,4; 0,5;
 

ra4o

Скриптер
Сообщения
985
Репутация
180
Еще заметил, что максимальное значение шкалы меньше чем 1 не падает
Да, максимальное значение шкалы может быть только целым числом - кратно единице .
 
Верх