Module: CTioga::Themes

Included in:
PlotMaker
Defined in:
lib/CTioga/themes.rb,
lib/CTioga/themes/demo.rb,
lib/CTioga/themes/fits.rb,
lib/CTioga/themes/mono.rb,
lib/CTioga/themes/classical.rb

Overview

The namespace for themes. Supposedly, all themes should end up here.

Defined Under Namespace

Classes: BaseTheme, ClassicalTheme, DemoTheme, FitsTheme, MonoTheme, PastelTheme

Constant Summary collapse

DisableRe =
/no(ne)?|off/i
StyleTypes =

A correspondance style_element -> style type

{
  :color => :color,
  :colors => :color,
  :marker => :marker,
  :markers => :marker,
  :marker_color => :color,
  :markers_colors => :color,
  :line_style => :line_style,
  :linestyle => :line_style,
  :linewidth => :float,
  :error_bar_color => :color,
  :marker_scale => :float,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#histogramObject

Returns the value of attribute histogram.



73
74
75
# File 'lib/CTioga/themes.rb', line 73

def histogram
  @histogram
end

#override_styleObject

Now, some code to be used as an inclusion directly.



72
73
74
# File 'lib/CTioga/themes.rb', line 72

def override_style
  @override_style
end

Instance Method Details

#choose_theme(theme_name) ⇒ Object

Selects the current theme:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/CTioga/themes.rb', line 121

def choose_theme(theme_name)
  if BaseTheme::THEMES.key?(theme_name)
    @theme_name = theme_name
    @theme = BaseTheme::THEMES[theme_name].new
    info "Selecting theme #{theme_name}"
    args = @theme.cmdline_extra_args
    debug "Theme #{theme_name} pushes #{args.join ' '} onto the "+
      "command line"
    unshift_cmdline_args(*args)
    return true
  else
    warn "Theme #{theme_name} doesn't exist, ignoring"
    return false
  end
end

#current_themeObject

The current theme:



541
542
543
# File 'lib/CTioga/themes.rb', line 541

def current_theme
  return @theme
end

#get_current_style(set) ⇒ Object

Returns the current style and resets all pending elements.



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
# File 'lib/CTioga/themes.rb', line 547

def get_current_style(set)
  debug "Legend: #{override_style.legend}"

  # We look in @restored_style first.
  style = @restored_style || 
    @theme.next_curve_style(tex_quote_text(set))
  @restored_style = nil     # Canceled everytime.

  style = style.dup         # To make sure we don't overwrite anything.
  if @autolegends 
    style.legend = tex_quote_text(set)
  else
    style.legend = false
  end
  debug "Override is #{override_style.inspect}"
  style.override!(override_style)

  # We remove the legend information, that it doesn't get used
  # again.
  override_style.delete(:legend)
  debug "Style: #{style.inspect}"

  # We push the last used style on the stack
  @style_stack << style
  return style
end

#initialize_themesObject



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
# File 'lib/CTioga/themes.rb', line 85

def initialize_themes
  # Initialize override_style
  reset_override_style

  # Start with an inexistant style to restore
  @restored_style = nil

  # The style stack; every single style used is pushed there
  @style_stack = []

  # The hash used for named saves
  @saved_styles = {}

  # And for saved overrides
  @saved_overrides = {}
  

  # We look for themes in the themes subdirectory of the directory
  # where this file is found and in the $HOME/.ctioga/themes
  for f in Dir.glob(File.dirname(__FILE__) + "/themes/*") +
      Dir.glob("#{ENV['HOME']}/.ctioga/themes/*")
    begin
      require f
      info "Successfully loaded theme file #{f}"
    rescue Exception => e
      warn "Failed to load theme file #{f}, ignoring"
      debug "Failed to load theme #{f} with #{e.inspect}"
    end
  end

  # The current theme:
  choose_theme(BaseTheme::THEMES.keys.first) unless
    choose_theme('Classical')
end

#reset_override_styleObject

Resets the override_style to default:



77
78
79
80
81
82
83
# File 'lib/CTioga/themes.rb', line 77

def reset_override_style
  @override_style = CurveStyle.new

  # To reproduce old ctioga behavior, we manually set
  # :marker to false
  @override_style[:marker] = false
end

#style_argument(style_element, value) ⇒ Object

Parses a style argument, in three different ways:

  • an ‘auto’ will remove the override

  • something matching DisableRe will turn it false

  • anything else will be converted using the appropriate type found in StyleTypes or using the block given



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
# File 'lib/CTioga/themes.rb', line 161

def style_argument(style_element, value)
  if value =~ /auto/i
    override_style.delete(style_element)
    debug "Deleting override #{style_element}"
    return
  elsif value =~ DisableRe or not value
    override_style[style_element] = false
  else
    override_style[style_element] = 
      if block_given?
        yield(value)
      else
        begin
          MetaBuilder::ParameterType.from_string(StyleTypes[style_element],
                                                 value)
        rescue Exception => e
          if e.is_a? MetaBuilder::ParameterType::IncorrectInput
            warn "The argument for #{style_element} was not recognized:"
            warn e.message
            warn "Expect problems later"
          end
          value
        end
      end
  end
  debug "Setting override #{style_element} to " +
    "#{override_style[style_element].inspect}"
end

#theme_bod_hook(t) ⇒ Object

Run the hook at beginning of the drawing:



575
576
577
# File 'lib/CTioga/themes.rb', line 575

def theme_bod_hook(t)
  current_theme.bod_hook(t)
end

#theme_prepare_parser(parser) ⇒ Object



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
# File 'lib/CTioga/themes.rb', line 191

def theme_prepare_parser(parser)
  parser.separator "\nStyle and themes options"
  parser.on("-c", "--[no-]color COLOR",
            "Sets the color for drawing the curves.", 
            "Use 'auto' to leave the decision to ",
            "the themes, and 'no' to get no lines.") do |val|
    style_argument(:color, val)
  end

  parser.on("-m", "--[no-]marker [MARKER]",
             "Sets the markers for drawing data points", 
             "Use 'auto' to get automatic markers,",
             "and 'no' to get no marker (the default)") do |val|
    style_argument(:marker, val)
  end
  
  parser.on("--marker-color COLOR",
             "Sets the markers' color. See also --color") do |val|
    style_argument(:marker_color, val)
  end

  parser.on("--[no-]line-style STYLE",
            "Sets the line style") do |val|
    style_argument(:line_style, val)
  end

  parser.on("--line-width WIDTH",
            "Sets the line width") do |w|
    style_argument(:linewidth, w) do |val|
      Float(val)
    end
  end

  parser.on("--[no-]interpolate",
            "If set, the points will be joined", "by a nice "+
            "interpolated curve") do |w|
    override_style.interpolate = w
  end

  parser.on("--marker-scale SCALE",
            "The scale of the markers used for curves.", 
            "Defaults to 0.5"
             ) do |w|
    style_argument(:marker_scale, w)
  end

  parser.on("--error-bar-color COLOR",
            "Sets the error bars' color. See also --color") do |val|
    style_argument(:error_bar_color, val)
  end

  parser.on("--drawing-order ORDER",
            "Sets the order for drawing curve elements") do |val|
    begin
      style_argument(:drawing_order, val) do |v|
        i = Integer(v)
        CurveStyle::DrawingOrder.fetch(i) # To raise an
        # exception in case i is not valid
        i 
      end
    rescue
      puts "Invalid drawing order #{val}. Valid ones are the " +
        "following integers"
      CurveStyle::DrawingOrder.each_with_index do |vals, i|
        puts "#{i} -> #{vals.map {|s| s.to_s}.join(', ')}"
      end
    end
  end

  parser.separator 'Transparency options'
  parser.on("--transparency T",
            "Sets the transparency for lines.") do |val|
    style_argument(:transparency, val) do |v|
      begin
        Float(v)
      rescue
        false
      end
    end
  end

  parser.on("--marker-transparency T",
            "Sets the transparency for markers.") do |val|
    style_argument(:marker_transparency, val) do |v|
      begin
        Float(v)
      rescue
        false
      end
    end
  end

  parser.on("--error-bar-transparency T",
            "Sets the transparency for error bars.") do |val|
    style_argument(:error_bars_transparency, val) do |v|
      begin
        Float(v)
      rescue
        false
      end
    end
  end

  
  parser.separator 'Filled curves'
  parser.on("--fill TYPE",
            "Set the filling type for curves to TYPE") do |val|
    style_argument(:fill_type, val) do |v|
      Utils.interpret_arg(v, CurveStyle::FillTypeArguments)
    end
  end

  parser.on("--fill-color COLOR",
            "Set the fill color") do |val|
    style_argument(:fill_color, val) do |v|
      CTioga.get_tioga_color(v)
    end
  end

  parser.on("--fill-transparency T",
            "Set the fill color") do |val|
    style_argument(:fill_transparency, val) do |v|
      begin
        Float(v)
      rescue
        false
      end
    end
  end

  parser.separator 'Histograms'
  parser.on("--[no-]histogram",
             "All the next curves will be drawn", 
            "as histograms. Deprecated.",
            "Please use the --hist option") do |w|
    @histogram = w
    style_argument(:hist_type, false)
  end
  parser.on("--hist TYPE",
            "Makes histograms from the next curves.",
            "TYPE specifies where the step should start from,",
            "See --fill-type for that") do |w|
    w = false if w =~ DisableRe
    if w
      @histogram = true
      style_argument(:hist_type, w) do |v|
        Utils.interpret_arg(v, CurveStyle::FillTypeArguments)
      end
    else 
      @histogram = false
    end
  end

  parser.on("--no-hist",
            "Stop making histograms") do 
    @histogram = false
  end

  parser.on("--hist-width WIDTH",
            "The ratio of the width drawn over the ",
            "total width") do |w|
    a = Float(w)
    style_argument(:hist_left, (1 - a)/2)
    style_argument(:hist_right, (1 + a)/2)
  end

  parser.on("--hist-left LEFT",
            "The position of the left side of the step",
            "relative to the total step") do |val|
    style_argument(:hist_left, val) do |v|
      begin
        Float(v)
      rescue
        false
      end
    end
  end
  parser.on("--hist-right RIGHT",
            "Pendant of --hist-left for the right") do |val|
    style_argument(:hist_right, val) do |v|
      begin
        Float(v)
      rescue
        false
      end
    end
  end


  parser.separator 'Themes and sets'
  parser.on("--theme THEME",
            "Chooses the current theme. See --theme-list for ",
            "a list of current valid themes"
             ) do |w|
    choose_theme(w)
  end

  parser.on("--theme-list",
            "Lists available themes.") do 
    puts
    puts "Currently available themes are"
    puts
    puts BaseTheme::THEMES.keys.map {|i| i.downcase}.uniq.join(" ")
  end

  parser.on("--reset-theme",
            "Resets theme defaults.") do 
    choose_theme(@theme_name)
  end


  parser.on("--mono",
             "Compatibility option for --theme mono") do |w|
    choose_theme('mono')
  end

  # TODO: there is no reason why sets should be restricted
  # to the following things. All style things should
  # potentially have a corresponding style-set.
  #
  # In short, all styles things should be converted to something
  # more in the spirit of Parameter and the like...

  # Sets:
  # Note that, due to scoping side-effects, one has to use the
  # block form of the iteration, else type gets overwritten
  # and we end up writing only to the last possibility.
  #
  # In addition to (and taking over) specifying named sets,
  # it is possible to provide:
  # * a single style element, in which case the set becomes the
  #   new element
  # * a |-separated list of elements, in which case the set
  #   becomes the given elements
  {
    :colors => "color",
    :markers =>  "marker",
    :markers_colors => "marker-color",
    :linestyle => "line-style"
  }.each do |type, name|
    parser.on("--#{name}-set SET", 
              "Choose the current set for #{name} ",
              "(#{@theme.sets[type].available_sets.join(' ')})") do |set|
      debug "Using set '#{set}' for #{type} on theme " +
        "#{current_theme.class}"
      if target_set = current_theme.sets[type]
        begin
          # If there is one '|', we split the 
          if ! target_set.valid_set?(set)
            debug "#{set} is not a named set for #{type}, " +
              "trying other interpretations"
            
            if set =~ /\|/
              set = SpecialArray.new(set.split(/\|/).map do |x|
                                       MetaBuilder::ParameterType.
                                         from_string(StyleTypes[type], x)
                                     end)
              info "Setting set #{name} to objects #{set.inspect}"
            else
              set = MetaBuilder::ParameterType.
                from_string(StyleTypes[type], set)
              info "Setting set #{name} to single-value object #{set.inspect}"
            end
          end
          current_theme.choose_set(type, set)
        rescue Exception => e
          error "Set #{set} was not understood for #{name}, ignoring"
          debug "Exception raised on set interpretation code: #{e.inspect}"
        end
      else
        warn "Theme #{current_theme.class} does not appear to have " +
          "sets for #{type}, ignoring"
      end
    end
  end

  parser.separator 'Style manipulations'

  parser.on("--skip-style",
            "Skips the next style" ) do 
    @theme.next_curve_style("useless")
  end

  parser.on("-s", "--same-style",
            "Uses the same style as last curve as a base ",
            "for next curve") do 
    @restored_style = @style_stack.last
  end

  parser.on("--save-style NAME",
            "Saves the style of the last curve for later use") do |n|
    @saved_styles[n] = @style_stack.last
  end
  parser.on("--use-style NAME",
            "Uses the style named NAME for the next curve",
            "If NAME does not exist, it will be interpreted as",
            "the 0-numbered style starting from the first") do |n|
    if @saved_styles.key?(n)
      @restored_style = @saved_styles[n]
    else
      @restored_style = @style_stack[n.to_i]
    end
  end

  parser.on("--reset-override",
            "Resets style override") do 
    reset_override_style
  end

  parser.on("--save-override NAME",
            "Saves the current override") do |n|
    @saved_overrides[n] = @override_style.dup
    # But we don't save the legend !!!
    @saved_overrides[n].delete(:legend)
  end
  parser.on("--use-override NAME",
            "Uses the override named NAME from now on") do |n|
    if @saved_overrides.key?(n)
      legend = @override_style.legend
      @override_style = @saved_overrides[n].dup
      if legend
        @override_style[:legend] = legend
      end
    else
      warn "Saved override #{n} does not exist"
    end
  end

  parser.separator 'Shortcuts'
  parser.on("--short SHORTCUT",
            "Use given shortcut") do |name|
    if Shortcut.has? name
      # Function from Plotmaker:
      unshift_cmdline_args(*Shortcut.args(name))
    else
      warn "Shortcut #{name} was not found"
    end
  end

  parser.on("--short-list",
            "Lists available shortcuts") do 
    puts "Available shortcuts:"
    Shortcut.pretty_print
  end


    
end