File Coverage

blib/lib/Lab/Moose/DataFile/Gnuplot.pm
Criterion Covered Total %
statement 113 302 37.4
branch 14 78 17.9
condition 0 21 0.0
subroutine 20 27 74.0
pod 6 7 85.7
total 153 435 35.1


line stmt bran cond sub pod time code
1             package Lab::Moose::DataFile::Gnuplot;
2             $Lab::Moose::DataFile::Gnuplot::VERSION = '3.900';
3             #ABSTRACT: Text based data file ('Gnuplot style')
4              
5 5     5   4064 use v5.20;
  5         22  
6              
7 5     5   37 use Moose;
  5         11  
  5         47  
8 5     5   34590 use MooseX::Params::Validate;
  5         11  
  5         43  
9 5     5   2338 use Moose::Util::TypeConstraints 'enum';
  5         15  
  5         61  
10 5     5   2306 use PDL::Core qw/topdl/;
  5         12  
  5         139  
11 5     5   581 use Data::Dumper;
  5         12  
  5         402  
12 5     5   36 use Carp;
  5         10  
  5         309  
13 5     5   44 use Scalar::Util 'looks_like_number';
  5         12  
  5         259  
14 5     5   42 use Module::Load 'load';
  5         10  
  5         49  
15 5     5   1576 use Lab::Moose::DataFile::Read;
  5         13  
  5         280  
16 5     5   35 use List::Util 'any';
  5         17  
  5         336  
17 5     5   48 use Time::HiRes 'time';
  5         12  
  5         56  
18 5     5   549 use namespace::autoclean;
  5         11  
  5         59  
19              
20             extends 'Lab::Moose::DataFile';
21              
22             has columns => (
23             is => 'ro',
24             isa => 'ArrayRef[Str]',
25             required => 1,
26             );
27              
28             has num_data_rows => (
29             is => 'ro',
30             isa => 'Int',
31             default => 0,
32             writer => '_num_data_rows',
33             init_arg => undef
34             );
35              
36             has num_blocks => (
37             is => 'ro',
38             isa => 'Int',
39             default => 0,
40             writer => '_num_blocks',
41             init_arg => undef,
42             );
43              
44             has precision => (
45             is => 'ro',
46             isa => enum( [ 1 .. 17 ] ),
47             default => 10,
48             );
49              
50             has plots => (
51             is => 'ro',
52             isa => 'ArrayRef',
53             default => sub { [] },
54             init_arg => undef
55             );
56              
57             has comment_string => (
58             is => 'rw',
59             isa => 'Str',
60             default => ' ',
61             );
62              
63             sub BUILD {
64 37     37 0 102 my $self = shift;
65 37         90 my @columns = @{ $self->columns() };
  37         1056  
66 37 50       144 if ( @columns == 0 ) {
67 0         0 croak "need at least one column";
68             }
69 37         1184 $self->log_comment( comment => $self->comment_string().join( "\t", @columns ) );
70             }
71              
72              
73             sub log {
74 137     137 1 356 my $self = shift;
75 137         455 $self->_log_bare(@_);
76 137         485 $self->refresh_plots( refresh => 'point' );
77             }
78              
79             # Log of one row of data. Do not trigger plots.
80             sub _log_bare {
81              
82             # We do not use MooseX::Params::Validate for performance reasons.
83 172     172   282 my $self = shift;
84 172         287 my %args;
85              
86 172 50       434 if ( ref $_[0] eq 'HASH' ) {
87 0         0 %args = %{ $_[0] };
  0         0  
88             }
89             else {
90 172         450 %args = @_;
91             }
92              
93 172         273 my @columns = @{ $self->columns() };
  172         5249  
94              
95 172         337 my $line = "";
96              
97 172         718 while ( my ( $idx, $column ) = each(@columns) ) {
98 445         955 my $value = delete $args{$column};
99 445 50       906 if ( not defined $value ) {
100 0         0 croak "missing value for column '$column'";
101             }
102 445 50       1147 if ( not looks_like_number($value) ) {
103 0         0 croak "value '$value' for column '$column' isn't numeric";
104             }
105 445         12889 my $precision = $self->precision();
106 445         1989 $line .= sprintf( "%.${precision}g", $value );
107 445 100       1360 if ( $idx != $#columns ) {
108 273         930 $line .= "\t";
109             }
110             }
111 172         312 $line .= "\n";
112              
113 172 50       411 if ( keys %args ) {
114 0         0 croak "unknown colums in log call: ", join( ' ', keys %args );
115             }
116              
117 172         4894 my $fh = $self->filehandle();
118 172         263 print {$fh} $line;
  172         4042  
119              
120 172         6972 my $num = $self->num_data_rows;
121 172         5895 $self->_num_data_rows( ++$num );
122             }
123              
124              
125             sub log_block {
126 7     7 1 15 my $self = shift;
127 7         52 my ( $prefix, $block, $add_newline, $refresh_plots ) = validated_list(
128             \@_,
129             prefix => { isa => 'HashRef[Num]', optional => 1 },
130             block => {},
131             add_newline => { isa => 'Bool', default => 0 },
132             refresh_plots => { isa => 'Bool', default => 0 },
133             );
134              
135 7         7042 $block = topdl($block);
136              
137 7         912 my @dims = $block->dims();
138              
139 7 50       30 if ( @dims == 1 ) {
    50          
140 0         0 $block = $block->dummy(1);
141 0         0 @dims = $block->dims();
142             }
143             elsif ( @dims != 2 ) {
144 0         0 croak "log_block needs 1D or 2D piddle";
145             }
146              
147 7 50       19 my $num_prefix_cols = $prefix ? ( keys %{$prefix} ) : 0;
  7         17  
148 7         11 my $num_block_cols = $dims[1];
149              
150 7         11 my @columns = @{ $self->columns() };
  7         255  
151              
152 7         12 my $num_cols = @columns;
153              
154 7 50       23 if ( $num_prefix_cols + $num_block_cols != $num_cols ) {
155 0         0 croak "need $num_cols columns, got $num_prefix_cols prefix columns"
156             . " and $num_block_cols block columns";
157             }
158              
159 7         10 my $num_rows = $dims[0];
160 7         23 for my $i ( 0 .. $num_rows - 1 ) {
161 35         61 my %log;
162              
163             # Add prefix columns to %log.
164 35         74 for my $j ( 0 .. $num_prefix_cols - 1 ) {
165 35         59 my $name = $columns[$j];
166 35         88 $log{$name} = $prefix->{$name};
167             }
168              
169             # Add block columns to %log.
170 35         61 for my $j ( 0 .. $num_block_cols - 1 ) {
171 70         539 my $name = $columns[ $j + $num_prefix_cols ];
172 70         201 $log{$name} = $block->at( $i, $j );
173             }
174 35         425 $self->_log_bare(%log);
175             }
176              
177 7 50       107 if ($add_newline) {
    50          
178 0         0 $self->new_block();
179             }
180             elsif ($refresh_plots) {
181 0         0 $self->refresh_plots( refresh => 'block' );
182 0         0 $self->refresh_plots( refresh => 'point' );
183             }
184             }
185              
186              
187             sub new_block {
188 19     19 1 40 my $self = shift;
189 19         515 my $fh = $self->filehandle;
190 19         38 print {$fh} "\n";
  19         343  
191 19         827 $self->_num_blocks( $self->num_blocks + 1 );
192 19         72 $self->refresh_plots( refresh => 'block' );
193 19         50 $self->refresh_plots( refresh => 'point' );
194             }
195              
196              
197             sub log_comment {
198 38     38 1 79 my $self = shift;
199 38         202 my ($comment) = validated_list(
200             \@_,
201             comment => { isa => 'Str' }
202             );
203 38         7700 my @lines = split( "\n", $comment );
204 38         1211 my $fh = $self->filehandle();
205 38         109 for my $line (@lines) {
206 38         76 print {$fh} "#$line\n";
  38         3043  
207             }
208             }
209              
210              
211             sub _add_plot_handle {
212 0     0   0 my ( $self, %args ) = validated_hash(
213             \@_,
214             plot => { isa => 'Lab::Moose::Plot' },
215             type => { isa => enum( [qw/2d pm3d/] ) },
216             curves => { isa => 'ArrayRef[HashRef]' },
217             legend => { isa => 'Maybe[Str]', optional => 1 },
218             refresh => { isa => 'Str' },
219             refresh_interval => { isa => 'Lab::Moose::PosNum' },
220             );
221              
222 0         0 my $plots = $self->plots();
223              
224 0         0 push @{$plots}, { %args, last_refresh_time => time() };
  0         0  
225             }
226              
227             sub _add_2d_plot {
228 0     0   0 my ( $self, %args ) = validated_hash(
229             \@_,
230             x => { isa => 'Str', optional => 1 },
231             y => { isa => 'Str', optional => 1 },
232             curves => { isa => 'ArrayRef[HashRef]', optional => 1 },
233             terminal => { isa => 'Str', optional => 1 },
234             terminal_options => { isa => 'HashRef', optional => 1 },
235             plot_options => { isa => 'HashRef', default => {} },
236             curve_options => { isa => 'HashRef', default => {} },
237             legend => { isa => 'Str', optional => 1 },
238             refresh => { isa => 'Str', default => 'point' },
239             refresh_interval => { isa => 'Lab::Moose::PosNum', default => 0 },
240             );
241              
242 0         0 my $error_msg = "Provide either 'x/y' or 'curves' arguments";
243 0         0 my $x_column = delete $args{x};
244 0         0 my $y_column = delete $args{y};
245 0         0 my $legend_column = delete $args{legend};
246 0         0 my $curves = delete $args{curves};
247              
248 0         0 my %default_curve_options = (
249             with => 'points',
250             );
251              
252             $args{curve_options}
253 0         0 = { %default_curve_options, %{ $args{curve_options} } };
  0         0  
254              
255 0 0       0 if ( defined $x_column ) {
256 0 0       0 if ( not defined $y_column ) {
257 0         0 croak $error_msg;
258             }
259             $curves = [
260             {
261             x => $x_column, y => $y_column,
262             curve_options => $args{curve_options}
263             }
264 0         0 ];
265             }
266             else {
267 0 0       0 if ( not defined $curves ) {
268 0         0 croak $error_msg;
269             }
270             }
271              
272 0         0 my $refresh = delete $args{refresh};
273 0         0 my $refresh_interval = delete $args{refresh_interval};
274              
275             my %default_plot_options = (
276             xlabel => $curves->[0]->{x},
277             ylabel => $curves->[0]->{y},
278 0         0 title => $self->path(),
279             grid => 1,
280             );
281 0         0 $args{plot_options} = { %default_plot_options, %{ $args{plot_options} } };
  0         0  
282              
283 0         0 my @check_columns = ();
284 0         0 for my $curve ( @{$curves} ) {
  0         0  
285 0         0 push @check_columns, ( $curve->{x}, $curve->{y} );
286             }
287 0 0       0 if ( defined $legend_column ) {
288 0         0 push @check_columns, $legend_column;
289             }
290 0         0 for my $column (@check_columns) {
291 0 0   0   0 if ( not any { $column eq $_ } @{ $self->columns } ) {
  0         0  
  0         0  
292 0         0 croak "column $column does not exist";
293             }
294             }
295              
296 0         0 my $plot = Lab::Moose::Plot->new(%args);
297              
298 0         0 $self->_add_plot_handle(
299             plot => $plot,
300             type => '2d',
301             curves => $curves,
302             legend => $legend_column,
303             refresh => $refresh,
304             refresh_interval => $refresh_interval,
305             );
306             }
307              
308             sub _add_pm3d_plot {
309 0     0   0 my ( $self, %args ) = validated_hash(
310             \@_,
311             x => { isa => 'Str', optional => 1 },
312             y => { isa => 'Str', optional => 1 },
313             z => { isa => 'Str', optional => 1 },
314             curves => { isa => 'ArrayRef[HashRef]', optional => 1 },
315             terminal => { isa => 'Str', optional => 1 },
316             terminal_options => { isa => 'HashRef', optional => 1 },
317             plot_options => { isa => 'HashRef', default => {} },
318             curve_options => { isa => 'HashRef', default => {} },
319             refresh => { isa => 'Str', default => 'block' },
320             refresh_interval => { isa => 'Lab::Moose::PosNum', default => 0 },
321             );
322              
323 0         0 my $error_msg = "Provide either 'x/y/z' or 'curves' arguments";
324 0         0 my $x_column = delete $args{x};
325 0         0 my $y_column = delete $args{y};
326 0         0 my $z_column = delete $args{z};
327 0         0 my $curves = delete $args{curves};
328              
329 0 0       0 if ( defined $x_column ) {
330 0 0 0     0 if ( not defined $y_column or not defined $z_column ) {
331 0         0 croak $error_msg;
332             }
333 0         0 $curves = [ { x => $x_column, y => $y_column, z => $z_column } ];
334             }
335             else {
336 0 0       0 if ( not defined $curves ) {
337 0         0 croak $error_msg;
338             }
339             }
340              
341 0         0 my $refresh = delete $args{refresh};
342 0         0 my $refresh_interval = delete $args{refresh_interval};
343              
344 0         0 my %default_plot_options = (
345             pm3d => 'implicit map corners2color c1',
346             surface => 0,
347             xlabel => $x_column,
348             ylabel => $y_column,
349             title => $self->path(),
350             grid => 1,
351              
352             # border => '4095 front linetype -1 linewidth 1.000');
353             );
354 0         0 my %default_curve_options = ();
355              
356 0         0 $args{plot_options} = { %default_plot_options, %{ $args{plot_options} } };
  0         0  
357             $args{curve_options}
358 0         0 = { %default_curve_options, %{ $args{curve_options} } };
  0         0  
359              
360 0         0 for my $curve ( @{$curves} ) {
  0         0  
361 0         0 for my $column ( $curve->{x}, $curve->{y}, $curve->{z} ) {
362 0 0   0   0 if ( not any { $column eq $_ } @{ $self->columns } ) {
  0         0  
  0         0  
363 0         0 croak "column $column does not exist";
364             }
365             }
366             }
367              
368 0         0 my $plot = Lab::Moose::Plot->new(%args);
369 0         0 $self->_add_plot_handle(
370             plot => $plot,
371             type => 'pm3d',
372             curves => $curves,
373             refresh => $refresh,
374             refresh_interval => $refresh_interval,
375             );
376             }
377              
378             sub add_plot {
379 0     0 1 0 my ( $self, %args ) = validated_hash(
380             \@_,
381             type => { isa => 'Str', default => 'points' },
382             live => { isa => 'Bool', default => 1 }, # only for testing
383             hard_copy => { isa => 'Str', optional => 1 },
384             hard_copy_suffix => { isa => 'Str', optional => 1 },
385             hard_copy_terminal => { isa => 'Str', optional => 1 },
386             hard_copy_terminal_options => { isa => 'HashRef', default => {} },
387             terminal_options => { isa => 'HashRef', default => {} },
388             MX_PARAMS_VALIDATE_ALLOW_EXTRA => 1,
389             );
390              
391             # only load PDL::Graphics::Gnuplot when needed. No gnuplot is needed
392             # unless 'add_plot' is called.
393 0         0 load 'Lab::Moose::Plot';
394              
395 0         0 my $type = delete $args{type};
396 0         0 my $hard_copy = delete $args{hard_copy};
397 0         0 my $hard_copy_suffix = delete $args{hard_copy_suffix};
398 0         0 my $terminal = $args{terminal};
399 0   0     0 my $hard_copy_terminal = delete $args{hard_copy_terminal} // 'png';
400 0         0 my $hard_copy_terminal_options = delete $args{hard_copy_terminal_options};
401 0         0 my $live = delete $args{live};
402              
403 0 0 0     0 if ( defined $hard_copy and defined $hard_copy_suffix ) {
404 0         0 croak "Give either 'hard_copy' or 'hard_copy_suffix' parameter";
405             }
406              
407 0 0       0 if ( not defined $hard_copy ) {
408 0         0 $hard_copy = $self->filename();
409              
410             # Remove suffix
411 0         0 $hard_copy =~ s/\.\w*$//;
412 0 0       0 if ( defined $hard_copy_suffix ) {
413 0         0 $hard_copy .= $hard_copy_suffix;
414             }
415 0         0 $hard_copy .= ".$hard_copy_terminal";
416             }
417              
418 0         0 my %default_terminal_options;
419 0 0 0     0 if ( not( defined $terminal and $terminal eq 'dumb' ) ) {
420 0         0 %default_terminal_options
421             = ( enhanced => 0, raise => 0, persist => 1 );
422             }
423              
424             $args{terminal_options}
425 0         0 = { %default_terminal_options, %{ $args{terminal_options} } };
  0         0  
426              
427             # Set enhanced to 0: png terminal needs to draw underscores in title
428 0         0 my %default_hard_copy_terminal_options = ( enhanced => 0 );
429             $hard_copy_terminal_options = {
430             %default_hard_copy_terminal_options,
431 0         0 %{$hard_copy_terminal_options}
  0         0  
432             };
433              
434 0         0 my $plot_generator_sub;
435 0 0       0 if ( $type =~ /points?/i ) {
    0          
436 0         0 $plot_generator_sub = '_add_2d_plot';
437             }
438             elsif ( $type =~ /pm3d/i ) {
439 0         0 $plot_generator_sub = '_add_pm3d_plot';
440             }
441             else {
442 0         0 croak "unknown plot type '$type'";
443             }
444              
445 0 0       0 if ($live) {
446 0         0 $self->$plot_generator_sub(%args);
447             }
448              
449             # add hard copy plot. Use Lab::Moose::DataFile to ensure that
450             # no filename is used twice and content is overwritten.
451 0         0 my $hard_copy_file = Lab::Moose::DataFile->new(
452             folder => $self->folder(),
453             filename => $hard_copy,
454             );
455              
456 0         0 delete $args{terminal};
457 0         0 delete $args{terminal_options};
458 0         0 my $hard_copy_path = $hard_copy_file->path();
459 0 0       0 if ( $hard_copy_path =~ /\\/ ) {
460 0         0 carp
461             "gnuplot on windows has problems with '\\' character. (hard copy filename: $hard_copy_path";
462             }
463             $self->$plot_generator_sub(
464             terminal => $hard_copy_terminal,
465             terminal_options => {
466 0         0 output => $hard_copy_file->path(), %{$hard_copy_terminal_options}
  0         0  
467             },
468             %args,
469             );
470             }
471              
472             sub _refresh_plot {
473 0     0   0 my $self = shift;
474 0         0 my ( $index, $force ) = validated_list(
475             \@_,
476             index => { isa => 'Int' },
477             force => { isa => 'Bool' },
478             );
479 0         0 my $plots = $self->plots();
480 0         0 my $plot = $plots->[$index];
481              
482             # Is it time to replot?
483 0 0 0     0 if ( not $force
484             and time() - $plot->{last_refresh_time} < $plot->{refresh_interval} )
485             {
486 0         0 return;
487             }
488 0         0 $plot->{last_refresh_time} = time();
489              
490 0 0       0 if ( not defined $plot ) {
491 0         0 croak "no plot with name at index $index";
492             }
493              
494 0         0 my $type = $plot->{type};
495 0         0 my $legend_column = $plot->{legend};
496 0         0 my @curves = @{ $plot->{curves} };
  0         0  
497 0         0 my $column_names = $self->columns();
498 0         0 my $num_columns = @{ $self->columns() };
  0         0  
499              
500 0 0       0 if ( $self->num_data_rows() < 2 ) {
501 0         0 return;
502             }
503              
504 0         0 my @data;
505              
506 0 0       0 if ( $type eq '2d' ) {
    0          
507 0         0 my $blocks = read_gnuplot_format(
508             type => 'bare',
509             fh => $self->filehandle(),
510             num_columns => $num_columns,
511             );
512              
513             # $block is 3d PDL with dimensions (column, line, block)
514              
515             # split along last dimension
516 0         0 my @blocks = $blocks->dog();
517              
518 0         0 my $legend_index;
519 0 0       0 if ( defined $legend_column ) {
520             ($legend_index)
521 0         0 = grep { $column_names->[$_] eq $legend_column }
522 0         0 0 .. $#{$column_names};
  0         0  
523             }
524              
525             # draw separate line for each curve
526 0         0 for my $curve (@curves) {
527 0         0 my $x = $curve->{x};
528 0         0 my $y = $curve->{y};
529 0   0     0 my $curve_options = $curve->{curve_options} // {};
530             my ($x_index)
531 0         0 = grep { $column_names->[$_] eq $x } 0 .. $#{$column_names};
  0         0  
  0         0  
532             my ($y_index)
533 0         0 = grep { $column_names->[$_] eq $y } 0 .. $#{$column_names};
  0         0  
  0         0  
534              
535             # draw separate line for each block
536 0         0 for my $block (@blocks) {
537 0         0 my %curve_options = %{$curve_options};
  0         0  
538 0 0       0 if ( defined $legend_column ) {
539 0         0 my $legend_value = $block->at( $legend_index, 0 );
540 0         0 %curve_options = ( %curve_options, legend =>
541             sprintf( "$legend_column = %g", $legend_value ) );
542             }
543 0         0 push @data,
544             (
545             {%curve_options}, $block->slice("$x_index,:")->flat,
546             $block->slice("$y_index,:")->flat
547             );
548              
549             }
550             }
551 0         0 $plot->{plot}->plot( data => \@data );
552             }
553             elsif ( $type eq 'pm3d' ) {
554 0 0       0 if ( $self->num_blocks < 2 ) {
555 0         0 return;
556             }
557 0         0 my @pixel_fields = read_gnuplot_format(
558             type => 'maps',
559             fh => $self->filehandle(),
560             num_columns => $num_columns,
561             );
562 0         0 for my $curve (@curves) {
563 0         0 my $x = $curve->{x};
564 0         0 my $y = $curve->{y};
565 0         0 my $z = $curve->{z};
566 0   0     0 my $curve_options = $curve->{curve_options} // {};
567             my ($x_index)
568 0         0 = grep { $column_names->[$_] eq $x } 0 .. $#{$column_names};
  0         0  
  0         0  
569             my ($y_index)
570 0         0 = grep { $column_names->[$_] eq $y } 0 .. $#{$column_names};
  0         0  
  0         0  
571             my ($z_index)
572 0         0 = grep { $column_names->[$_] eq $z } 0 .. $#{$column_names};
  0         0  
  0         0  
573              
574 0         0 push @data,
575             (
576             $curve_options,
577             @pixel_fields[ $x_index, $y_index, $z_index ]
578             );
579             }
580 0         0 $plot->{plot}->splot( data => \@data );
581             }
582             else {
583 0         0 croak "unknown plot type '$type'";
584             }
585              
586             }
587              
588              
589             sub refresh_plots {
590 171     171 1 313 my $self = shift;
591 171         1180 my ( $refresh, $force ) = validated_list(
592             \@_,
593             refresh => { isa => 'Str', optional => 1 },
594             force => { isa => 'Bool', default => 0 },
595             );
596              
597 171         31230 my @plots = @{ $self->plots() };
  171         5461  
598              
599 171         311 my @indices;
600              
601 171 50       391 if ( defined $refresh ) {
602 171         527 for my $index ( 0 .. $#plots ) {
603 0         0 my $plot = $plots[$index];
604 0 0 0     0 if ( defined $plot->{refresh} and $plot->{refresh} eq $refresh ) {
605 0         0 push @indices, $index;
606             }
607             }
608             }
609              
610             else {
611 0         0 @indices = ( 0 .. $#plots );
612             }
613              
614 171         811 for my $index (@indices) {
615 0           $self->_refresh_plot( index => $index, force => $force );
616             }
617             }
618              
619             __PACKAGE__->meta->make_immutable();
620              
621             1;
622              
623             __END__
624              
625             =pod
626              
627             =encoding UTF-8
628              
629             =head1 NAME
630              
631             Lab::Moose::DataFile::Gnuplot - Text based data file ('Gnuplot style')
632              
633             =head1 VERSION
634              
635             version 3.900
636              
637             =head1 SYNOPSIS
638              
639             use Lab::Moose;
640              
641             my $folder = datafolder();
642              
643             # datafile with two simple 2D plots:
644              
645             my $file = datafile(
646             type => 'Gnuplot',
647             folder => $folder,
648             filename => 'gnuplot-file.dat',
649             columns => [qw/time voltage temp/]
650             );
651              
652             # add live plot
653             $file->add_plot(
654             x => 'time',
655             y => 'voltage',
656             curve_options => {with => 'points'},
657             );
658            
659             $file->add_plot(
660             x => 'time',
661             y => 'temp',
662             hard_copy => 'gnuplot-file-time-temp.png'
663             );
664              
665             # or both curves in one plot
666             $file->add_plot(
667             curves => [
668             {x => 'time', y => 'voltage'},
669             {x => 'time', y => 'temp', curve_options => {axes => 'x1y2'}}
670             ]
671             );
672              
673             $file->log(time => 1, voltage => 2, temp => 3);
674              
675             # datafile with pm3d plot
676             my $datafile = datafile(
677             type => 'Gnuplot',
678             folder => datafolder(),
679             filename => 'data.dat',
680             columns => [qw/x y z/],
681             comment_string => "COLUMNS#\t"
682             );
683              
684             $datafile->add_plot(
685             type => 'pm3d',
686             x => 'x',
687             y => 'y',
688             z => 'z',
689             hard_copy => 'data.png',
690             );
691            
692            
693             for my $x (0..100) {
694             for my $y (0..100) {
695             $datafile->log(x => $x, y => $y, z => rand());
696             }
697             $datafile->new_block();
698             }
699              
700             =head1 METHODS
701              
702             =head2 new
703              
704             Supports the following attributtes in addition to the L<Lab::Moose::DataFile>
705             requirements:
706              
707             =over
708              
709             =item * columns
710              
711             (mandatory) arrayref of column names
712              
713             =item * precision
714              
715             The numbers are formatted with a C<%.${precision}g> format specifier. Default
716             is 10.
717              
718             =back
719              
720             =head2 log
721              
722             $file->log(column1 => $value1, column2 => $value2, ...);
723              
724             Log one line of data.
725              
726             =head2 log_block
727              
728             $file->log_block(
729             prefix => {column1 => $value1, ...},
730             block => $block,
731             add_newline => 1
732             );
733              
734             Log a 1D or 2D PDL or array ref. The first dimension runs over the datafile
735             rows. You can add prefix columns, which will be the same for each line in the
736             block. E.g. when using a spectrum analyzer inside a voltage sweep, one would
737             log the returned PDL prefixed with the sweep voltage.
738              
739             =head2 new_block
740              
741             $file->new_block()
742              
743             print "\n" to the datafile.
744              
745             =head2 log_comment
746              
747             $file->log_comment(comment => $string);
748              
749             log a comment string, which will be prefixed with '#'. If C<$string> contains
750             newline characters, several lines of comments will be written.
751              
752             =head2 add_plot
753              
754             $file->add_plot(
755             type => 'pm3d',
756             x => 'x-column',
757             y => 'y-column',
758             z => 'z-column',
759             plot_options => {grid => 1},
760             hard_copy => 'myplot.png',
761             hard_copy_terminal => 'svg',
762             );
763              
764             Add a new live plot to the datafile. Options:
765              
766             =over
767              
768             =item * type
769              
770             Supported types: C<points (default), pm3d>.
771              
772             =item * x (mandatory)
773              
774             Name of the column which is used for the x-axis.
775              
776             =item * y (mandatory)
777              
778             Name of the column which is used for the y-axis.
779              
780             =item * z (mandatory for 'pm3d' plot type)
781              
782             Name of the column which is used tor the cb-axis in a pm3d plot.
783              
784             =item * legend (only for '2d' plot type)
785              
786             For datafiles with multiple blocks, name of the column which is used to label the curves (See example in L<Lab::Measurement::Tutorial>).
787              
788             =item * terminal
789              
790             gnuplot terminal. If not set, use default gnuplot terminal.
791              
792             =item * terminal_options
793              
794             HashRef of terminal options. This defaults to
795             C<< {persist => 1, raise => 0, enhanced => 0} >>.
796             The different graphical terminal types (qt,x11,wxt) show different behaviour
797             regarding persistent windows. For wxt, the plot windows are closed when
798             the datafile object goes out of scope. To prevent this, push each new
799             datafile object onto a global array.
800              
801             =item * plot_options
802              
803             HashRef of plotting options (See L<PDL::Graphics::Gnuplot> for the complete
804             list). Those are appended to the default plot options.
805              
806             =item * curve_options
807              
808             HashRef of curve options (See L<PDL::Graphics::Gnuplot> for the complete
809             list).
810              
811             =item * refresh
812              
813             Set this to a string, if you need to refresh the plot manually with the
814             C<refresh_plots> option. Multiple plots can share the same refresh handle
815             string.
816              
817             Predefined refresh types:
818              
819             =over
820              
821             =item * 'point'
822              
823             Default for 2D plots. Replot for each new row.
824              
825             =item * 'block'
826              
827             Default for 3D plots. Replot when finishing a block.
828              
829             =back
830              
831             =item * refresh_interval
832              
833             Minimum time between replots. Default: replot as often as C<refresh> attribute allows.
834              
835             =item * hard_copy
836              
837             Filename for the copy of the plot in the data folder. Default: Switch datafile
838             filename suffix of datafile to the $terminal, e.g. F<data.dat> =>
839             F<data.png>. Mandatory if you add multiple plots to one datafile.
840              
841             =item * hard_copy_suffix
842              
843             Filename suffix for the copy of the plot in the data folder. The filename off the copy will be basename off the datafile with this suffix added.
844              
845             =item * hard_copy_terminal
846              
847             Terminal for hard_copy option. Use png terminal by default. The 'output'
848             terminal option must be supported.
849              
850             =item * live
851              
852             Set to false to only create the hardcopy and no live plot.
853              
854             $file->add_plot(
855             live => 0,
856             ...,
857             );
858              
859             =back
860              
861             =head2 refresh_plots
862              
863             $file->refresh_plots(refresh => $refresh_type);
864             # or
865             $file->refresh_plots();
866              
867             Call C<refresh_plot> for each plot with hanle C<$handle>.
868              
869             If the C<handle> argument is not given, refresh all plots.
870              
871             =head1 COPYRIGHT AND LICENSE
872              
873             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
874              
875             Copyright 2016 Simon Reinhardt
876             2017 Andreas K. Huettel, Simon Reinhardt
877             2018-2019 Simon Reinhardt
878             2020 Andreas K. Huettel
879             2021 Simon Reinhardt
880             2022 Mia Schambeck
881              
882              
883             This is free software; you can redistribute it and/or modify it under
884             the same terms as the Perl 5 programming language system itself.
885              
886             =cut