purebasic.info

PureBasic forum
Текущее время: Пн дек 17, 2018 5:52 pm

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 28 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: Линейный график
СообщениеДобавлено: Пт окт 05, 2018 12:39 pm 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
Линейный график с авто-масштабом, многоканальный.(модуль)
Код будет обновляться по мере развития.

ChartMod.pbi:
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
;*********************************
; Module:   CHART
; Author:   void
; Compiler: PB546
; Ver:      016
;
; линейный график с авто-масштабом на несколько линий.
;*********************************
DeclareModule CHART
  EnableExplicit
  Enumeration LabelXIsTime
    #XIdxNum    ;X - номера индексов
    #XTimeSec   ;X - время в секундах
    #XTimeMin   ;X - время в минутах
  EndEnumeration
  ;Открыть график в отдельном, модальном окне.
  ;Параметры: Заголовок, Ширина, Высота, Массив_Значений, Массив_Цветов, Флаг_глобального_масштаба(масштаб по всему диапозону),
  ;LabelXIsTime - Отображение шкалы по X.
  Declare Open(Title$, Width, Height, Array Values.d(2), Array Colors.l(2), GlobLoc = #False, LabelXIsTime = #XIdxNum)
  ;Открыть график в своем окне в виде гаджета.
  Declare OpenChild(Windows, X, Y, Width, Height, Array Values.d(2), Array Colors.l(2), GlobLoc = #False, LabelXIsTime = #XIdxNum)
  ;Обновить данные присоединенного графика.
  Declare Update(Windows, Chart, Array Values.d(2), Array Colors.l(2))
  ;Удалить присоединенный график.
  Declare FreeChild(Windows, Chart)
 
EndDeclareModule
 
Module CHART
  EnableExplicit
  #FontName$    = "Lucida Console" ;шрифт для меток, должен быть ТТ и моноширинный
  #FontSize     = 8                ;размер шрифта
  #GridColor    = $E0E0E0          ;цвет сетки
  #BackColor    = #White
  #FrameColor   = #Black
  #LabelXColor  = #Black
  #LabelYColor  = #Black
  #LabelSColor  = #Black
  #BackTTColor  = #White           ;ToolTip цвет фона
  #LabelTTColor = #Black           ;ToolTip цвет текста
  #MouseWheelX  = 100              ;чувствительность колеса мыши (за 1 шаг)
  #WidthGrid    = 30               ;горизонтальный шаг сетки
  #HeightGrid   = 30               ;вертикальный шаг сетки
  #DashSize     = 4                ;размер "засечек" к меткам
  #DigitsY      = 6                ;количество значащих цифр в метках Y
  #ToolTipDelay = 10               ;задержка всплывающей подсказки в десятых долях секунды
 
  Structure ChartContext
    HWnd.i
    Canvas.i
    Drawed.i
    CurY.i
    CurX.i
    OldX.i
    LknDwn.i
    DeltaX.i
    StartIdx.i
    GlobMinMax.i
    LabelXMode.i
    ScaleXMul.i
    ScaleXDiv.i
    IsFirstStep.i
    ToolTipSet.i
    GMax.d
    GMin.d
    Array d.d(0,0)
    Array c.l(0,0)
  EndStructure
 
  Global NewList Context.ChartContext(), LastCanvas, Timer, TimerCount, CanvasCount, Font
 
  Procedure Max(a, b)
    If a > b
      ProcedureReturn a
    EndIf
    ProcedureReturn b
  EndProcedure
 
  Procedure SetContextG()
    With Context()  
      ForEach(Context())
        If \Canvas = LastCanvas
          ProcedureReturn
        EndIf
      Next
    EndWith
  EndProcedure  
 
  Procedure SetContextWG(Windows, Gadget)
    With Context()
      ForEach(Context())
        If \HWnd = Windows And \Canvas = Gadget
          ProcedureReturn #True
        EndIf
      Next
    EndWith
  EndProcedure  
 
  Procedure SetContext()
    SetContextWG(EventWindow(), EventGadget())
  EndProcedure  
 
  Procedure DrawGraph(ToolTip = #False)
    Protected   Width, Height, WidthBorder, HeightBorder, TxtWidthX, TxtWidthY, TxtHeight, RoundHeight
    Protected.d Max, Min, Multipler, Shifter, Y1, Y2, LabelY
    Protected   s$, i, j, EndIdx, DigitsX, LenY, LabelX, WorkBorder
    #MinDouble  = -1e308
    #MaxDouble  = 1e308
    With Context()
     
      StartDrawing(CanvasOutput(\Canvas))
      DrawingFont(FontID(Font))
     
      ;вычисляем размеры подписей и из них, размеры бордюров
      s$ =  LSet("", #DigitsY, "0")  + " +. "       ;длина правой подписи +знак +точка +2 пробела по бокам
      LenY          = Len(s$)                       ;полная длина меток по Y в символах
      TxtWidthY     = TextWidth(s$)                 ;размер меток по Y
      TxtHeight     = TextHeight(s$)                ;вертикальный размер меток (для центровки)
      WidthBorder   = TxtWidthY + #DashSize         ;размер правого бордюра с учетом "засечек"
      Select \LabelXMode
        Case #XTimeSec
          s$ = "00:00:00"                           ;временная метка в секундах
        Case #XTimeMin
          s$ = "00:00"                              ;временная метка в минутах
        Default;#XIdxNum
          s$ = Str(ArraySize(\d(),2))               ;X - номера индексов
      EndSelect
      DigitsX       = Len(s$)                       ;максимальная длина меток по X в символах
      TxtWidthX     = TextWidth(s$ + "  ")          ;размер меток по X (с учетом отступов в 2 пробела)
      HeightBorder  = TxtWidthX + #DashSize         ;размер нижнего бордюра с учетом "засечек"
      Width         = OutputWidth()  - WidthBorder  ;ширина рабочей зоны
      Height        = OutputHeight() - HeightBorder ;высота рабочей зоны
      RoundHeight   = Height - Height % #HeightGrid ;высота рабочей зоны округленная до полных ячеек
     
      ;если отступ до нижней границы меньше удвойнной высоты подписи - сокращаем масштаб на 1 шаг сетки
      If Height - RoundHeight < TxtHeight * 2: RoundHeight - #HeightGrid: EndIf
     
      ;отступы сверху и снизу
      WorkBorder = (Height - RoundHeight) / 2
     
      ;рисуем экран и рамку
      Box(0, 0, OutputWidth(), OutputHeight(), #BackColor)
      LineXY(0, 0, OutputWidth() - 1, 0, #FrameColor)
      LineXY(0, 0, 0, OutputHeight() - 1, #FrameColor)
      LineXY(Width, Height, Width, 0, #FrameColor)
      LineXY(Width, Height, 0, Height, #FrameColor)
      LineXY(0, OutputHeight() - 1, OutputWidth() - 1, OutputHeight() - 1, #FrameColor)
      LineXY(OutputWidth() - 1, 0, OutputWidth() - 1, OutputHeight() - 1, #FrameColor)
     
      ;корректируем начальный индекс, на выход из диапазона
      If \StartIdx > ArraySize(\d(),2) - Width * \ScaleXDiv / \ScaleXMul
        \StartIdx = ArraySize(\d(),2) - Width * \ScaleXDiv / \ScaleXMul
      EndIf
      If \StartIdx < 0: \StartIdx = 0: EndIf
     
      ;глобальные экстремумы вычисляем по всему массиву 1 раз
      If \IsFirstStep
        \GMax = #MinDouble: \GMin = #MaxDouble
        For j = 0 To ArraySize(\d(),1)
          For i = 0 To ArraySize(\d(),2)
            If \GMax < \d(j,i): \GMax = \d(j,i): EndIf
            If \GMin > \d(j,i): \GMin = \d(j,i): EndIf
          Next
        Next    
      EndIf
     
      ;ищем минимальное и максимальное значение
      ;если масштаб локальный - ищем по видимому диапозону
      If \GlobMinMax
        Max = \GMax: Min = \Gmin
      Else
        EndIdx = \StartIdx + Width * \ScaleXDiv / \ScaleXMul
        If EndIdx > ArraySize(\d(),2): EndIdx = ArraySize(\d(),2): EndIf    
        Max = #MinDouble: Min = #MaxDouble
        For j = 0 To ArraySize(\d(),1)
          For i = \StartIdx To EndIdx
            If Max < \d(j,i): Max = \d(j,i): EndIf
            If Min > \d(j,i): Min = \d(j,i): EndIf
          Next    
        Next      
      EndIf
     
      ;вычисляем текущий масштаб для вписывания в окно по Y
      If Min <> Max
        Multipler = RoundHeight / (Max - Min)
      Else
        Multipler = RoundHeight
      EndIf
      Shifter = Min * Multipler + RoundHeight + WorkBorder    
     
      ;рисуем вертикальные линии сетки
      i = #WidthGrid - (\StartIdx * \ScaleXMul / \ScaleXDiv) % #WidthGrid
      While i < Width
        LineXY(i, 1, i, Height - 1, #GridColor)
        i + #WidthGrid
      Wend
 
      ;рисуем горизонтальные линии сетки
      i = WorkBorder
      While i < Height  
        LineXY(1, i, Width - 1, i, #GridColor)
        i + #HeightGrid
      Wend
     
      ;рисуем засечки и подписи снизу
      i = #WidthGrid - (\StartIdx * \ScaleXMul / \ScaleXDiv) % #WidthGrid
      While i <= Width
        LineXY(i, Height, i, Height + #DashSize, #FrameColor)  ;засечка
        LabelX = \StartIdx + i * \ScaleXDiv / \ScaleXMul
        Select \LabelXMode
          Case #XTimeSec
            s$ = FormatDate(" %hh:%ii:%ss" , LabelX)           ;представляем в виде времени в секундах
          Case #XTimeMin
            s$ = FormatDate(" %hh:%ii" , LabelX * 60)          ;представляем в виде времени в минутах
          Default                                              ;#XIdxNum
            s$ = RSet(Str(LabelX), DigitsX + 1)                ;добиваем пробелами слева
        EndSelect
        DrawRotatedText(i - TxtHeight / 2, Height + HeightBorder, s$, 90, #LabelXColor)
        i + #WidthGrid
      Wend
     
      ;рисуем засечки и подписи справа
      i = WorkBorder
      While i < Height  
        LineXY(Width, i, Width + #DashSize, i, #FrameColor) ;засечка
        LabelY = (Shifter - i) / Multipler
        If LabelY < 0
          s$ = " " + Left(StrD(LabelY, #DigitsY - 1), #DigitsY + 2)
        Else
          s$ = " +" + Left(StrD(LabelY, #DigitsY - 1), #DigitsY + 1)
        EndIf
        DrawText(Width + #DashSize, i - TxtHeight / 2, s$, #LabelYColor, #BackColor)
        i + #HeightGrid
      Wend
     
      ;информация по масштабу в нижнем правом углу
      If LenY > 7
        If \ScaleXMul > 1
          s$ = " X:" + Str(\ScaleXMul)
        Else
          s$ = " X:1/" + Str(\ScaleXDiv)
        EndIf
        DrawText(Width + #DashSize, Height + #DashSize, s$, #LabelSColor, #BackColor)
        If \GlobMinMax
          s$ = " Y:Glob"
        Else
          s$ = " Y:Loc"
        EndIf
        DrawText(Width + #DashSize, Height + #DashSize + TxtHeight + TxtHeight / 2, s$, #LabelSColor, #BackColor)
      EndIf
     
      ;если данных меньше ширины окна
      EndIdx = Width * \ScaleXDiv / \ScaleXMul - 1
      If EndIdx > ArraySize(\d(),2) - 1
        EndIdx = ArraySize(\d(),2) - 1
      EndIf
     
      ;рисуем сам график
      For j = 0 To ArraySize(\d(),1)
        For i = 0 To EndIdx
          Y1 = Shifter - \d(j, \StartIdx + i) * Multipler
          Y2 = Shifter - \d(j, \StartIdx + i + 1) * Multipler
          LineXY(i * \ScaleXMul / \ScaleXDiv, Y1, (i + 1) * \ScaleXMul / \ScaleXDiv, Y2, \c(j, \StartIdx + i))
        Next
      Next
     
      ;Рисуем ToolTip
      Protected TTLeft, TTTop, TTHeight, TTWidth
      TTWidth = Max(TxtWidthX, TxtWidthY) + TextWidth("X2:")
      If ToolTip And \CurX >= 0 And \CurX <= Width And \CurY > 0 And \CurY < Height And
         (\StartIdx + \CurX * \ScaleXDiv / \ScaleXMul) <= ArraySize(\d(),2)
        TTHeight = (TxtHeight + #DashSize) * (ArraySize(\d(),1) + 2) + #DashSize
        If \CurX < Width / 2  ;курср левее середины
          TTLeft = \CurX + 10
        Else
          TTLeft = \CurX - TTWidth - 10
        EndIf
        If \CurY < Height / 2 ;курсор выше середины
          TTTop = \CurY + 10
        Else
          TTTop = \CurY - TTHeight - 10
        EndIf
        Box(TTLeft, TTTop, TTWidth, TTHeight, #BackTTColor)
        LineXY(TTLeft, TTTop, TTLeft + TTWidth, TTTop, #FrameColor)
        LineXY(TTLeft + TTWidth, TTTop, TTLeft + TTWidth, TTTop + TTHeight, #FrameColor)
        LineXY(TTLeft + TTWidth, TTTop + TTHeight, TTLeft, TTTop + TTHeight, #FrameColor)
        LineXY(TTLeft, TTTop + TTHeight, TTLeft, TTTop, #FrameColor)
        LabelX = \StartIdx + \CurX * \ScaleXDiv / \ScaleXMul
        Select \LabelXMode
          Case #XTimeSec
            s$ = FormatDate("%hh:%ii:%ss" , LabelX)           ;представляем в виде времени в секундах
          Case #XTimeMin
            s$ = FormatDate("%hh:%ii" , LabelX * 60)          ;представляем в виде времени в минутах
          Default ;#XIdxNum
            s$ = Str(LabelX)                                  ;в виде номера индекса
        EndSelect
        DrawText(TTLeft + #DashSize, TTTop + #DashSize, "X : " + s$, #LabelTTColor, #BackTTColor)
       
        For i = 0 To ArraySize(\d(),1)
          LabelY = \d(i, \StartIdx + \CurX * \ScaleXDiv / \ScaleXMul)
          If LabelY < 0
            s$ = "Y" + Str(i + 1) + ":" + Left(StrD(LabelY, #DigitsY - 1), #DigitsY + 2)
          Else
            s$ = "Y" + Str(i + 1) + ":+" + Left(StrD(LabelY, #DigitsY - 1), #DigitsY + 1)
          EndIf          
          DrawText(TTLeft + #DashSize, TTTop + #DashSize + (#DashSize + TxtHeight) * (i + 1), s$, #LabelTTColor, #BackTTColor)
        Next
      EndIf
     
      StopDrawing()
      ProcedureReturn \StartIdx
    EndWith
  EndProcedure
 
  Procedure CheckDraw()
    With Context()
      If \LknDwn And \OldX <> \CurX
        \DeltaX + \OldX - \CurX: \OldX = \CurX
      EndIf
      If \DeltaX * \ScaleXDiv / \ScaleXMul
        \StartIdx + \DeltaX * \ScaleXDiv / \ScaleXMul
        \DeltaX = 0: \Drawed = #False
      EndIf
      If Not \Drawed
        \StartIdx = DrawGraph()
        \IsFirstStep = #False: \Drawed = #True
      EndIf
    EndWith
  EndProcedure  
 
  Procedure IsResizedWindows()
    SetContext()
    With Context()
      ResizeGadget(\Canvas, 0, 0, WindowWidth(\HWnd), WindowHeight(\HWnd))
      \Drawed = #False: CheckDraw()
    EndWith
  EndProcedure
 
  Procedure IsLkMouseDown()
    SetContext()
    With Context()
      \OldX = GetGadgetAttribute(\Canvas, #PB_Canvas_MouseX)
      \LknDwn = #True: CheckDraw()
    EndWith
  EndProcedure
 
  Procedure IsLkMouseUp()
    SetContext()
    With Context()
      \LknDwn = #False
    EndWith
  EndProcedure
 
  Procedure IsMouseMove()
    SetContext()
    With Context()
      \CurX = GetGadgetAttribute(\Canvas, #PB_Canvas_MouseX)
      \CurY = GetGadgetAttribute(\Canvas, #PB_Canvas_MouseY)
      CheckDraw()
      TimerCount = 0
      If \ToolTipSet
        DrawGraph()
        \ToolTipSet = #False
      EndIf
    EndWith
  EndProcedure
 
  Procedure IsMouseEnter()
    SetContext()
    With Context()
      SetActiveGadget(\Canvas)
      LastCanvas = \Canvas
    EndWith
  EndProcedure
 
  Procedure IsWindowsTimer()
    SetContextG()
    With Context()
      TimerCount + 1
      If TimerCount = #ToolTipDelay And Not \ToolTipSet
        DrawGraph(#True)
        \ToolTipSet = #True
      EndIf
    EndWith
  EndProcedure
 
  Procedure IsMouseWheel()
    SetContext()
    With Context()
      \DeltaX + GetGadgetAttribute(\Canvas, #PB_Canvas_WheelDelta) * #MouseWheelX * \ScaleXMul / \ScaleXDiv
      CheckDraw()
    EndWith
  EndProcedure
 
  Procedure IsKeyDown()
    SetContext()
    With Context()
      Select GetGadgetAttribute(\Canvas, #PB_Canvas_Key)
        Case #PB_Shortcut_Left
          \DeltaX - #WidthGrid; * \ScaleXMul / \ScaleXDiv
        Case #PB_Shortcut_Right  
          \DeltaX + #WidthGrid; * \ScaleXMul / \ScaleXDiv
        Case #PB_Shortcut_Up
          \DeltaX - GadgetWidth(\Canvas) / 2
        Case #PB_Shortcut_Down
          \DeltaX + GadgetWidth(\Canvas) / 2
        Case #PB_Shortcut_Add, #VK_OEM_PLUS
          If \ScaleXDiv = 1
            \ScaleXMul + 1
            ;не больше шага сетки
            If \ScaleXMul > #WidthGrid: \ScaleXMul = #WidthGrid: EndIf  
            ;подгонка под ближайшее кратное
            While #WidthGrid % \ScaleXMul: \ScaleXMul + 1: Wend    
          Else
            \ScaleXDiv / 2
          EndIf
          \Drawed = #False
        Case #PB_Shortcut_Subtract, #VK_OEM_MINUS
          If \ScaleXMul > 1
            \ScaleXMul - 1
            ;подгонка под ближайшее кратное
            While #WidthGrid % \ScaleXMul: \ScaleXMul - 1: Wend
          Else
            ;если отображение больше окна - можно сжимать еще
            If ArraySize(\d(),2) / \ScaleXDiv > GadgetWidth(\Canvas): \ScaleXDiv * 2: EndIf
          EndIf
          \Drawed = #False
        Case #PB_Shortcut_0, #PB_Shortcut_Insert
          \ScaleXMul = 1: \ScaleXDiv = 1: \Drawed = #False
        Case #PB_Shortcut_Multiply, #PB_Shortcut_8
          \GlobMinMax = 1 - Bool(\GlobMinMax)
          \Drawed = #False
      EndSelect
      CheckDraw()
    EndWith
  EndProcedure  
 
  Procedure Update(Windows, Chart, Array Values.d(2), Array Colors.l(2))
    SetContextWG(Windows, Chart)
    With Context()
      CopyArray(Values(),\d()): CopyArray(Colors(),\c())
      \ScaleXMul = 1: \ScaleXDiv = 1: \IsFirstStep = #True: \Drawed = #False
      CheckDraw()
    EndWith
  EndProcedure  
 
  Procedure OpenChild(Windows, X, Y, Width, Height, Array Values.d(2), Array Colors.l(2), GlobLoc = #False, LabelXIsTime = #XIdxNum)
    With Context()
      AddElement(Context())
      CanvasCount + 1
      \HWnd = Windows
      \Canvas = CanvasGadget(#PB_Any, X, Y, Width, Height, #PB_Canvas_Keyboard);|#PB_Canvas_DrawFocus)
      SetGadgetAttribute(\Canvas, #PB_Canvas_Cursor, #PB_Cursor_Cross)
      If Not Font
        Font = LoadFont(#PB_Any, #FontName$, #FontSize)
        If Not IsFont(Font)
          MessageRequester("ERROR!", "Unable to load the " + #DQUOTE$ + #FontName$ + #DQUOTE$ + " font!", #MB_ICONERROR)
          End
        EndIf
      EndIf
      SetActiveGadget(\Canvas)
      CopyArray(Values(),\d()): CopyArray(Colors(),\c()): \GlobMinMax = GlobLoc: \LabelXMode = LabelXIsTime
      \ScaleXMul = 1: \ScaleXDiv = 1: \IsFirstStep = #True: \Drawed = #False: CheckDraw()
      BindEvent(#PB_Event_Gadget, @IsLkMouseDown(), \HWnd, \Canvas, #PB_EventType_LeftButtonDown)
      BindEvent(#PB_Event_Gadget, @IsLkMouseUp(), \HWnd, \Canvas, #PB_EventType_LeftButtonUp)
      BindEvent(#PB_Event_Gadget, @IsMouseMove(), \HWnd, \Canvas, #PB_EventType_MouseMove)
      BindEvent(#PB_Event_Gadget, @IsMouseEnter(), \HWnd, \Canvas, #PB_EventType_MouseEnter)
      BindEvent(#PB_Event_Gadget, @IsMouseWheel(), \HWnd, \Canvas, #PB_EventType_MouseWheel)
      BindEvent(#PB_Event_Gadget, @IsKeyDown(), \HWnd, \Canvas, #PB_EventType_KeyDown)
      If Not Timer
        Timer = \HWnd
        AddWindowTimer(\HWnd, Timer, 100)
        BindEvent(#PB_Event_Timer, @IsWindowsTimer(), \HWnd, Timer)
      EndIf
      ProcedureReturn \Canvas
    EndWith
  EndProcedure
 
  Procedure Open(Title$, Width, Height, Array Values.d(2), Array Colors.l(2), GlobLoc = #False, LabelXIsTime = #XIdxNum)
    With Context()
      Protected HWnd, Chart
      ;AddElement(Context()): CanvasCount + 1
      HWnd = OpenWindow(#PB_Any, 0, 0, Width, Height, Title$ +
                        " (Navigate: Up, Down, MouseDrag, Wheel | ScaleXMul: +, -, 0 | ScaleY Global/Local: *)",
                        #PB_Window_SizeGadget|#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|
                        #PB_Window_MaximizeGadget|#PB_Window_ScreenCentered)
      Chart = OpenChild(HWnd, 0, 0, WindowWidth(HWnd), WindowHeight(HWnd), Values(), Colors(), GlobLoc, LabelXIsTime)
      BindEvent(#PB_Event_SizeWindow, @IsResizedWindows(), HWnd)
     
      Repeat: Until WaitWindowEvent() = #PB_Event_CloseWindow
     
      UnbindEvent(#PB_Event_SizeWindow, @IsResizedWindows(), HWnd)
      FreeChild(HWnd, Chart)
      CloseWindow(HWnd)
    EndWith
  EndProcedure
 
  Procedure FreeChild(Windows, Chart)
    If SetContextWG(Windows, Chart)
      With Context()
        UnbindEvent(#PB_Event_Gadget, @IsLkMouseDown(), \HWnd, \Canvas, #PB_EventType_LeftButtonDown)
        UnbindEvent(#PB_Event_Gadget, @IsLkMouseUp(), \HWnd, \Canvas, #PB_EventType_LeftButtonUp)
        UnbindEvent(#PB_Event_Gadget, @IsMouseMove(), \HWnd, \Canvas, #PB_EventType_MouseMove)
        UnbindEvent(#PB_Event_Gadget, @IsMouseEnter(), \HWnd, \Canvas, #PB_EventType_MouseEnter)
        UnbindEvent(#PB_Event_Gadget, @IsMouseWheel(), \HWnd, \Canvas, #PB_EventType_MouseWheel)
        UnbindEvent(#PB_Event_Gadget, @IsKeyDown(), \HWnd, \Canvas, #PB_EventType_KeyDown)
        CanvasCount - 1
        If Not CanvasCount ;если графиков больше нет - таймер и фонт не нужны
          UnbindEvent(#PB_Event_Timer, @IsWindowsTimer(), \HWnd, Timer)
          RemoveWindowTimer(\HWnd , Timer): Timer = 0
          FreeFont(Font): Font = 0
        EndIf
        Dim \d(0,0): Dim \c(0,0)
        FreeGadget(\Canvas)
        DeleteElement(Context())
      EndWith
    EndIf
  EndProcedure
 
EndModule
 
 
;Test Module
CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
 
  Procedure SinCos(Array d.d(2), Array c.l(2), Faze=0)
    Protected i
    For i = 0 To ArraySize(d(),2)
      d(0,i) = Sin(Radian(i + Faze))
      If d(0,i) > 0
        c(0,i) = #Red
      Else
        c(0,i) = #Blue
      EndIf
      d(1,i) = Cos(Radian(i + Faze))
      If d(1,i) > 0
        c(1,i) = #Green
      Else
        c(1,i) = #Magenta
      EndIf
    Next
  EndProcedure
 
 
  #MinOfDay = 24 * 60 * 30
 
  Dim d.d(1, #MinOfDay - 1)
  Dim c.l(1, #MinOfDay - 1)
 
  SinCos(d(), c())
  CHART::Open("Single Modal Windows", 800, 620, d(), c(), #True, CHART::#XTimeMin)
 
  Define Event, Win, Chart1, Chart2
  Win = OpenWindow(#PB_Any, 0, 0, 800, 600, "Test Windows", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
  ButtonGadget(0,5,5,60,20,"Faze 90")
  ButtonGadget(1,5,40,60,20,"Faze 0")
  ButtonGadget(2,5,70,60,20,"Hide")
  ButtonGadget(3,5,100,60,20,"UnHide")
  ButtonGadget(4,5,130,60,20,"Free2")
 
  Chart1 = CHART::OpenChild(Win, 80, 10, 350, 580, d(), c(), #True, CHART::#XIdxNum)
  Chart2 = CHART::OpenChild(Win, 440, 10, 350, 580, d(), c(), #True, CHART::#XIdxNum)
 
  Repeat
    Event = WaitWindowEvent()
    ;Debug Event
    Select Event
      Case  #PB_Event_Gadget
        Select EventGadget()
          Case 0
            SinCos(d(), c(), 90)
            CHART::Update(Win, Chart1, d(), c())
          Case 1
            SinCos(d(), c(), 0)
            CHART::Update(Win, Chart1, d(), c())
          Case 2
            HideGadget(Chart1, #True)
          Case 3
            HideGadget(Chart1, #False)
          Case 4
            CHART::FreeChild(Win, Chart2)
          Default
           
        EndSelect
    EndSelect    
  Until Event = #PB_Event_CloseWindow
 
CompilerEndIf
 



Последний раз редактировалось Kuzmat Пт ноя 16, 2018 11:17 am, всего редактировалось 19 раз(а).

Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Пт окт 05, 2018 4:10 pm 
Не в сети
профессор

Зарегистрирован: Вс июл 05, 2009 5:55 pm
Сообщений: 345
Благодарил (а): 1 раз.
Поблагодарили: 13 раз.
Пункты репутации: 0
прикольно.
Правда нет ноля и его линии, она гуляет везде при маштабировании.
Сетка должна также маштабироваться и менять шаг если слишком мелкий шаг получился.
Вертикальная сетка(шаг) должна быть кратна какимто числам например
0.0
0.5
10.

или
0.0
0.125
.25
.5
.075
1.0
или гуще так проще считать параметры иначе без калькулятора никак :roll: .
По горизонту особо несмотрел, но бросается в глаза что когда числа большие получается слитная строка без пробелов.
А так ничё получилось :) , правда гдето видел велосипед на пурике и даже неодин.

_________________
искатель истины


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Пт окт 05, 2018 4:18 pm 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
Для моих целей, ноль не нужен (он вне диапазона), а нужно отслеживать относительные изменения, без особой привязки к уровням.

зы. макет - есть, раздел - "OpenSource", кому надо - тот допилит под свои хотелки...


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Пн ноя 12, 2018 12:12 pm 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
Глобально переписал, в целях упрощения логики работы, и за комментировал, что можно, и не нужно.
Ставил целью сделать полностью "резиновым", кажется удалось. :D
Константы в начале модуля можно менять на свой вкус (в разумных пределах)
Шкалы подстраиваются автоматом под размер шрифта.

зы. код в первом посте - обновлен.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Вт ноя 13, 2018 7:56 am 
Не в сети
профессор

Зарегистрирован: Вс авг 31, 2014 12:11 am
Сообщений: 457
Благодарил (а): 51 раз.
Поблагодарили: 20 раз.
Пункты репутации: 10
Kuzmat, спасибо большое за то, что поделился. Нужная вещь.
Если не сложно, то можно попросить по иксу разложить время? При масштабе 1:1 шаг = 30 мин (от 0:00 до 23:30).


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Вт ноя 13, 2018 11:44 am 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
knower писал(а):
При масштабе 1:1 шаг = 30 мин (от 0:00 до 23:30).

Если правильно понял, шаг сетки 30мин, соответственно шаг массива 1 минута.
Сейчас поправлю метки по Х и выложу... (или данных всего, по 1 выборке на 30мин ?)
Вот:
Код:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
;*********************************
; Module:   CHART
; Author:   void
; Compiler: PB546
; Ver:      006
;
; линейный график с авто-масштабом на несколько линий.
;*********************************
DeclareModule CHART
  EnableExplicit
  #FontName$    = "Lucida Console" ;шрифт для меток, должен быть ТТ и моноширинный
  #FontSize     = 8                ;размер шрифта
  #GridColor    = $E0E0E0          ;цвет сетки
  #MouseWheelX  = 100              ;чувствительность колеса мыши (за 1 шаг)
  #WidthGrid    = 30               ;горизонтальный шаг сетки
  #HeightGrid   = 30               ;вертикальный шаг сетки
  #DashSize     = 4                ;размер "засечек" к меткам
  #DigitsY      = 6                ;количество значащих цифр в метках Y
 
  ;Параметры: Заголовок, Ширина, Высота, Массив_Значений, Массив_Цветов, Флаг_глобального_масштаба(масштаб по всему диапозону)
  Declare Open(Title$,Width,Height,Array d.d(2), Array c.l(2), GlobMinMax = #False)
 
EndDeclareModule
Module CHART
  EnableExplicit
 
  Procedure DrawGraph(Canvas, StartIdx, ScaleXMul, ScaleXDiv, Font, Array d.d(2), Array c.l(2), GlobMinMax, IsFirstStep)
    Protected   Width, Height, WidthBorder, HeightBorder, TxtWidthX, TxtWidthY, TxtHeight, RoundHeight
    Protected.d Max, Min, Multipler, Shifter, Y1, Y2, LabelX, LabelY
    Protected   s$, i, j, EndIdx, DigitsX
    Static.d    GMax, GMin
    #MinDouble  = -1e308
    #MaxDouble  = 1e308
   
    StartDrawing(CanvasOutput(Canvas))
    DrawingFont(FontID(Font))
   
    ;вычисляем размеры подписей и из них, размеры бордюров
    s$ =  LSet("" , #DigitsY + 4, "0")            ;полная длина правой подписи +знак +точка +2 пробела по бокам
    TxtWidthY      = TextWidth(s$)                ;размер меток по Y
    TxtHeight     = TextHeight(s$)                ;вертикальный размер меток (для центровки)
    WidthBorder   = TxtWidthY + #DashSize         ;размер правого бордюра с учетом "засечек"
    ;s$ = Str(ArraySize(d(),2))                   ;максимальный размер подписей снизу"
    s$ = "00:00"                                  ;временная метка
    DigitsX = Len(s$)                             ;максимальная длина меток по X в символах
    TxtWidthX = TextWidth(s$ + "  ")              ;размер меток по X (с учетом отступов в 2 пробела)
    HeightBorder  = TxtWidthX + #DashSize         ;размер нижнего бордюра с учетом "засечек"
    Width         = OutputWidth()  - WidthBorder  ;ширина рабочей зоны
    Height        = OutputHeight() - HeightBorder ;высота рабочей зоны
    RoundHeight   = Height - Height % #HeightGrid ;высота рабочей зоны округленная до размера сетки
   
    ;если отступ до нижней границы меньше половины сетки - сокращаем масштаб на 1 шаг сетки
    If Height - RoundHeight < #HeightGrid / 2: RoundHeight - #HeightGrid: EndIf
   
    ;рисуем экран и рамку
    Box(0, 0, OutputWidth(), OutputHeight(), #White)
    LineXY(0, 0, Width, 0, #Black)
    LineXY(0, 0, 0, Height, #Black)
    LineXY(Width, Height, Width, 0, #Black)
    LineXY(Width, Height, 0, Height, #Black)
   
    ;корректируем начальный индекс, на выход из диапазона
    If StartIdx > ArraySize(d(),2) - Width * ScaleXDiv / ScaleXMul - 1
      StartIdx = ArraySize(d(),2) - Width * ScaleXDiv / ScaleXMul - 1
    EndIf
    If StartIdx < 0: StartIdx = 0: EndIf
   
    ;глобальные экстремумы вычисляем по всему массиву 1 раз
    If IsFirstStep
      GMax = #MinDouble: GMin = #MaxDouble
      For j = 0 To ArraySize(d(),1)
        For i = 0 To ArraySize(d(),2)
          If GMax < d(j,i): GMax = d(j,i): EndIf
          If GMin > d(j,i): GMin = d(j,i): EndIf
        Next
      Next    
    EndIf
   
    ;ищем минимальное и максимальное значение
    ;если масштаб локальный - ищем по видимому диапозону
    If GlobMinMax
      Max = GMax: Min = Gmin
    Else
      EndIdx = StartIdx + Width * ScaleXDiv / ScaleXMul
      If EndIdx > ArraySize(d(),2): EndIdx = ArraySize(d(),2): EndIf    
      Max = #MinDouble: Min = #MaxDouble
      For j = 0 To ArraySize(d(),1)
        For i = StartIdx To EndIdx
          If Max < d(j,i): Max = d(j,i): EndIf
          If Min > d(j,i): Min = d(j,i): EndIf
        Next    
      Next      
    EndIf
   
    ;вычисляем текущий масштаб для вписывания в окно по Y
    If Min <> Max
      Multipler = (RoundHeight - #HeightGrid) / (Max - Min)
    Else
      Multipler = (RoundHeight - #HeightGrid)
    EndIf
    Shifter = Min * Multipler + RoundHeight      
   
    ;рисуем вертикальные линии сетки
    i = #WidthGrid
    While i < Width
      LineXY(i, 1, i, Height - 1, #GridColor)
      i + #WidthGrid
    Wend
   
    ;рисуем горизонтальные линии сетки
    i = #HeightGrid
    While i < Height  
      LineXY(1, i, Width - 1, i, #GridColor)
      i + #HeightGrid
    Wend
   
    ;рисуем засечки и подписи снизу
    i = #WidthGrid
    While i < Width
      LineXY(i, Height, i, Height + #DashSize, #Black)  ;засечка
      LabelX = StartIdx + i * ScaleXDiv / ScaleXMul
      ;s$ = RSet(Str(LabelX), DigitsX + 1)               ;добиваем пробелами слева
      s$ = FormatDate(" %hh:%ii" , LabelX * 60)          ;представляем в виде времени
      DrawRotatedText(i - TxtHeight / 2, Height + HeightBorder, s$, 90, #Black)
      i + #WidthGrid
    Wend
   
    ;рисуем засечки и подписи справа
    i = #HeightGrid
    While i < Height  
      LineXY(Width, i, Width + #DashSize, i, #Black) ;засечка
      LabelY = (Shifter - i) / Multipler
      If LabelY < 0
        s$ = " " + Left(StrD(LabelY, #DigitsY - 1), #DigitsY + 2)
      Else
        s$ = " +" + Left(StrD(LabelY, #DigitsY - 1), #DigitsY + 1)
      EndIf
      DrawText(Width + #DashSize, i - TxtHeight / 2, s$, #Black, #White)
      i + #HeightGrid
    Wend
   
    ;информация по масштабу в нижнем правом углу
    If ScaleXMul > 1
      s$ = " SX:" + StrD(ScaleXMul)
    Else
      s$ = " SX:1/" + StrD(ScaleXDiv)
    EndIf
    DrawText(Width + #DashSize, Height + #DashSize, s$, #Black, #White)
    If GlobMinMax
      s$ = " SY:Glob"
    Else
      s$ = " SY:Loc"
    EndIf
    DrawText(Width + #DashSize, Height + #DashSize + TxtHeight * 1.5, s$, #Black, #White)
   
    ;если данных меньше ширины окна
    EndIdx = Width * ScaleXDiv / ScaleXMul - 1
    If EndIdx > ArraySize(d(),2) - 1
      EndIdx = ArraySize(d(),2) - 1
    EndIf
   
    ;рисуем сам график
    For j = 0 To ArraySize(d(),1)
      For i = 0 To EndIdx
        Y1 = Shifter - d(j, StartIdx + i) * Multipler
        Y2 = Shifter - d(j, StartIdx + i + 1) * Multipler
        LineXY(i * ScaleXMul / ScaleXDiv, Y1, (i + 1) * ScaleXMul / ScaleXDiv, Y2, c(j, StartIdx + i))
      Next
    Next
   
    StopDrawing()
    ProcedureReturn StartIdx
  EndProcedure
 
  Procedure Open(Title$, Width, Height, Array d.d(2), Array c.l(2), GlobMinMax = #False)
    Protected HWnd, Canvas, Event, Drawed, CurX, OldX, LknDwn, DeltaX, CurIdx, Font
    Protected ScaleXMul = 1, ScaleXDiv = 1, IsFirstStep = #True
    HWnd = OpenWindow(#PB_Any, 0, 0, Width, Height, Title$ +
                      " (Navigate: Up, Down, MouseDrag, Wheel | ScaleXMul: +, -, 0 | ScaleY Global/Local: *)",
                      #PB_Window_SizeGadget|#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|
                      #PB_Window_MaximizeGadget|#PB_Window_ScreenCentered)
    Canvas = CanvasGadget(#PB_Any, 0, 0, WindowWidth(HWnd), WindowHeight(HWnd), #PB_Canvas_Keyboard)
    SetGadgetAttribute(Canvas, #PB_Canvas_Cursor, #PB_Cursor_Cross)
    SetActiveGadget(Canvas)
    Font = LoadFont(#PB_Any, #FontName$, #FontSize)
    If Not IsFont(Font)
      MessageRequester("ERROR!", "Unable to load the " + #DQUOTE$ + #FontName$ + #DQUOTE$ + " font!", #MB_ICONERROR)
      End
    EndIf
    Repeat
      Event = WaitWindowEvent()
      Select Event
        Case #PB_Event_SizeWindow
          ResizeGadget(Canvas, 0, 0, WindowWidth(HWnd), WindowHeight(HWnd))
          Drawed = #False
        Case  #PB_Event_Gadget
          Select  EventType()
            Case #PB_EventType_LeftButtonDown
              OldX = GetGadgetAttribute(Canvas, #PB_Canvas_MouseX)
              LknDwn = #True
            Case #PB_EventType_LeftButtonUp
              LknDwn = #False
            Case #PB_EventType_MouseMove
              CurX = GetGadgetAttribute(Canvas, #PB_Canvas_MouseX)
            Case #PB_EventType_MouseWheel
              DeltaX = GetGadgetAttribute(Canvas, #PB_Canvas_WheelDelta) * #MouseWheelX * ScaleXDiv / ScaleXMul
            Case #PB_EventType_KeyDown
              Select GetGadgetAttribute(Canvas, #PB_Canvas_Key)
                Case #PB_Shortcut_Left
                  DeltaX - #WidthGrid * ScaleXDiv / ScaleXMul
                Case #PB_Shortcut_Right  
                  DeltaX + #WidthGrid * ScaleXDiv / ScaleXMul
                Case #PB_Shortcut_Up
                  DeltaX - WindowWidth(HWnd) * ScaleXDiv / ScaleXMul / 2
                Case #PB_Shortcut_Down
                  DeltaX + WindowWidth(HWnd) * ScaleXDiv / ScaleXMul / 2
                Case #PB_Shortcut_Add, #VK_OEM_PLUS
                  If ScaleXDiv = 1
                    ScaleXMul + 1
                    ;не больше шага сетки
                    If ScaleXMul > #WidthGrid: ScaleXMul = #WidthGrid: EndIf  
                    ;подгонка под ближайшее кратное
                    While #WidthGrid % ScaleXMul: ScaleXMul + 1: Wend    
                  Else
                    ScaleXDiv / 2
                  EndIf
                  Drawed = #False
                Case #PB_Shortcut_Subtract, #VK_OEM_MINUS
                  If ScaleXMul > 1
                    ScaleXMul - 1
                    ;подгонка под ближайшее кратное
                    While #WidthGrid % ScaleXMul: ScaleXMul - 1: Wend
                  Else
                    ;если отображение больше окна - можно сжимать еще
                    If ArraySize(d(),2) / ScaleXDiv > WindowWidth(HWnd): ScaleXDiv * 2: EndIf
                  EndIf
                  Drawed = #False
                Case #PB_Shortcut_0, #PB_Shortcut_Insert
                  ScaleXMul = 1: ScaleXDiv = 1: Drawed = #False
                Case #PB_Shortcut_Multiply, #PB_Shortcut_8
                  GlobMinMax = 1 - Bool(GlobMinMax)
                  Drawed = #False
              EndSelect
          EndSelect
      EndSelect
      If LknDwn And OldX <> CurX
        DeltaX = OldX - CurX: OldX = CurX
      EndIf
      If DeltaX
        CurIdx + DeltaX: DeltaX = 0: Drawed = #False
      EndIf
      If Not Drawed
        CurIdx = DrawGraph(Canvas, CurIdx, ScaleXMul, ScaleXDiv, Font, d(), c(), GlobMinMax, IsFirstStep)
        IsFirstStep = #False: Drawed = #True
      EndIf
    Until Event = #PB_Event_CloseWindow
    FreeFont(Font)
    FreeGadget(Canvas)
    CloseWindow(HWnd)
  EndProcedure
 
EndModule
 
 
;Test Module
CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
  #MinOfDay = 24 * 60 - 1
  Define i
  Dim d.d(1, #MinOfDay)
  Dim c.l(1, #MinOfDay)
 
  For i = 0 To #MinOfDay
   
    d(0,i) = Sin(Radian(i))
    If d(0,i) > 0
      c(0,i) = #Red
    Else
      c(0,i) = #Blue
    EndIf
   
    d(1,i) = Cos(Radian(i))
    If d(1,i) > 0
      c(1,i) = #Green
    Else
      c(1,i) = #Magenta
    EndIf
   
  Next
 
  CHART::Open("Sin - Cos Global Scale ",800,620,d(),c(),#True)
 
CompilerEndIf
 



Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Вт ноя 13, 2018 1:00 pm 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
Код в 1 посте обновлен, добавлен доп. параметр как отображать шкалу Х:
0 - номера индексов.
1 - время в секундах.
2 - время в минутах.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 4:52 am 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
Код в 1 посте обновлен, теперь можно добавлять график в свое окно, типа умного гаджета (свои эвенты обрабатывает сам).
Но есть одно неприятное ограничение... график может быть только один, по причине: глобальные параметры.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 9:24 am 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
Код в 1 посте обновлен, теперь графиков может быть произвольное кол-во.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 9:48 am 
Не в сети
профессор

Зарегистрирован: Вс авг 31, 2014 12:11 am
Сообщений: 457
Благодарил (а): 51 раз.
Поблагодарили: 20 раз.
Пункты репутации: 10
Kuzmat, очень хорошо!
Продолжительное время отсутствовал, но интерес к этой теме вовсе не потерян.
Цитата:
Если правильно понял, шаг сетки 30мин, соответственно шаг массива 1 минута.
Сейчас поправлю метки по Х и выложу... (или данных всего, по 1 выборке на 30мин ?)

Не совсем так (относительно выборки).
Ситуация такая: читается файл с данными за сутки (может и не за полные, допустим за сегодняшний полдень. Не суть.). Достоверно известно, что данные (начиная от 0:00) с периодом в 30 минут точно будут содержаться в читаемом файле. Следовательно эти точки будут отмечены на графике (собственно отсюда и взят такой временной интервал, как некий стандарт). И здесь шаг сетки тоже должен быть равен 30-ти, так как также есть вероятность того, что данные в файле будут следовать за период меньший чем 30 мин. (минимум 1 мин.). В таком случае (при масштабе, равном 1:1) график получается очень плотным и становится проблематично рассмотреть значение за конкретное время (в 30-минутном интервале, одном шаге сетки). Вот здесь и спасает масштабирование. Получается что при максимальном увеличении (если можно так сказать) в один шаг сетки(остаётся неизменным) укладывается ровно 1 минута данных. Что есть очень хорошо и читабельно.

Из вышесказанного следует, что на счёт конечной точки графика в 23:30 - погорячился. Нужна более полная развёртка по x от 0:00 до 23:59.
При прокрутке графика "колесом", либо "протягивании" замечен интересный эффект: время сбивается (не выдерживается 30-ти минутный интервал). Вместо этого рисуются "левые" минуты.

Очень ненавязчиво.
Может по игреку пользователю тоже дать некоторую свободу выбора? Допустим дать возможность заполнить список/массив своими цифрами (в пределах которых будет рисоваться график), а где-нибудь повыше надпись, что мол за величина измеряется. При этом "растягивать" только по шкале x. Так как в моём случае, например, в файле хранятся несколько величин, некоторые из которых изменяются в очень широких пределах (от нуля до нескольких тысяч). Понятное дело, что "уложить" это всё детально не получится, какой бы ни был выбран шаг сетки. Да ещё при масштабировании кратность соблюдать.

Ещё одно немаловажное дополнение/пожелание: не нужно отмечать все пики и провалы, но такие две точки как минимум и максимум будут совсем не лишними. Хотя бы просто отметить жирными точками.

P.S. В любом случае(будут реализованы "хотелки" али нет) ещё раз спасибо за работу!


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 10:54 am 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
knower Спасибо за интерес, комментарии и предложения.
1) Ваш случай (разряженные данные):
Написать график для такого случая можно, хотя и сложновато, да и надо-ли,
проще "приготовить" данные для "линейного" графика: заполнить тем что есть поминутный массив,
и дополнить пропуски предыдущими значениями, или линейно-усредненными (как правильнее в вашем случае, решать вам).

2) Сбивки по шкале были в какой-то из предыдущих версий, проверьте на новой, я не замечаю

3) Про несколько линий с сильно разными величинами: (например 1 - болтается возле 0, 2 - скачет до тысяч)
Тут прямо не знаю как лучше сделать...
а) возможен независимый масштаб каждой линии... и тогда нужно несколько шкал по Y
б) возможно сделать переключаемый выбор по какой линии масштаб (вторая улетит за горизонт, или сожмется до прямой)
в) Сделать возможным ручной масштаб, кнопками (как по X)
г) не париться и оставить как есть, на совесть того кто "готовит" данные для графика.

4) Пики, провалы, и другие метки:
Я не зря сделал массив цветов. Кривая может изменить цвет в любой момент, по превышению/понижению, и.т.д.
На графике плохо (как не выёживайся) видно точный уровень по конкретному месту,
и при "подготовке" данных надо "подкрашивать" важные моменты. (такая была мысль)

зы. сейчас озадачился всплывающим окошком по курсору (с точными данными по данной точке),
если есть что предложить/ругнуть, не стесняйтесь.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 11:45 am 
Не в сети
профессор

Зарегистрирован: Чт фев 09, 2017 10:37 am
Сообщений: 239
Благодарил (а): 22 раз.
Поблагодарили: 33 раз.
Пункты репутации: 0
Kuzmat
Попробовал на Linux, пришлось удалить строки и подвыражения Case, так как константы #VK_OEM_PLUS, #VK_OEM_MINUS и #MB_ICONERROR дают ошибку.
Код:
1
2
3
MessageRequester("ERROR!", "Unable to load the " + #DQUOTE$ + #FontName$ + #DQUOTE$ + " font!", #MB_ICONERROR)
Case #PB_Shortcut_Add, #VK_OEM_PLUS
Case #PB_Shortcut_Subtract, #VK_OEM_MINUS



Кстати для сравнения посмотри похожий функционал на AutoIt Внизу я выкладывал примеры работы функционала. Так как меня двигала идея, то сделал примеры максимально разными, цветными, разного типа линии, толщины, линейные графики, синусоиды, затухающая синусоида, варианты меандр, пилообразные и как они получаются сложением гармоник, то есть выводил три графика в одном и результат их сложений. И ещё один с движением, то есть перерисовал графики и создавалась иллюзия движения.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 12:17 pm 
Не в сети
профессор

Зарегистрирован: Вс авг 31, 2014 12:11 am
Сообщений: 457
Благодарил (а): 51 раз.
Поблагодарили: 20 раз.
Пункты репутации: 10
Kuzmat
Цитата:
Сбивки по шкале были в какой-то из предыдущих версий, проверьте на новой, я не замечаю

К сожалению не могу подтвердить. Заранее извиняюсь если много рекламы. Ссылка. Вот. А если несколько раз произвольно сдвинуть график "вперёд-назад", то минуты вообще "плывут".

На счёт каждую минуту. Наверное тут я не договорил. Там не всё так однозначно. Может быть и такой расклад: допустим от 0:00 до 12:00 данные "идут разреженные" - раз в полчаса по одной отметке (стандарт), затем... скажем с 12:00 по 12:30 могут "шпарить" каждую минуту (то есть 30 значений в один шаг сетки, вот он... сжатый участок графика, который возможно потребуется масштабировать/рассмотреть подробней), далее могут следовать опять же разреженные данные с небольшими/произвольными вставками/вкраплениями, например, 12:30; 12:33; 12:50; 13:00; 13:12; ... и т.д.
жирным выделил основные точки графика, которые гарантировано будут присутствовать на графике. Остальные точки - промежуточные, которые могут быть или нет. Ещё раз. За стандарт решил принять получасовой период следования данных (масштаб 1:1), так как это обычный сценарий.
Вкратце обрисовал. Графики планировал строить по одному, то есть выбрал какую-либо одну величину и отобразил.

На счёт шкалы Y. Просто хотел сказать, чтобы пользователь сам мог иметь возможность грубо заполнить отметки/цифры против засечек (заполнив массив/список). Масштабировать по Y не обязательно, тем более если планируется добавить всплывающую подсказку с показом точного значения над курсором в каждой конкретной точке графика. Это было бы просто замечательно.

Так или иначе буду внимательно следить за темой и её развитием.


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 12:23 pm 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
AZJIO писал(а):
Kuzmat
Попробовал на Linux, пришлось удалить строки и подвыражения Case, так как константы #VK_OEM_PLUS, #VK_OEM_MINUS и #MB_ICONERROR дают ошибку.

Если укажете, какие константы заменяют эти в Линуксе (я им не пользуюсь), я сделаю совместимость.
Нужны коды клавиш +,- на основной клавиатуре (а иконку из MessageRequester можно просто выкинуть)
За пример на Autoit спасибо, гляну. Там вроде под GDI+ сделано, разве он в Линуксе есть?


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: Линейный график
СообщениеДобавлено: Ср ноя 14, 2018 12:48 pm 
Не в сети
профессор

Зарегистрирован: Чт сен 22, 2011 6:21 pm
Сообщений: 278
Благодарил (а): 37 раз.
Поблагодарили: 28 раз.
Пункты репутации: 0
knower Понял в каком смысле минуты "плывут", это не ошибка это свойство графика,
если вы медленно потянете мышкой график(ЛКМ и тянуть в сторону), то увидите, что сетка стоит на месте, а данные,
как-бы плывут под ней, а метки по X соответственно перестраиваются под текущий индекс.
Дело в том, что я писал график под большой обьем данных (сильно больше чем может уместится в окне),
соответственно сетка для примерной ориентировки, где мы сейчас (она не двигается вместе с данными).
Над вашей задачей подумаю, на сколько понял:
1) Min-Max - задаются при вызове (авто-масштаб по Y не нужен).
2) подписи по Y из массива. (а как привязаться к размерам по вертикали?)
3) сетку двигать вместе с данными, что-бы метки по X не менялись.
Все верно?


Последний раз редактировалось Kuzmat Ср ноя 14, 2018 12:51 pm, всего редактировалось 1 раз.

Вернуться наверх
 Профиль  
 
Показать сообщения за:  Сортировать по:  
Начать новую тему Ответить на тему  [ Сообщений: 28 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group (блог о phpBB)
Сборка создана CMSart Studio
Русская поддержка phpBB