1# 2# tkextlib/tcllib/plotchart.rb 3# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) 4# 5# * Part of tcllib extension 6# * Simple plotting and charting package 7# 8# (The following is the original description of the library.) 9# 10# Plotchart is a Tcl-only package that focuses on the easy creation of 11# xy-plots, barcharts and other common types of graphical presentations. 12# The emphasis is on ease of use, rather than flexibility. The procedures 13# that create a plot use the entire canvas window, making the layout of the 14# plot completely automatic. 15# 16# This results in the creation of an xy-plot in, say, ten lines of code: 17# -------------------------------------------------------------------- 18# package require Plotchart 19# 20# canvas .c -background white -width 400 -height 200 21# pack .c -fill both 22# 23# # 24# # Create the plot with its x- and y-axes 25# # 26# set s [::Plotchart::createXYPlot .c {0.0 100.0 10.0} {0.0 100.0 20.0}] 27# 28# foreach {x y} {0.0 32.0 10.0 50.0 25.0 60.0 78.0 11.0 } { 29# $s plot series1 $x $y 30# } 31# 32# $s title "Data series" 33# -------------------------------------------------------------------- 34# 35# A drawback of the package might be that it does not do any data management. 36# So if the canvas that holds the plot is to be resized, the whole plot must 37# be redrawn. The advantage, though, is that it offers a number of plot and 38# chart types: 39# 40# * XY-plots like the one shown above with any number of data series. 41# * Stripcharts, a kind of XY-plots where the horizontal axis is adjusted 42# automatically. The result is a kind of sliding window on the data 43# series. 44# * Polar plots, where the coordinates are polar instead of cartesian. 45# * Isometric plots, where the scale of the coordinates in the two 46# directions is always the same, i.e. a circle in world coordinates 47# appears as a circle on the screen. 48# You can zoom in and out, as well as pan with these plots (Note: this 49# works best if no axes are drawn, the zooming and panning routines do 50# not distinguish the axes), using the mouse buttons with the control 51# key and the arrow keys with the control key. 52# * Piecharts, with automatic scaling to indicate the proportions. 53# * Barcharts, with either vertical or horizontal bars, stacked bars or 54# bars side by side. 55# * Timecharts, where bars indicate a time period and milestones or other 56# important moments in time are represented by triangles. 57# * 3D plots (both for displaying surfaces and 3D bars) 58# 59 60require 'tk' 61require 'tkextlib/tcllib.rb' 62 63# TkPackage.require('Plotchart', '0.9') 64# TkPackage.require('Plotchart', '1.1') 65# TkPackage.require('Plotchart', '1.6.3') 66TkPackage.require('Plotchart') 67 68module Tk 69 module Tcllib 70 module Plotchart 71 PACKAGE_NAME = 'Plotchart'.freeze 72 def self.package_name 73 PACKAGE_NAME 74 end 75 76 def self.package_version 77 begin 78 TkPackage.require('Plotchart') 79 rescue 80 '' 81 end 82 end 83 end 84 end 85end 86 87module Tk::Tcllib::Plotchart 88 extend TkCore 89 ############################ 90 def self.view_port(w, *args) # args := pxmin, pymin, pxmax, pymax 91 tk_call_without_enc('::Plotchart::viewPort', w.path, *(args.flatten)) 92 end 93 94 def self.world_coordinates(w, *args) # args := xmin, ymin, xmax, ymax 95 tk_call_without_enc('::Plotchart::worldCoordinates', 96 w.path, *(args.flatten)) 97 end 98 99 def self.world_3D_coordinates(w, *args) 100 # args := xmin, ymin, zmin, xmax, ymax, zmax 101 tk_call_without_enc('::Plotchart::world3DCoordinates', 102 w.path, *(args.flatten)) 103 end 104 105 def self.coords_to_pixel(w, x, y) 106 list(tk_call_without_enc('::Plotchart::coordsToPixel', w.path, x, y)) 107 end 108 109 def self.coords_3D_to_pixel(w, x, y, z) 110 list(tk_call_without_enc('::Plotchart::coords3DToPixel', w.path, x, y, z)) 111 end 112 113 def self.plotconfig(*args) 114 case args.length 115 when 0, 1, 2 116 # 0: (no args) --> list of chat types 117 # 1: charttype --> list of components 118 # 2: charttype, component --> list of properties 119 simplelist(tk_call('::Plotchart::plotconfig', *args)) 120 when 3 121 # 3: charttype, component, property --> current value 122 tk_call('::Plotchart::plotconfig', *args) 123 else 124 # 4: charttype, component, property, value : set new value 125 # 5+: Error on Tcl/Tk 126 tk_call('::Plotchart::plotconfig', *args) 127 nil 128 end 129 end 130 131 def self.plotpack(w, dir, *plots) 132 tk_call_without_enc('::Plotchart::plotpack', w.path, dir, *plots) 133 w 134 end 135 136 def self.polar_coordinates(w, radmax) 137 tk_call_without_enc('::Plotchart::polarCoordinates', w.path, radmax) 138 end 139 140 def self.polar_to_pixel(w, rad, phi) 141 list(tk_call_without_enc('::Plotchart::polarToPixel', w.path, rad, phi)) 142 end 143 144 def self.pixel_to_coords(w, x, y) 145 list(tk_call_without_enc('::Plotchart::coordsToPixel', w.path, x, y)) 146 end 147 148 def self.determine_scale(*args) # (xmin, xmax, inverted=false) 149 tk_call_without_enc('::Plotchart::determineScale', *args) 150 end 151 152 def self.set_zoom_pan(w) 153 tk_call_without_enc('::Plotchart::setZoomPan', w.path) 154 end 155 156 ############################ 157 module ChartMethod 158 include TkCore 159 160 def title(str) 161 tk_call_without_enc(@chart, 'title', _get_eval_enc_str(str)) 162 self 163 end 164 165 def save_plot(filename) 166 tk_call_without_enc(@chart, 'saveplot', filename) 167 self 168 end 169 170 def xtext(str) 171 tk_call_without_enc(@chart, 'xtext', _get_eval_enc_str(str)) 172 self 173 end 174 175 def ytext(str) 176 tk_call_without_enc(@chart, 'ytext', _get_eval_enc_str(str)) 177 self 178 end 179 180 def xconfig(key, value=None) 181 if key.kind_of?(Hash) 182 tk_call_without_enc(@chart, 'xconfig', *hash_kv(key, true)) 183 else 184 tk_call(@chart, 'xconfig', "-#{key}",value) 185 end 186 self 187 end 188 189 def yconfig(key, value=None) 190 if key.kind_of?(Hash) 191 tk_call_without_enc(@chart, 'yconfig', *hash_kv(key, true)) 192 else 193 tk_call(@chart, 'yconfig', "-#{key}", value) 194 end 195 self 196 end 197 198 def background(part, color_or_image, dir) 199 tk_call_without_enc(@chart, 'background', 200 part, color_or_image, dir) 201 self 202 end 203 204 def xticklines(color=None) 205 tk_call(@chart, 'xticklines', color) 206 self 207 end 208 209 def yticklines(color=None) 210 tk_call(@chart, 'yticklines', color) 211 self 212 end 213 214 def legendconfig(key, value=None) 215 if key.kind_of?(Hash) 216 tk_call_without_enc(@chart, 'legendconfig', *hash_kv(key, true)) 217 else 218 tk_call(@chart, 'legendconfig', "-#{key}", value) 219 end 220 self 221 end 222 223 def legend(series, text) 224 tk_call_without_enc(@chart, 'legend', 225 _get_eval_enc_str(series), _get_eval_enc_str(text)) 226 self 227 end 228 229 def balloon(*args) # args => (x, y, text, dir) or ([x, y], text, dir) 230 if args[0].kind_of?(Array) 231 # args => ([x, y], text, dir) 232 x, y = args.shift 233 else 234 # args => (x, y, text, dir) 235 x = args.shift 236 y = args.shift 237 end 238 239 text, dir = args 240 241 tk_call_without_enc(@chart, 'balloon', x, y, 242 _get_eval_enc_str(text), dir) 243 self 244 end 245 246 def balloonconfig(key, value=None) 247 if key.kind_of?(Hash) 248 tk_call_without_enc(@chart, 'balloonconfig', *hash_kv(key, true)) 249 else 250 tk_call(@chart, 'balloonconfig', "-#{key}", value) 251 end 252 end 253 254 def plaintext(*args) # args => (x, y, text, dir) or ([x, y], text, dir) 255 if args[0].kind_of?(Array) 256 # args => ([x, y], text, dir) 257 x, y = args.shift 258 else 259 # args => (x, y, text, dir) 260 x = args.shift 261 y = args.shift 262 end 263 264 text, dir = args 265 266 tk_call_without_enc(@chart, 'plaintext', x, y, 267 _get_eval_enc_str(text), dir) 268 self 269 end 270 271 ############################ 272 273 def view_port(*args) # args := pxmin, pymin, pxmax, pymax 274 tk_call_without_enc('::Plotchart::viewPort', @path, *(args.flatten)) 275 self 276 end 277 278 def world_coordinates(*args) # args := xmin, ymin, xmax, ymax 279 tk_call_without_enc('::Plotchart::worldCoordinates', 280 @path, *(args.flatten)) 281 self 282 end 283 284 def world_3D_coordinates(*args) 285 # args := xmin, ymin, zmin, xmax, ymax, zmax 286 tk_call_without_enc('::Plotchart::world3DCoordinates', 287 @path, *(args.flatten)) 288 self 289 end 290 291 def coords_to_pixel(x, y) 292 list(tk_call_without_enc('::Plotchart::coordsToPixel', @path, x, y)) 293 end 294 295 def coords_3D_to_pixel(x, y, z) 296 list(tk_call_without_enc('::Plotchart::coords3DToPixel', @path, x, y, z)) 297 end 298 299 def plotpack(dir, *plots) 300 tk_call_without_enc('::Plotchart::plotpack', @path, dir, *plots) 301 self 302 end 303 304 def polar_coordinates(radmax) 305 tk_call_without_enc('::Plotchart::polarCoordinates', @path, radmax) 306 self 307 end 308 309 def polar_to_pixel(rad, phi) 310 list(tk_call_without_enc('::Plotchart::polarToPixel', @path, rad, phi)) 311 end 312 313 def pixel_to_coords(x, y) 314 list(tk_call_without_enc('::Plotchart::coordsToPixel', @path, x, y)) 315 end 316 317 def determine_scale(xmax, ymax) 318 tk_call_without_enc('::Plotchart::determineScale', @path, xmax, ymax) 319 self 320 end 321 322 def set_zoom_pan() 323 tk_call_without_enc('::Plotchart::setZoomPan', @path) 324 self 325 end 326 end 327 328 ############################ 329 class XYPlot < Tk::Canvas 330 include ChartMethod 331 332 TkCommandNames = [ 333 'canvas'.freeze, 334 '::Plotchart::createXYPlot'.freeze 335 ].freeze 336 337 def initialize(*args) # args := ([parent,] xaxis, yaxis [, keys]) 338 # xaxis := Array of [minimum, maximum, stepsize] 339 # yaxis := Array of [minimum, maximum, stepsize] 340 if args[0].kind_of?(Array) 341 @xaxis = args.shift 342 @yaxis = args.shift 343 344 super(*args) # create canvas widget 345 else 346 parent = args.shift 347 348 @xaxis = args.shift 349 @yaxis = args.shift 350 351 if parent.kind_of?(Tk::Canvas) 352 @path = parent.path 353 else 354 super(parent, *args) # create canvas widget 355 end 356 end 357 358 @chart = _create_chart 359 end 360 361 def _create_chart 362 p self.class::TkCommandNames[1] if $DEBUG 363 tk_call_without_enc(self.class::TkCommandNames[1], @path, 364 array2tk_list(@xaxis), array2tk_list(@yaxis)) 365 end 366 private :_create_chart 367 368 def __destroy_hook__ 369 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.mutex.synchronize{ 370 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.delete(@path) 371 } 372 end 373 374 def plot(series, x, y) 375 tk_call_without_enc(@chart, 'plot', _get_eval_enc_str(series), x, y) 376 self 377 end 378 379 def contourlines(xcrd, ycrd, vals, clss=None) 380 xcrd = array2tk_list(xcrd) if xcrd.kind_of?(Array) 381 ycrd = array2tk_list(ycrd) if ycrd.kind_of?(Array) 382 vals = array2tk_list(vals) if vals.kind_of?(Array) 383 clss = array2tk_list(clss) if clss.kind_of?(Array) 384 385 tk_call(@chart, 'contourlines', xcrd, ycrd, vals, clss) 386 self 387 end 388 389 def contourfill(xcrd, ycrd, vals, clss=None) 390 xcrd = array2tk_list(xcrd) if xcrd.kind_of?(Array) 391 ycrd = array2tk_list(ycrd) if ycrd.kind_of?(Array) 392 vals = array2tk_list(vals) if vals.kind_of?(Array) 393 clss = array2tk_list(clss) if clss.kind_of?(Array) 394 395 tk_call(@chart, 'contourfill', xcrd, ycrd, vals, clss) 396 self 397 end 398 399 def contourbox(xcrd, ycrd, vals, clss=None) 400 xcrd = array2tk_list(xcrd) if xcrd.kind_of?(Array) 401 ycrd = array2tk_list(ycrd) if ycrd.kind_of?(Array) 402 vals = array2tk_list(vals) if vals.kind_of?(Array) 403 clss = array2tk_list(clss) if clss.kind_of?(Array) 404 405 tk_call(@chart, 'contourbox', xcrd, ycrd, vals, clss) 406 self 407 end 408 409 def color_map(colors) 410 colors = array2tk_list(colors) if colors.kind_of?(Array) 411 412 tk_call_without_enc(@chart, 'colorMap', colors) 413 self 414 end 415 416 def grid_cells(xcrd, ycrd) 417 xcrd = array2tk_list(xcrd) if xcrd.kind_of?(Array) 418 ycrd = array2tk_list(ycrd) if ycrd.kind_of?(Array) 419 420 tk_call_without_enc(@chart, 'grid', xcrd, ycrd) 421 self 422 end 423 424 def dataconfig(series, key, value=None) 425 if key.kind_of?(Hash) 426 tk_call_without_enc(@chart, 'dataconfig', series, *hash_kv(key, true)) 427 else 428 tk_call(@chart, 'dataconfig', series, "-#{key}", value) 429 end 430 end 431 432 def rescale(xscale, yscale) # xscale|yscale => [newmin, newmax, newstep] 433 tk_call_without_enc(@chart, 'rescale', xscale, yscale) 434 self 435 end 436 437 def trend(series, xcrd, ycrd) 438 tk_call_without_enc(@chart, 'trend', 439 _get_eval_enc_str(series), xcrd, ycrd) 440 self 441 end 442 443 def rchart(series, xcrd, ycrd) 444 tk_call_without_enc(@chart, 'rchart', 445 _get_eval_enc_str(series), xcrd, ycrd) 446 self 447 end 448 449 def interval(series, xcrd, ymin, ymax, ycenter=None) 450 tk_call(@chart, 'interval', series, xcrd, ymin, ymax, ycenter) 451 self 452 end 453 454 def box_and_whiskers(series, xcrd, ycrd) 455 tk_call_without_enc(@chart, 'box-and-whiskers', 456 _get_eval_enc_str(series), xcrd, ycrd) 457 self 458 end 459 alias box_whiskers box_and_whiskers 460 461 def vectorconfig(series, key, value=None) 462 if key.kind_of?(Hash) 463 tk_call_without_enc(@chart, 'vectorconfig', 464 _get_eval_enc_str(series), *hash_kv(key, true)) 465 else 466 tk_call(@chart, 'vectorconfig', series, "-#{key}", value) 467 end 468 self 469 end 470 471 def vector(series, xcrd, ycrd, ucmp, vcmp) 472 tk_call_without_enc(@chart, 'vector', _get_eval_enc_str(series), 473 xcrd, ycrd, ucmp, vcmp) 474 self 475 end 476 477 def dotconfig(series, key, value=None) 478 if key.kind_of?(Hash) 479 tk_call_without_enc(@chart, 'dotconfig', 480 _get_eval_enc_str(series), *hash_kv(key, true)) 481 else 482 tk_call(@chart, 'dotconfig', series, "-#{key}", value) 483 end 484 self 485 end 486 487 def dot(series, xcrd, ycrd, value) 488 tk_call_without_enc(@chart, 'dot', _get_eval_enc_str(series), 489 xcrd, ycrd, value) 490 self 491 end 492 end 493 494 ############################ 495 class Stripchart < XYPlot 496 TkCommandNames = [ 497 'canvas'.freeze, 498 '::Plotchart::createStripchart'.freeze 499 ].freeze 500 end 501 502 ############################ 503 class TXPlot < XYPlot 504 TkCommandNames = [ 505 'canvas'.freeze, 506 '::Plotchart::createTXPlot'.freeze 507 ].freeze 508 end 509 510 ############################ 511 class XLogYPlot < XYPlot 512 TkCommandNames = [ 513 'canvas'.freeze, 514 '::Plotchart::createXLogYPlot'.freeze 515 ].freeze 516 end 517 518 ############################ 519 class Histogram < XYPlot 520 TkCommandNames = [ 521 'canvas'.freeze, 522 '::Plotchart::createHistgram'.freeze 523 ].freeze 524 end 525 526 ############################ 527 class PolarPlot < Tk::Canvas 528 include ChartMethod 529 530 TkCommandNames = [ 531 'canvas'.freeze, 532 '::Plotchart::createPolarplot'.freeze 533 ].freeze 534 535 def initialize(*args) # args := ([parent,] radius_data [, keys]) 536 # radius_data := Array of [maximum_radius, stepsize] 537 if args[0].kind_of?(Array) 538 @radius_data = args.shift 539 540 super(*args) # create canvas widget 541 else 542 parent = args.shift 543 544 @radius_data = args.shift 545 546 if parent.kind_of?(Tk::Canvas) 547 @path = parent.path 548 else 549 super(parent, *args) # create canvas widget 550 end 551 end 552 553 @chart = _create_chart 554 end 555 556 def _create_chart 557 p self.class::TkCommandNames[1] if $DEBUG 558 tk_call_without_enc(self.class::TkCommandNames[1], @path, 559 array2tk_list(@radius_data)) 560 end 561 private :_create_chart 562 563 def __destroy_hook__ 564 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.mutex.synchronize{ 565 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.delete(@path) 566 } 567 end 568 569 def plot(series, radius, angle) 570 tk_call_without_enc(@chart, 'plot', _get_eval_enc_str(series), 571 radius, angle) 572 self 573 end 574 575 def dataconfig(series, key, value=None) 576 if key.kind_of?(Hash) 577 tk_call_without_enc(@chart, 'dataconfig', _get_eval_enc_str(series), 578 *hash_kv(key, true)) 579 else 580 tk_call(@chart, 'dataconfig', series, "-#{key}", value) 581 end 582 end 583 end 584 Polarplot = PolarPlot 585 586 ############################ 587 class IsometricPlot < Tk::Canvas 588 include ChartMethod 589 590 TkCommandNames = [ 591 'canvas'.freeze, 592 '::Plotchart::createIsometricPlot'.freeze 593 ].freeze 594 595 def initialize(*args) # args := ([parent,] xaxis, yaxis, [, step] [, keys]) 596 # xaxis := Array of [minimum, maximum] 597 # yaxis := Array of [minimum, maximum] 598 # step := Float of stepsize | "noaxes" | :noaxes 599 if args[0].kind_of?(Array) 600 @xaxis = args.shift 601 @yaxis = args.shift 602 603 if args[0].kind_of?(Hash) 604 @stepsize = :noaxes 605 else 606 @stepsize = args.shift 607 end 608 609 super(*args) # create canvas widget 610 else 611 parent = args.shift 612 613 @xaxis = args.shift 614 @yaxis = args.shift 615 616 if args[0].kind_of?(Hash) 617 @stepsize = :noaxes 618 else 619 @stepsize = args.shift 620 end 621 622 if parent.kind_of?(Tk::Canvas) 623 @path = parent.path 624 else 625 super(parent, *args) # create canvas widget 626 end 627 end 628 629 @chart = _create_chart 630 end 631 632 def _create_chart 633 p self.class::TkCommandNames[1] if $DEBUG 634 tk_call_without_enc(self.class::TkCommandNames[1], @path, 635 array2tk_list(@xaxis), array2tk_list(@yaxis), 636 @stepsize) 637 end 638 private :_create_chart 639 640 def plot(type, *args) 641 self.__send__("plot_#{type.to_s.tr('-', '_')}", *args) 642 end 643 644 def plot_rectangle(*args) # args := x1, y1, x2, y2, color 645 tk_call_without_enc(@chart, 'plot', 'rectangle', *(args.flatten)) 646 self 647 end 648 649 def plot_filled_rectangle(*args) # args := x1, y1, x2, y2, color 650 tk_call_without_enc(@chart, 'plot', 'filled-rectangle', *(args.flatten)) 651 self 652 end 653 654 def plot_circle(*args) # args := xc, yc, radius, color 655 tk_call_without_enc(@chart, 'plot', 'circle', *(args.flatten)) 656 self 657 end 658 659 def plot_filled_circle(*args) # args := xc, yc, radius, color 660 tk_call_without_enc(@chart, 'plot', 'filled-circle', *(args.flatten)) 661 self 662 end 663 end 664 Isometricplot = IsometricPlot 665 666 ############################ 667 class Plot3D < Tk::Canvas 668 include ChartMethod 669 670 TkCommandNames = [ 671 'canvas'.freeze, 672 '::Plotchart::create3DPlot'.freeze 673 ].freeze 674 675 def initialize(*args) # args := ([parent,] xaxis, yaxis, zaxis [, keys]) 676 # xaxis := Array of [minimum, maximum, stepsize] 677 # yaxis := Array of [minimum, maximum, stepsize] 678 # zaxis := Array of [minimum, maximum, stepsize] 679 if args[0].kind_of?(Array) 680 @xaxis = args.shift 681 @yaxis = args.shift 682 @zaxis = args.shift 683 684 super(*args) # create canvas widget 685 else 686 parent = args.shift 687 688 @xaxis = args.shift 689 @yaxis = args.shift 690 @zaxis = args.shift 691 692 if parent.kind_of?(Tk::Canvas) 693 @path = parent.path 694 else 695 super(parent, *args) # create canvas widget 696 end 697 end 698 699 @chart = _create_chart 700 end 701 702 def _create_chart 703 p self.class::TkCommandNames[1] if $DEBUG 704 tk_call_without_enc(self.class::TkCommandNames[1], @path, 705 array2tk_list(@xaxis), 706 array2tk_list(@yaxis), 707 array2tk_list(@zaxis)) 708 end 709 private :_create_chart 710 711 def plot_function(cmd=Proc.new) 712 Tk.ip_eval("proc #{@path}_#{@chart} {x y} {#{install_cmd(cmd)} $x $y}") 713 tk_call_without_enc(@chart, 'plotfunc', "#{@path}_#{@chart}") 714 self 715 end 716 717 def plot_funcont(conts, cmd=Proc.new) 718 conts = array2tk_list(conts) if conts.kind_of?(Array) 719 Tk.ip_eval("proc #{@path}_#{@chart} {x y} {#{install_cmd(cmd)} $x $y}") 720 tk_call_without_enc(@chart, 'plotfuncont', "#{@path}_#{@chart}", conts) 721 self 722 end 723 724 def grid_size(nxcells, nycells) 725 tk_call_without_enc(@chart, 'gridsize', nxcells, nycells) 726 self 727 end 728 729 def plot_line(dat, color) 730 # dat has to be provided as a 2 level array. 731 # 1st level contains rows, drawn in y-direction, 732 # and each row is an array whose elements are drawn in x-direction, 733 # for the columns. 734 tk_call_without_enc(@chart, 'plotline', dat, color) 735 self 736 end 737 738 def plot_data(dat) 739 # dat has to be provided as a 2 level array. 740 # 1st level contains rows, drawn in y-direction, 741 # and each row is an array whose elements are drawn in x-direction, 742 # for the columns. 743 tk_call_without_enc(@chart, 'plotdata', dat) 744 self 745 end 746 747 def zconfig(key, value=None) 748 if key.kind_of?(Hash) 749 tk_call_without_enc(@chart, 'zconfig', *hash_kv(key, true)) 750 else 751 tk_call(@chart, 'zconfig', "-#{key}", value) 752 end 753 self 754 end 755 756 def colour(fill, border) 757 # configure the colours to use for polygon borders and inner area 758 tk_call_without_enc(@chart, 'colour', fill, border) 759 self 760 end 761 alias colours colour 762 alias colors colour 763 alias color colour 764 end 765 766 ############################ 767 class Barchart3D < Tk::Canvas 768 include ChartMethod 769 770 TkCommandNames = [ 771 'canvas'.freeze, 772 '::Plotchart::create3DBarchart'.freeze 773 ].freeze 774 775 def initialize(*args) # args := ([parent,] yaxis, nobars [, keys]) 776 # yaxis := Array of [minimum, maximum, stepsize] 777 # nobars := number of bars 778 if args[0].kind_of?(Array) 779 @yaxis = args.shift 780 @nobars = args.shift 781 782 super(*args) # create canvas widget 783 else 784 parent = args.shift 785 786 @yaxis = args.shift 787 @nobars = args.shift 788 789 if parent.kind_of?(Tk::Canvas) 790 @path = parent.path 791 else 792 super(parent, *args) # create canvas widget 793 end 794 end 795 796 @chart = _create_chart 797 end 798 799 def _create_chart 800 p self.class::TkCommandNames[1] if $DEBUG 801 tk_call_without_enc(self.class::TkCommandNames[1], @path, 802 array2tk_list(@yaxis), @nobars) 803 end 804 private :_create_chart 805 806 def plot(label, yvalue, color) 807 tk_call_without_enc(@chart, 'plot', _get_eval_enc_str(label), 808 _get_eval_enc_str(yvalue), color) 809 self 810 end 811 812 def config(key, value=None) 813 if key.kind_of?(Hash) 814 tk_call_without_enc(@chart, 'config', *hash_kv(key, true)) 815 else 816 tk_call(@chart, 'config', "-#{key}", value) 817 end 818 self 819 end 820 end 821 822 ############################ 823 class RibbonChart3D < Tk::Canvas 824 include ChartMethod 825 826 TkCommandNames = [ 827 'canvas'.freeze, 828 '::Plotchart::create3DRibbonChart'.freeze 829 ].freeze 830 831 def initialize(*args) # args := ([parent,] names, yaxis, zaxis [, keys]) 832 # names := Array of the series 833 # yaxis := Array of [minimum, maximum, stepsize] 834 # zaxis := Array of [minimum, maximum, stepsize] 835 if args[0].kind_of?(Array) 836 @names = args.shift 837 @yaxis = args.shift 838 @zaxis = args.shift 839 840 super(*args) # create canvas widget 841 else 842 parent = args.shift 843 844 @names = args.shift 845 @yaxis = args.shift 846 @zaxis = args.shift 847 848 if parent.kind_of?(Tk::Canvas) 849 @path = parent.path 850 else 851 super(parent, *args) # create canvas widget 852 end 853 end 854 855 @chart = _create_chart 856 end 857 858 def _create_chart 859 p self.class::TkCommandNames[1] if $DEBUG 860 tk_call_without_enc(self.class::TkCommandNames[1], @path, 861 array2tk_list(@names), 862 array2tk_list(@yaxis), 863 array2tk_list(@zaxis)) 864 end 865 private :_create_chart 866 867 def line(*args) # xypairs, color 868 color = args.pop # last argument is a color 869 xypairs = TkComm.slice_ary(args.flatten, 2) # regenerate xypairs 870 tk_call_without_enc(@chart, 'line', xypairs, color) 871 self 872 end 873 874 def area(*args) # xypairs, color 875 color = args.pop # last argument is a color 876 xypairs = TkComm.slice_ary(args.flatten, 2) # regenerate xypairs 877 tk_call_without_enc(@chart, 'area', xypairs, color) 878 self 879 end 880 881 def zconfig(key, value=None) 882 if key.kind_of?(Hash) 883 tk_call_without_enc(@chart, 'zconfig', *hash_kv(key, true)) 884 else 885 tk_call(@chart, 'zconfig',"-#{key}", value) 886 end 887 self 888 end 889 end 890 891 892 ############################ 893 class Piechart < Tk::Canvas 894 include ChartMethod 895 896 TkCommandNames = [ 897 'canvas'.freeze, 898 '::Plotchart::createPiechart'.freeze 899 ].freeze 900 901 def initialize(*args) # args := ([parent] [, keys]) 902 if args[0].kind_of?(Tk::Canvas) 903 parent = args.shift 904 @path = parent.path 905 else 906 super(*args) # create canvas widget 907 end 908 @chart = _create_chart 909 end 910 911 def _create_chart 912 p self.class::TkCommandNames[1] if $DEBUG 913 tk_call_without_enc(self.class::TkCommandNames[1], @path) 914 end 915 private :_create_chart 916 917 def plot(*dat) # argument is a list of [label, value] 918 tk_call(@chart, 'plot', dat.flatten) 919 self 920 end 921 922 def colours(*list) 923 tk_call_without_enc(@chart, 'colours', *list) 924 self 925 end 926 alias colors colours 927 end 928 929 930 ############################ 931 class Radialchart < Tk::Canvas 932 include ChartMethod 933 934 TkCommandNames = [ 935 'canvas'.freeze, 936 '::Plotchart::createRadialchart'.freeze 937 ].freeze 938 939 def initialize(*args) # args := ([parent,] names, scale, style [, keys]) 940 # radius_data := Array of [maximum_radius, stepsize] 941 if args[0].kind_of?(Array) 942 @names = args.shift 943 @scale = args.shift 944 @style = args.shift 945 946 super(*args) # create canvas widget 947 else 948 parent = args.shift 949 950 @names = args.shift 951 @scale = args.shift 952 @style = args.shift 953 954 if parent.kind_of?(Tk::Canvas) 955 @path = parent.path 956 else 957 super(parent, *args) # create canvas widget 958 end 959 end 960 961 @chart = _create_chart 962 end 963 964 def _create_chart 965 p self.class::TkCommandNames[1] if $DEBUG 966 tk_call_without_enc(self.class::TkCommandNames[1], @path, 967 array2tk_list(@names), @scale, @style) 968 end 969 private :_create_chart 970 971 def __destroy_hook__ 972 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.mutex.synchronize{ 973 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.delete(@path) 974 } 975 end 976 977 def plot(data, color, thickness) 978 tk_call_without_enc(@chart, 'plot', _get_eval_enc_str(data), 979 color, thickness) 980 self 981 end 982 983 def colours(*list) 984 tk_call_without_enc(@chart, 'colours', *list) 985 self 986 end 987 alias colors colours 988 end 989 990 ############################ 991 class Barchart < Tk::Canvas 992 include ChartMethod 993 994 TkCommandNames = [ 995 'canvas'.freeze, 996 '::Plotchart::createBarchart'.freeze 997 ].freeze 998 999 def initialize(*args) 1000 # args := ([parent,] xlabels, ylabels [, series] [, keys]) 1001 # xlabels, ylabels := labels | axis ( depend on normal or horizontal ) 1002 # labels := Array of [label, label, ...] 1003 # (It determines the number of bars that will be plotted per series.) 1004 # axis := Array of [minimum, maximum, stepsize] 1005 # series := Integer number of data series | 'stacked' | :stacked 1006 if args[0].kind_of?(Array) 1007 @xlabels = args.shift 1008 @ylabels = args.shift 1009 1010 if args[0].kind_of?(Hash) 1011 @series_size = :stacked 1012 else 1013 @series_size = args.shift 1014 end 1015 1016 super(*args) # create canvas widget 1017 else 1018 parent = args.shift 1019 1020 @xlabels = args.shift 1021 @ylabels = args.shift 1022 1023 if args[0].kind_of?(Hash) 1024 @series_size = :stacked 1025 else 1026 @series_size = args.shift 1027 end 1028 1029 if parent.kind_of?(Tk::Canvas) 1030 @path = parent.path 1031 else 1032 super(parent, *args) # create canvas widget 1033 end 1034 end 1035 1036 @chart = _create_chart 1037 end 1038 1039 def _create_chart 1040 p self.class::TkCommandNames[1] if $DEBUG 1041 tk_call_without_enc(self.class::TkCommandNames[1], @path, 1042 array2tk_list(@xlabels), array2tk_list(@ylabels), 1043 @series_size) 1044 end 1045 private :_create_chart 1046 1047 def __destroy_hook__ 1048 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.mutex.synchronize{ 1049 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.delete(@path) 1050 } 1051 end 1052 1053 def plot(series, dat, col=None) 1054 tk_call(@chart, 'plot', series, dat, col) 1055 self 1056 end 1057 1058 def colours(*cols) 1059 # set the colours to be used 1060 tk_call(@chart, 'colours', *cols) 1061 self 1062 end 1063 alias colour colours 1064 alias colors colours 1065 alias color colours 1066 end 1067 1068 ############################ 1069 class HorizontalBarchart < Barchart 1070 TkCommandNames = [ 1071 'canvas'.freeze, 1072 '::Plotchart::createHorizontalBarchart'.freeze 1073 ].freeze 1074 end 1075 1076 ############################ 1077 class Boxplot < Tk::Canvas 1078 include ChartMethod 1079 1080 TkCommandNames = [ 1081 'canvas'.freeze, 1082 '::Plotchart::createBoxplot'.freeze 1083 ].freeze 1084 1085 def initialize(*args) # args := ([parent,] xaxis, ylabels [, keys]) 1086 # xaxis := Array of [minimum, maximum, stepsize] 1087 # yaxis := List of labels for the y-axis 1088 if args[0].kind_of?(Array) 1089 @xaxis = args.shift 1090 @ylabels = args.shift 1091 1092 super(*args) # create canvas widget 1093 else 1094 parent = args.shift 1095 1096 @xaxis = args.shift 1097 @ylabels = args.shift 1098 1099 if parent.kind_of?(Tk::Canvas) 1100 @path = parent.path 1101 else 1102 super(parent, *args) # create canvas widget 1103 end 1104 end 1105 1106 @chart = _create_chart 1107 end 1108 1109 def _create_chart 1110 p self.class::TkCommandNames[1] if $DEBUG 1111 tk_call_without_enc(self.class::TkCommandNames[1], @path, 1112 array2tk_list(@xaxis), array2tk_list(@ylabels)) 1113 end 1114 private :_create_chart 1115 1116 def __destroy_hook__ 1117 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.mutex.synchronize{ 1118 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.delete(@path) 1119 } 1120 end 1121 1122 def plot(label, *values) 1123 tk_call(@chart, 'plot', label, values.flatten) 1124 self 1125 end 1126 end 1127 1128 ############################ 1129 class RightAxis < Tk::Canvas 1130 include ChartMethod 1131 1132 TkCommandNames = [ 1133 'canvas'.freeze, 1134 '::Plotchart::createRightAxis'.freeze 1135 ].freeze 1136 1137 def initialize(*args) # args := ([parent,] yaxis [, keys]) 1138 # yaxis := Array of [minimum, maximum, stepsize] 1139 if args[0].kind_of?(Array) 1140 @yaxis = args.shift 1141 1142 super(*args) # create canvas widget 1143 else 1144 parent = args.shift 1145 1146 @yaxis = args.shift 1147 1148 if parent.kind_of?(Tk::Canvas) 1149 @path = parent.path 1150 else 1151 super(parent, *args) # create canvas widget 1152 end 1153 end 1154 1155 @chart = _create_chart 1156 end 1157 1158 def _create_chart 1159 p self.class::TkCommandNames[1] if $DEBUG 1160 tk_call_without_enc(self.class::TkCommandNames[1], @path, 1161 array2tk_list(@yaxis)) 1162 end 1163 private :_create_chart 1164 1165 def __destroy_hook__ 1166 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.mutex.synchronize{ 1167 Tk::Tcllib::Plotchart::PlotSeries::SeriesID_TBL.delete(@path) 1168 } 1169 end 1170 end 1171 1172 ############################ 1173 class Timechart < Tk::Canvas 1174 include ChartMethod 1175 1176 TkCommandNames = [ 1177 'canvas'.freeze, 1178 '::Plotchart::createTimechart'.freeze 1179 ].freeze 1180 1181 def initialize(*args) 1182 # args := ([parent,] time_begin, time_end, items [, keys]) 1183 # time_begin := String of time format (e.g. "1 january 2004") 1184 # time_end := String of time format (e.g. "1 january 2004") 1185 # items := Expected/maximum number of items 1186 # ( This determines the vertical spacing. ) 1187 if args[0].kind_of?(String) 1188 @time_begin = args.shift 1189 @time_end = args.shift 1190 @items = args.shift 1191 1192 super(*args) # create canvas widget 1193 else 1194 parent = args.shift 1195 1196 @time_begin = args.shift 1197 @time_end = args.shift 1198 @items = args.shift 1199 1200 if parent.kind_of?(Tk::Canvas) 1201 @path = parent.path 1202 else 1203 super(parent, *args) # create canvas widget 1204 end 1205 end 1206 1207 @chart = _create_chart 1208 end 1209 1210 def _create_chart 1211 p self.class::TkCommandNames[1] if $DEBUG 1212 tk_call_without_enc(self.class::TkCommandNames[1], @path, 1213 @time_begin, @time_end, @items) 1214 end 1215 private :_create_chart 1216 1217 def period(txt, time_begin, time_end, col=None) 1218 tk_call(@chart, 'period', txt, time_begin, time_end, col) 1219 self 1220 end 1221 1222 def milestone(txt, time, col=None) 1223 tk_call(@chart, 'milestone', txt, time, col) 1224 self 1225 end 1226 1227 def vertline(txt, time) 1228 tk_call(@chart, 'vertline', txt, time) 1229 self 1230 end 1231 1232 def hscroll=(scr) 1233 tk_call_without_enc(@chart, 'hscroll', scr) 1234 scr 1235 end 1236 def hscroll(scr) 1237 tk_call_without_enc(@chart, 'hscroll', scr) 1238 self 1239 end 1240 1241 def vscroll=(scr) 1242 tk_call_without_enc(@chart, 'vscroll', scr) 1243 scr 1244 end 1245 def vscroll(scr) 1246 tk_call_without_enc(@chart, 'vscroll', scr) 1247 self 1248 end 1249 end 1250 1251 ############################ 1252 class Ganttchart < Tk::Canvas 1253 include ChartMethod 1254 1255 TkCommandNames = [ 1256 'canvas'.freeze, 1257 '::Plotchart::createGanttchart'.freeze 1258 ].freeze 1259 1260 def initialize(*args) 1261 # args := ([parent,] time_begin, time_end, items [, text_width] [, keys]) 1262 # time_begin := String of time format (e.g. "1 january 2004") 1263 # time_end := String of time format (e.g. "1 january 2004") 1264 # args := Expected/maximum number of items 1265 # ( This determines the vertical spacing. ), 1266 # Expected/maximum width of items, 1267 # Option Hash ( { key=>value, ... } ) 1268 if args[0].kind_of?(String) 1269 @time_begin = args.shift 1270 @time_end = args.shift 1271 @args = args 1272 1273 super(*args) # create canvas widget 1274 else 1275 parent = args.shift 1276 1277 @time_begin = args.shift 1278 @time_end = args.shift 1279 @args = args 1280 1281 if parent.kind_of?(Tk::Canvas) 1282 @path = parent.path 1283 else 1284 super(parent, *args) # create canvas widget 1285 end 1286 end 1287 1288 @chart = _create_chart 1289 end 1290 1291 def _create_chart 1292 p self.class::TkCommandNames[1] if $DEBUG 1293 tk_call(self.class::TkCommandNames[1], @path, 1294 @time_begin, @time_end, *args) 1295 end 1296 private :_create_chart 1297 1298 def task(txt, time_begin, time_end, completed=0.0) 1299 list(tk_call(@chart, 'task', txt, time_begin, time_end, 1300 completed)).collect!{|id| 1301 TkcItem.id2obj(self, id) 1302 } 1303 end 1304 1305 def milestone(txt, time, col=None) 1306 tk_call(@chart, 'milestone', txt, time, col) 1307 self 1308 end 1309 1310 def vertline(txt, time) 1311 tk_call(@chart, 'vertline', txt, time) 1312 self 1313 end 1314 1315 def connect(from_task, to_task) 1316 from_task = array2tk_list(from_task) if from_task.kind_of?(Array) 1317 to_task = array2tk_list(to_task) if to_task.kind_of?(Array) 1318 1319 tk_call(@chart, 'connect', from_task, to_task) 1320 self 1321 end 1322 1323 def summary(txt, tasks) 1324 tasks = array2tk_list(tasks) if tasks.kind_of?(Array) 1325 tk_call(@chart, 'summary', tasks) 1326 self 1327 end 1328 1329 def color_of_part(keyword, newcolor) 1330 tk_call(@chart, 'color', keyword, newcolor) 1331 self 1332 end 1333 1334 def font_of_part(keyword, newfont) 1335 tk_call(@chart, 'font', keyword, newfont) 1336 self 1337 end 1338 1339 def hscroll=(scr) 1340 tk_call_without_enc(@chart, 'hscroll', scr) 1341 scr 1342 end 1343 def hscroll(scr) 1344 tk_call_without_enc(@chart, 'hscroll', scr) 1345 self 1346 end 1347 1348 def vscroll=(scr) 1349 tk_call_without_enc(@chart, 'vscroll', scr) 1350 scr 1351 end 1352 def vscroll(scr) 1353 tk_call_without_enc(@chart, 'vscroll', scr) 1354 self 1355 end 1356 end 1357 1358 ############################ 1359 class PlotSeries < TkObject 1360 SeriesID_TBL = TkCore::INTERP.create_table 1361 1362 (Series_ID = ['series'.freeze, TkUtil.untrust('00000')]).instance_eval{ 1363 @mutex = Mutex.new 1364 def mutex; @mutex; end 1365 freeze 1366 } 1367 TkCore::INTERP.init_ip_env{ 1368 SeriesID_TBL.mutex.synchronize{ SeriesID_TBL.clear } 1369 } 1370 1371 def self.id2obj(chart, id) 1372 path = chart.path 1373 SeriesID_TBL.mutex.synchronize{ 1374 if SeriesID_TBL[path] 1375 SeriesID_TBL[path][id]? SeriesID_TBL[path][id]: id 1376 else 1377 id 1378 end 1379 } 1380 end 1381 1382 def initialize(chart, keys=nil) 1383 @parent = @chart_obj = chart 1384 @ppath = @chart_obj.path 1385 Series_ID.mutex.synchronize{ 1386 @path = @series = @id = Series_ID.join(TkCore::INTERP._ip_id_) 1387 Series_ID[1].succ! 1388 } 1389 SeriesID_TBL.mutex.synchronize{ 1390 SeriesID_TBL[@ppath] ||= {} 1391 SeriesID_TBL[@ppath][@id] = self 1392 } 1393 dataconfig(keys) if keys.kind_of?(Hash) 1394 end 1395 1396 def plot(*args) 1397 @chart_obj.plot(@series, *args) 1398 end 1399 1400 def dataconfig(key, value=None) 1401 @chart_obj.dataconfig(@series, key, value) 1402 end 1403 end 1404end 1405