UltraScan III
us_colorgradient.cpp
Go to the documentation of this file.
1 #include <QApplication>
3 
4 #include "us_colorgradient.h"
5 #include "us_defines.h"
6 #include "us_license.h"
7 #include "us_license_t.h"
8 #include "us_settings.h"
9 #include "us_gui_settings.h"
10 #include "us_util.h"
11 
12 //const qreal lgndfrac=0.33333; // legend 1/3th of width (1 x 2)
13 const qreal lgndfrac=0.20; // legend 1/5th of width (1 x 4)
14 //const qreal lgndfrac=0.11111; // legend 1/9th of width (1 x 8)
15 
16 // Main program for US_ColorGradient
17 int main( int argc, char* argv[] )
18 {
19  QApplication application( argc, argv );
20 
21  #include "main1.inc"
22 
23  // License is OK. Start up.
25  w.show(); // memberof QWidget
26  return application.exec(); // memberof QApplication
27 }
28 
29 MyButton::MyButton( int value ) : QPushButton()
30 {
31  which = value;
32  connect( this, SIGNAL( clicked() ), SLOT( pushed() ) );
33 }
34 
35 void MyButton::pushed( void )
36 {
37  emit click( which );
38 };
39 
40 
41 // The ColorGradient constructor is a standard US_Widgets type.
42 // It builds the GUI with banner, various buttons and selectors
43 // and the gradient label that displays the implied color gradient.
44 US_ColorGradient::US_ColorGradient( QWidget* parent, Qt::WindowFlags flags )
45  : US_Widgets( true, parent, flags )
46 {
47  // Give initial values for run time variables
48  have_save = false;
49  new_mods = false;
51 
52  // Clean up the ./etc directories
53  int nefmods = clean_etc_dir( true );
54 qDebug() << "CG: nefmods" << nefmods;
55 
56  setWindowTitle( tr( "Color Gradient Generator" ) );
57  setPalette( US_GuiSettings::frameColor() );
58 
59  // Set up top layout and add banner to it
60  QBoxLayout* topbox = new QVBoxLayout( this );
61  topbox->setSpacing( 2 );
62 
63  QGridLayout* gbuttons = new QGridLayout();
64  lb_banner1 = us_banner( "" );
65  QString btext = tr( "Please select the number of steps\n"
66  "and each Color Step for your gradient:\n"
67  "( Currently 1 step; 2 total colors;\n" );
68  lb_banner1->setText( btext );
69  topbox->addWidget( lb_banner1 );
70 
71  lb_nsteps = us_label( tr( "Number of Color Steps:" ) );
72  ct_nsteps = us_counter( 2, 1.0, 10.0, 1.0 );
73  ct_nsteps->setStep( 1.0 );
74 
75  connect( ct_nsteps, SIGNAL( valueChanged( double ) ),
76  this, SLOT( update_steps( double ) ) );
77 
78  QHBoxLayout* steps = new QHBoxLayout();
79 
80  steps->addWidget( lb_nsteps );
81  steps->addWidget( ct_nsteps );
82 
83  topbox->addLayout( steps );
84 
85  QGridLayout* colors = new QGridLayout();
86 
87  // check need to change style of buttons so that they can be colored
88  QStyle* btnsty = new QPlastiqueStyle(); // style sure to be colorable
89  QString stynam = qApp->style()->objectName();
90  bool needbsty = stynam.startsWith( "windowsv", Qt::CaseInsensitive ) ||
91  stynam.startsWith( "windowsx", Qt::CaseInsensitive ) ||
92  stynam.startsWith( "mac" , Qt::CaseInsensitive );
93  int c_row = 0;
94 
95  for ( int ii = 0; ii < 11; ii++ )
96  {
97  pb_c[ ii ] = new MyButton( ii );
98 
99  if ( needbsty ) // change button style for coloring if need be
100  pb_c[ ii ]->setStyle( btnsty );
101 
102  ct_c[ ii ] = us_counter( 2, 1.0, 101.0, 10.0 );
103  ct_c[ ii ]->setStep( 1.0 );
104 
105  colors->addWidget( pb_c[ ii ], c_row, 0 );
106  colors->addWidget( ct_c[ ii ], c_row++, 1 );
107 
108  connect( pb_c[ ii ], SIGNAL( click ( int ) ),
109  SLOT ( c_click ( int ) ) );
110 
111  connect( ct_c[ ii ], SIGNAL( valueChanged ( double ) ),
112  SLOT ( c_cnt_change ( double ) ) );
113 
114  if ( ii > 1 )
115  {
116  pb_c[ ii ]->setVisible( false );
117  ct_c[ ii ]->setVisible( false );
118  }
119  else if ( ii == 0 )
120  {
121  ct_c[ ii ]->setVisible( false );
122  }
123  }
124 
125  update_banner();
126 
127  topbox->addLayout( colors );
128 
129  int row = 0;
130 
131  pb_help = us_pushbutton( tr( "Help" ) );
132  pb_reset = us_pushbutton( tr( "Reset" ) );
133  connect( pb_help, SIGNAL( clicked() ), this, SLOT( help() ) );
134  connect( pb_reset, SIGNAL( clicked() ), this, SLOT( reset() ) );
135  gbuttons->addWidget( pb_help, row, 0 );
136  gbuttons->addWidget( pb_reset, row++, 1 );
137 
138  pb_load = us_pushbutton( tr( "Load Gradient" ) );
139  pb_show = us_pushbutton( tr( "Show Gradient" ) );
140  connect( pb_load, SIGNAL( clicked() ), this, SLOT( load_gradient() ) );
141  connect( pb_show, SIGNAL( clicked() ), this, SLOT( show_gradient() ) );
142  gbuttons->addWidget( pb_load, row, 0 );
143  gbuttons->addWidget( pb_show, row++, 1 );
144 
145  pb_save = us_pushbutton( tr( "Save Gradient" ) );
146  pb_close = us_pushbutton( tr( "Close" ) );
147  connect( pb_save, SIGNAL( clicked() ), this, SLOT( save_gradient() ) );
148  connect( pb_close, SIGNAL( clicked() ), this, SLOT( safe_close() ) );
149  gbuttons->addWidget( pb_save, row, 0 );
150  gbuttons->addWidget( pb_close, row++, 1 );
151 
152  // Add general purpose buttons layout to topmost layout
153  topbox->addLayout( gbuttons );
154 
155  // Create and add the bottom label for displaying the gradient
156  QBoxLayout* showgrad = new QHBoxLayout();
157  lb_gradient = us_label( NULL );
158  lb_gradient->setPalette( QColor( Qt::black ) );
159  showgrad->addWidget( lb_gradient );
160  width_lb = lb_gradient->width();
161  height_lb = lb_gradient->height();
162  margin = lb_gradient->margin() * 2;
163 
164  topbox->addLayout( showgrad );
165 
166  show_gradient();
167 
168  new_mods = false; // don't count color,counter changes as new
169 }
170 
171 void US_ColorGradient::c_click( int which )
172 {
173  QPalette p = pb_c[ which ]->palette();
174 
175  QColor color = QColorDialog::getColor( p.color( QPalette::Button ), this );
176 
177  if ( color.isValid() )
178  {
179  p.setColor( QPalette::Button, color );
180  pb_c[ which ]->setPalette( p );
181  new_mods = true;
182  }
183 
184  show_gradient();
185 }
186 
187 void US_ColorGradient::c_cnt_change( double /*count*/ )
188 {
189  new_mods = true;
190 
191  show_gradient();
192 }
193 
194 // Slot for handling the reset button: clear all settings
196 {
197  new_mods = false;
198  have_save = false;
199  is_reset = true;
200 
201  ct_nsteps->setValue( 1.0 );
202  ct_c[ 1 ]->setValue( 1.0 );
203 
204  lb_gradient->resize( width_lb, height_lb );
205 
206  update_banner();
207  show_gradient();
208 }
209 
210 // A slot to save the color gradient step counts and colors
211 // to an XML file, as specified in a file dialog.
213 {
214  QString save_file = grad_dir + "/new_gradient.xml";
215  save_file = QFileDialog::getSaveFileName( this,
216  tr( "Specify XML File Name for Gradient Save" ), grad_dir,
217  tr( "Color Map files (*cm-*.xml);;"
218  "Any XML files (*.xml);;Any files (*)" ) );
219 
220  save_file = save_file.replace( "\\", "/" );
221  int jj = save_file.lastIndexOf( "/" ) + 1;
222  QString fdir = save_file.left( jj );
223  QString fnam = save_file.mid( jj );
224 
225  if ( !save_file.isEmpty() )
226  {
227  out_filename = save_file;
228 
229  // Make sure file name is in "cm_<name>.xml" form
230  if ( save_file.endsWith( "." ) )
231  { // ending with '.' signals no ".xml" to be added
232  save_file = save_file.left( save_file.length() - 1 );
233  fnam = fnam .left( fnam .length() - 1 );
234  }
235 
236  else if ( ! save_file.endsWith( ".xml" ) )
237  { // if no .xml extension, add one
238  save_file = save_file + ".xml";
239  fnam = fnam + ".xml";
240  }
241 
242  if ( fnam.startsWith( "." ) )
243  { // starting with '.' signals no "cm-" prefix
244  save_file = fdir + fnam.mid( 1 );
245  }
246 
247  else if ( ! fnam.startsWith( "cm-" ) )
248  { // if no cm- prefix, add one
249  save_file = fdir + "cm-" + fnam;
250  }
251 
252  out_filename = save_file;
253 
254  have_save = true;
255  new_mods = false;
256 
257  // create the XML file holding color step information
258  QFile fileo( out_filename );
259 
260  if ( !fileo.open( QIODevice::WriteOnly | QIODevice::Text ) )
261  {
262  QMessageBox::information( this,
263  tr( "Error" ),
264  tr( "Cannot open file " ) + out_filename );
265  return;
266  }
267 
268  QXmlStreamWriter xmlo;
269  xmlo.setDevice( &fileo );
270  xmlo.setAutoFormatting( true );
271  xmlo.writeStartDocument( "1.0" );
272  xmlo.writeComment( "DOCTYPE UltraScanColorSteps" );
273  xmlo.writeCharacters( "\n" );
274  xmlo.writeStartElement( "colorsteps" );
275 
276  // write start color
277  int npoints = 0;
278  QColor s_color = pb_c[ 0 ]->palette().color( QPalette::Button );
279  xmlo.writeStartElement( "color" );
280  xmlo.writeAttribute( "red", QString::number( s_color.red() ) );
281  xmlo.writeAttribute( "green", QString::number( s_color.green() ) );
282  xmlo.writeAttribute( "blue", QString::number( s_color.blue() ) );
283  xmlo.writeAttribute( "points", "0" );
284  xmlo.writeEndElement();
285  int nsteps = qRound( ct_nsteps->value() );
286 
287  // write each step's end-color and number-points
288 
289  for ( int ii = 1; ii <= nsteps; ii++ )
290  {
291  s_color = pb_c[ ii ]->palette().color( QPalette::Button );
292  npoints = qRound( ct_c[ ii ]->value() );
293  xmlo.writeStartElement( "color" );
294  xmlo.writeAttribute( "red", QString::number( s_color.red() ) );
295  xmlo.writeAttribute( "green", QString::number( s_color.green() ) );
296  xmlo.writeAttribute( "blue", QString::number( s_color.blue() ) );
297  xmlo.writeAttribute( "points", QString::number( npoints ) );
298  xmlo.writeEndElement();
299  }
300 
301  xmlo.writeEndElement();
302  xmlo.writeEndDocument();
303  fileo.close();
304 
305  // now optionally save the color legend as a PNG file
306  QString png_fname = out_filename
307  .replace( QRegExp( ".xml$" ), QString( ".png" ) );
308  const QPixmap* mcolors = lb_gradient->pixmap();
309  int widp = mcolors->width();
310  int hgtp = mcolors->height();
311  int recx = (int)( (qreal)widp * ( 1.0 - lgndfrac ) );
312  int widl = widp - recx - 2;
313  int hgtl = hgtp - 2;
314  QString msg = tr( "Do you want to also save the %1 x %2 color legend"
315  " image\n in file \"%3\" .\n" )
316  .arg(widl).arg(hgtl).arg(png_fname);
317  QMessageBox msgBox;
318  msgBox.setText( msg );
319  msgBox.setStandardButtons( QMessageBox::No | QMessageBox::Yes );
320  msgBox.setDefaultButton( QMessageBox::Yes );
321  int result = msgBox.exec();
322  if ( result == QMessageBox::Yes )
323  {
324  QPixmap clegend = mcolors->copy( recx, 0, widl, hgtl );
325  clegend.toImage().save( png_fname );
326  }
327  }
328 }
329 
330 // Slot to allow the user to save any modifications to file
331 // and then to close.
333 {
334  if ( new_mods )
335  { // there have been mods since last file save
336  QMessageBox msgBox;
337  msgBox.setTextFormat( Qt::RichText );
338  msgBox.setText( tr( "You have modified the gradient since the "
339  "last save to file.<br>"
340  "Do you want to save to file before exiting?<ul>"
341  "<li><b>[Yes]</b> to save and close;</li>"
342  "<li><b>[No]</b> to close with no save;</li>"
343  "<li><b>[Cancel]</b> to resume gradient modifications.</li></ul>" ) );
344  msgBox.addButton( QMessageBox::Cancel );
345  msgBox.addButton( QMessageBox::No );
346  msgBox.addButton( QMessageBox::Yes );
347  msgBox.setDefaultButton( QMessageBox::Yes );
348  int result = msgBox.exec();
349 
350  if ( result == QMessageBox::Yes )
351  {
352  save_gradient();
353  }
354  else if ( result == QMessageBox::Cancel )
355  {
356  return;
357  }
358  }
359 
360  this->close();
361 }
362 
363 // Slot to load the color gradient step counts and colors
364 // from an XML file, as specified in a file dialog.
366 {
367  QString load_file = grad_dir + "/old_gradient.xml";
368  load_file = QFileDialog::getOpenFileName( this,
369  tr( "Select XML File Name for Gradient Load" ), grad_dir,
370  tr( "Color Map files (*cm-*.xml);;"
371  "Any XML files (*.xml);;Any files (*)" ) );
372 
373  if ( !load_file.isEmpty() )
374  {
375  in_filename = load_file;
376 
377  if ( !load_file.endsWith( "." ) && !load_file.endsWith( ".xml" ) )
378  {
379  in_filename = load_file + ".xml";
380  }
381 
382  QFile filei( in_filename );
383 
384  if ( !filei.open( QIODevice::ReadOnly ) )
385  {
386  QMessageBox::information( this,
387  tr( "Error" ),
388  tr( "Cannot open file " ) + in_filename );
389  return;
390  }
391 
392  QXmlStreamReader xmli( &filei );
393  bool is_uscs = false;
394  int ncolors = 0;
395  int nsteps = 0;
396 
397  // parse xml input file to repopulate color steps
398 
399  while ( ! xmli.atEnd() )
400  {
401  xmli.readNext();
402 
403  if ( xmli.isComment() )
404  { // verify DOCTYPE UltraScanColorSteps
405  QString comm = xmli.text().toString();
406 
407  if ( comm.contains( "UltraScanColorSteps" ) )
408  {
409  is_uscs = true;
410  }
411  else
412  {
413  QMessageBox::information( this, tr( "Error" ),
414  tr( "File " ) + in_filename
415  + tr(" is not an UltraScanColorSteps xml file.") );
416  filei.close();
417  return;
418  }
419  }
420 
421  if ( xmli.isStartElement() && xmli.name() == "color" )
422  { // update color step entries
423  QXmlStreamAttributes ats = xmli.attributes();
424  int cred = ats.value( "red" ).toString().toInt();
425  int cgrn = ats.value( "green" ).toString().toInt();
426  int cblu = ats.value( "blue" ).toString().toInt();
427  QColor s_color = QColor( cred, cgrn, cblu );
428  int npoints = ats.value( "points" ).toString().toInt();
429 
430  if ( npoints > 0 )
431  { // step color and points
432  nsteps++;
433  ncolors += npoints;
434  }
435  else
436  { // start color
437  nsteps = 0;
438  ncolors = 1;
439  }
440 
441  QPalette pa = pb_c[ nsteps ]->palette();
442  pa.setColor( QPalette::Button, s_color );
443  pb_c[ nsteps ]->setPalette( pa );
444  ct_c[ nsteps ]->setValue( (double)npoints );
445  }
446  }
447 
448  if ( xmli.hasError() )
449  {
450  QMessageBox::information( this, tr( "Error" ),
451  tr( "File " ) + in_filename + tr(" is not a valid XML file.") );
452  }
453  else if ( ! is_uscs )
454  {
455  QMessageBox::information( this, tr( "Error" ),
456  tr( "File " ) + in_filename
457  + tr(" is not an UltraScanColorSteps xml file.") );
458  }
459  else
460  {
462  ct_nsteps->setValue( (qreal)nsteps );
463 
464  update_banner();
465  show_gradient();
466 
467  have_save = true;
468  new_mods = false;
469  }
470 
471  filei.close();
472  }
473 }
474 
475 // Slot called when the number-of-steps counter changes.
476 // This function changes the number of visible color buttons.
477 void US_ColorGradient::update_steps( double newval )
478 {
479  int nsteps = qRound( newval );
480  new_mods = true;
481 
482  for ( int ii = 2; ii < 11; ii++ )
483  {
484  bool show = ( ii <= nsteps ) ? true : false;
485  pb_c[ ii ]->setVisible( show );
486  ct_c[ ii ]->setVisible( show );
487  }
488 
489  show_gradient();
490 }
491 
492 // Slot to show the current gradient upon button click.
493 // The displayed gradient is in the form of concentric circles,
494 // with a rectangular vertical legend to the right.
496 {
497  // get width of gradient space; resize so height matches
498  int widthl = lb_gradient->width() - margin; // width of label
499  int widthp = widthl - margin; // width of pixmap
500  qreal wlbl = (qreal)widthl;
501  qreal wrec = wlbl * lgndfrac; // legend rect. width fraction of label
502  qreal hlbl = wlbl - wrec - 8.0; // new height of label
503  int heightl = (int)hlbl; // height of label
504  int heightp = heightl - margin; // height of pixmap
505  int nsteps = qRound( ct_nsteps->value() );
506 
507  if ( is_reset )
508  { // reset back to original values if this follows reset
509  widthl = width_lb - margin;
510  heightl = height_lb - margin;
511  widthp = widthl - margin;
512  heightp = heightl - margin;
513  }
514 
515  lb_gradient->setScaledContents( true );
516  lb_gradient->resize( widthl, heightl );
517 
518  // create Pixmap of appropriate size and aspect ratio
519  pm_gradient = new QPixmap( widthp, heightp );
520  pm_gradient->fill( Qt::black );
521 
522  if ( is_reset )
523  { // clear pixmap and return now, if this follows reset
524  lb_gradient->setPixmap( *pm_gradient );
525  resize( widthl, heightl );
526  update();
527  ct_nsteps->setValue( 1.0 );
528  ct_c[ 1 ]->setValue( 1.0 );
529  lb_gradient->show();
530  is_reset = false;
531  return;
532  }
533 
534  QPainter* pa = new QPainter( pm_gradient );
535 
536  // do an initial step loop just to count total colors
537  int ncolors = 1;
538  for ( int ii = 1; ii <= nsteps; ii++ )
539  {
540  int npoints = qRound( ct_c[ ii ]->value() );
541  ncolors += npoints;
542  }
543 
544  // calculate the geometry of the concentric circles
545  qreal radi = (qreal)heightp / 2.0; // radius is half of height
546  qreal thikc = radi / (qreal)ncolors; // thickness is radius/#colors
547  qreal x1leg = heightp + thikc + 4.0; // rectangle x start
548  qreal x2leg = (qreal)widthp - 4.0; // rectangle x end
549  qreal y1leg = (qreal)heightp; // rectangle y start
550  qreal dyleg = y1leg / (qreal)ncolors; // y increment legend rect
551  qreal thikl = dyleg + 1.0; // thickness legend band lines
552  y1leg -= ( dyleg / 2.0 ); // adjust start legend y
553  radi -= ( thikc / 2.0 ); // back off initial radius a bit
554  QPointF cenpt( radi, radi ); // circles center point
555  QColor cc = pb_c[ 0 ]->palette().color( QPalette::Button );
556 
557  // loop through steps determining color range in each
558 
559  for ( int ii = 1; ii <= nsteps; ii++ )
560  {
561  // get step color and number of points
562  int npoints = qRound( ct_c[ ii ]->value() );
563  QColor ec = pb_c[ ii ]->palette().color( QPalette::Button );
564 
565  // determine the delta for RGB values
566  qreal rngp = (qreal)npoints;
567  qreal delr = (qreal)( ec.red() - cc.red() ) / rngp;
568  qreal delg = (qreal)( ec.green() - cc.green() ) / rngp;
569  qreal delb = (qreal)( ec.blue() - cc.blue() ) / rngp;
570 
571  // set up initial step RGB values
572  qreal dvlr = (qreal)cc.red();
573  qreal dvlg = (qreal)cc.green();
574  qreal dvlb = (qreal)cc.blue();
575 
576  // get color at each point in a step
577 
578  for ( int jj = 0; jj < npoints ; jj++ )
579  {
580  // set brush color from RGB components
581  int ivlr = qRound( dvlr );
582  int ivlg = qRound( dvlg );
583  int ivlb = qRound( dvlb );
584  QBrush brc = QBrush( QColor( ivlr, ivlg, ivlb ) );
585 
586  // draw circle of that color
587  pa->setPen( QPen( brc, thikc+2.0 ) );
588  pa->drawEllipse( cenpt, radi, radi );
589 
590  // draw legend rectangle line
591  pa->setPen( QPen( brc, thikl ) );
592  pa->drawLine( QPointF( x1leg, y1leg ), QPointF( x2leg, y1leg ) );
593 
594  // bump RGB values and decrement radius
595  dvlr += delr;
596  dvlg += delg;
597  dvlb += delb;
598  radi -= thikc;
599  y1leg -= dyleg;
600  }
601  // next start color is this step's end
602  cc = ec;
603  }
604 
605  // draw the final, innermost, circle
606  QBrush brc = QBrush( cc );
607  pa->setPen( QPen( brc, thikc ) );
608  pa->drawEllipse( cenpt, radi, radi );
609  // draw last color legend horizontal line
610  pa->setPen( QPen( brc, thikl ) );
611  pa->drawLine( QPointF( x1leg, y1leg ), QPointF( x2leg, y1leg ) );
612 
613  // set pixel map for gradient label
614  lb_gradient->setPixmap( *pm_gradient );
615  lb_gradient->show();
616 
617  // update the banner text to reflect current color steps state
618  update_banner();
619 }
620 
621 // A method to update the top banner text to reflect the
622 // current state of the color steps. This includes information on
623 // number of steps, number of total colors, and any save-file name.
625 {
626  int ncolors = 1;
627  int nsteps = qRound( ct_nsteps->value() );
628 
629  for ( int ii = 1; ii <= nsteps; ii++ )
630  {
631  ncolors += qRound( ct_c[ ii ]->value() );
632  }
633 
634  QString steps = ( nsteps == 1 ) ?
635  "1 step" :
636  QString::number( nsteps ) + " steps";
637  QString colors = QString::number( ncolors );
638 
639  QString btext = tr( "Please select the number of steps\n"
640  "and each Color Step for your gradient:\n"
641  "( Currently %1; %2 total colors;\n" )
642  .arg(steps).arg(colors);
643 
644  if ( have_save )
645  {
646  btext += tr( " Save file: " )
647  + QFileInfo( out_filename ).completeBaseName() + " )";
648  }
649  else
650  {
651  btext += tr( " Not saved to any file )" );
652  }
653 
654  lb_banner1->setText( btext );
655 }
656