Что нового

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

Genics

Новичок
Сообщения
34
Репутация
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

Скриптер
Сообщения
1 003
Репутация
184
Рисовалка Вашего графика (пока без динамики)
Код:
#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

Новичок
Сообщения
34
Репутация
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

Скриптер
Сообщения
1 003
Репутация
184
Так ?
Код:
#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 115
Репутация
520
OffTopic:
Genics [?]
Поэтому надо поработать над
Поэтому можно не стесняться и предложить человеку денежку за работу. тогда работа будет идти веселее и все будет красивее.
 
Автор
G

Genics

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

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

Вложения

ra4o

Скриптер
Сообщения
1 003
Репутация
184
Пробуйте
Код:
#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

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

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

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

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

ra4o

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

Genics

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

ra4o

Скриптер
Сообщения
1 003
Репутация
184
Все таки надо чтобы значения сравнивал.
Тогда, можно так :
Код:
#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

Новичок
Сообщения
34
Репутация
0
Да так лучше :smile:

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

ra4o

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

Genics

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

ra4o

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

Genics

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

ra4o

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

Genics

Новичок
Сообщения
34
Репутация
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

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