UltraScan III
us_associations_gui.cpp
Go to the documentation of this file.
1 
3 #include "us_associations_gui.h"
4 #include "us_gui_settings.h"
5 #include "us_settings.h"
6 #include "us_constants.h"
7 
8 US_PushButton::US_PushButton( const QString& text, int i )
9  : QPushButton( text ), index( i )
10 {
11  setFont( QFont( US_GuiSettings::fontFamily(),
13 
14  setPalette( US_GuiSettings::pushbColor() );
15  setAutoDefault( false );
16 }
17 
18 void US_PushButton::mousePressEvent( QMouseEvent* e )
19 {
20  emit pushed( index );
21  e->accept();
22 }
23 
25  : US_WidgetsDialog( 0, 0 ), model( current_model )
26 {
27  setWindowTitle ( "UltraScan Model Associations" );
28  setPalette ( US_GuiSettings::frameColor() );
29  setAttribute ( Qt::WA_DeleteOnClose );
30  setWindowModality( Qt::WindowModal );
31 
32  // Very light gray
33  QPalette gray = US_GuiSettings::editColor();
34  gray.setColor( QPalette::Base, QColor( 0xe0, 0xe0, 0xe0 ) );
35 
36  QGridLayout* main = new QGridLayout( this );
37  main->setContentsMargins( 2, 2, 2, 2 );
38  main->setSpacing ( 2 );
39 
41  fm = new QFontMetrics( font );
42 
43  int row = 0;
44 
45  // Start widgets
46  // Models List Box
48  lw_analytes->setDragDropMode( QAbstractItemView::DragOnly );
49 
50  char leading = 'A';
51 
52  for ( int i = 0; i < model.components.size(); i++ )
53  {
55  lw_analytes->addItem( QString( QChar( leading ) ) + " " + sc->name );
56  leading++;
57  }
58 
59  main->addWidget( lw_analytes, row, 0, 5, 2 );
60  row += 5;
61 
62  tw = new QTableWidget();
63  tw->setPalette( US_GuiSettings::editColor() );
64  tw->setColumnCount( 7 );
65  tw->setRowCount ( 0 );
66 
67  QStringList headers;
68  headers << "" << "Analyte 1" << "Analyte 2" << "<==>" << "Product"
69  << "K_dissociation\n(molar units)" << "K_off Rate\n(1/sec)";
70  int flwidth = fm->width( "8888.3456e+00" );
71  tw->setMinimumWidth( flwidth * 5 + 100 );
72  tw->setRowHeight( 0, fm->height() + 4 );
73  tw->setColumnWidth( 0, fm->width( "D" ) + 6 );
74  tw->setColumnWidth( 1, flwidth );
75  tw->setColumnWidth( 2, flwidth );
76  tw->setColumnWidth( 3, fm->width( "<==>" ) - 2 );
77  tw->setColumnWidth( 4, flwidth );
78  tw->setColumnWidth( 5, flwidth );
79  tw->setColumnWidth( 6, flwidth );
80 
81  new_row();
82 
83  tw->setHorizontalHeaderLabels( headers );
84  tw->setDragDropMode( QAbstractItemView::DropOnly );
85  tw->horizontalHeader()->setStretchLastSection( true );
86 
87  connect( tw, SIGNAL( cellChanged( int, int ) ),
88  SLOT ( changed ( int, int ) ) );
89 
90  main->addWidget( tw, row, 0, 5, 2 );
91  row += 5;
92 
93  // Brief help label
94  QTextEdit* te_help = us_textedit();
95  QPalette pa( US_GuiSettings::labelColor() );
96  te_help->setPalette( pa );
97  te_help->setTextBackgroundColor( pa.color( QPalette::Window ) );
98  te_help->setTextColor( pa.color( QPalette::WindowText ) );
99  QFontMetrics fm( te_help->font() );
100  te_help->setMaximumHeight( fm.lineSpacing() * 13 / 2 );
101  te_help->setText( tr(
102  "* Drag a component from the upper list and drop it in an"
103  " Analyte or Product cell.\n"
104  "* Set the stoichiometry counter to the left of each component.\n"
105  "* Enter values for K_dissociation and K_off Rate in their text cells.\n"
106  "* Click on the \"D\" on the left side of any row to delete that row.\n"
107  "* Click the \"Accept\" button when all equations are as desired." ) );
108  main->addWidget( te_help, row, 0, 5, 2 );
109  row += 5;
110 
111  // Pushbuttons
112  QBoxLayout* buttonbox = new QHBoxLayout;
113 
114  QPushButton* pb_help = us_pushbutton( tr( "Help") );
115  connect( pb_help, SIGNAL( clicked() ), SLOT( help()) );
116  buttonbox->addWidget( pb_help );
117 
118  QPushButton* pb_close = us_pushbutton( tr( "Cancel") );
119  buttonbox->addWidget( pb_close );
120  connect( pb_close, SIGNAL( clicked() ), SLOT( close() ) );
121 
122  QPushButton* pb_accept = us_pushbutton( tr( "Accept") );
123  buttonbox->addWidget( pb_accept );
124  connect( pb_accept, SIGNAL( clicked() ), SLOT( complete()) );
125 
126  main->addLayout( buttonbox, row++, 0, 1, 2 );
127 
128  populate();
129 }
130 
132 {
133  for ( int i = 0; i < model.associations.size(); i++ )
134  {
135  int index = 0;
137 
138  // First set K_d and k_off
139  QString s = QString::number( as->k_d, 'e', 4 );
140  tw->setItem( i, 5, new QTableWidgetItem( s ) );
141 
142  s = QString::number( as->k_off, 'e', 4 );
143  tw->setItem( i, 6, new QTableWidgetItem( s ) );
144 
145 
146  // reaction_components must be size 2 or 3
147  set_component( index++, i, 1 );
148 
149  if ( as->rcomps.size() > 2 )
150  set_component( index++, i, 2 );
151 
152  set_component( index++, i, 4 );
153  }
154 }
155 
156 void US_AssociationsGui::set_component( int index, int row, int col )
157 {
159 
160  int component = as->rcomps[ index ];
161  QString s = lw_analytes->item( component )->text();
162  int koligo = model.components[ component ].oligomer;
163  s = s.left( 1 ) + QString::number( koligo );
164  tw->setItem( row, col, new QTableWidgetItem( s ) );
165 
166  qApp->processEvents(); // Let the signals work
167 
168  // Set the counter
169  QWidget* w = tw->cellWidget( row, col );
170  QLayout* L = w->layout();
171 
172  w = L->itemAt( 0 )->widget();
173  QwtCounter* c = dynamic_cast< QwtCounter* >( w );
174 
175  c->setValue( fabs( (double)as->stoichs[ index ] ) );
176 }
177 
178 void US_AssociationsGui::changed( int row, int col )
179 {
180  tw->disconnect();
181 
182  QTableWidgetItem* item = tw->item( row, col );
183 
184  if ( col > 4 )
185  {
186  double value = item->text().toDouble();
187  item->setText( QString::number( value, 'e', 4 ) );
188  }
189 
190  else if ( col == 3 )
191  {
192  item->setText( QString() );
193  }
194 
195  else
196  {
197  QWidget* w = new QWidget;
198  QHBoxLayout* L = new QHBoxLayout( w );
199  L->setContentsMargins( 0, 0, 0, 0 );
200  L->setSpacing ( 0 );
201 
202  QwtCounter* c = us_counter( 1, 1.0, 20.0 );
203  c->setStep( 1.0 );
204  L->addWidget( c );
205 
206  QString text = item->text().left( 1 );
207  QString sscr = item->text().mid( 1 );
208 
209  if ( sscr.startsWith( " " ) )
210  {
211  int index = lw_analytes->currentRow();
212  int colig = model.components[ index ].oligomer;
213  sscr = QString::number( colig );
214  }
215 
216  delete item;
217  QLabel* label = us_label( text + "<sub>" + sscr + "</sub>" );
218  label->setPalette( US_GuiSettings::editColor() );
219  L->addWidget( label );
220  tw->setCellWidget( row, col, w );
221  }
222 
223  connect( tw, SIGNAL( cellChanged( int, int ) ),
224  SLOT ( changed ( int, int ) ) );
225 
226  if ( row == tw->rowCount() - 1 ) new_row();
227 }
228 
230 {
231  int count = tw->rowCount();
232 
233  tw->setRowCount ( count + 1 );
234  tw->setRowHeight( count, fm->height() + 4 );
235 
236  QPushButton* pb = new US_PushButton( "D", count );
237  pb->setMaximumWidth( fm->width( "D" ) + 6 );
238  connect( pb, SIGNAL( pushed( int ) ), SLOT( del( int ) ) );
239 
240  tw->setCellWidget( count, 0, pb );
241 }
242 
243 void US_AssociationsGui::del( int index )
244 {
245  // Don't delete last row
246  if ( index == tw->rowCount() - 1 ) return;
247  tw->removeRow( index );
248 
249  for ( int i = 0; i < tw->rowCount(); i++ )
250  {
251  QWidget* w = tw->cellWidget( i, 0 );
252  US_PushButton* pb = dynamic_cast< US_PushButton* >( w );
253  pb->setIndex( i );
254  }
255 }
256 
258 {
259  QVector< US_Model::Association > associations;
260 
261  // Check validity
262  for ( int i = 0; i < tw->rowCount() - 1; i++ )
263  {
264  int moles_left = 0;
265  int moles_right = 0;
266 
267  QLayout* L;
268  QwtCounter* c;
269  int index;
270  int count;
271  int koligo;
272 
273  US_Model::Association association;
274  QTableWidgetItem* item;
275 
276  // If Kd and koff are not set, the default is zero
277  item = tw->item( i, 5 );
278  if ( item != 0 ) association.k_d = item->text().toDouble();
279 
280  item = tw->item( i, 6 );
281  if ( item != 0 ) association.k_off = item->text().toDouble();
282 
283  QWidget* w = tw->cellWidget( i, 1 );
284 
285  if ( w != 0 )
286  {
287  L = w->layout();
288 
289  if ( L != 0 )
290  {
291  w = L->itemAt( 0 )->widget();
292  c = dynamic_cast< QwtCounter* >( w );
293  count = (int) c->value();
294  association.stoichs << count;
295 
296  w = L->itemAt( 1 )->widget();
297  index = dynamic_cast< QLabel* >( w )->text().at( 0 ).cell() - 'A';
298  association.rcomps << index;
299 
300  koligo = model.components[ index ].oligomer;
301 
302  moles_left += count * koligo;
303  }
304  }
305 
306  w = tw->cellWidget( i, 2 );
307 
308  if ( w != 0 )
309  {
310  L = w->layout();
311 
312  if ( L != 0 )
313  {
314  w = L->itemAt( 0 )->widget();
315  c = dynamic_cast< QwtCounter* >( w );
316  count = (int) c->value();
317  association.stoichs << count;
318 
319  w = L->itemAt( 1 )->widget();
320  index = dynamic_cast< QLabel* >( w )->text().at( 0 ).cell() - 'A';
321  association.rcomps << index;
322 
323  koligo = model.components[ index ].oligomer;
324 
325  moles_left += count * koligo;
326  }
327  }
328 
329  w = tw->cellWidget( i, 4 );
330 
331  if ( w != 0 )
332  {
333  L = w->layout();
334 
335  if ( L != 0 )
336  {
337  w = L->itemAt( 0 )->widget();
338  c = dynamic_cast< QwtCounter* >( w );
339  count = (int) c->value();
340  association.stoichs << -count;
341 
342  w = L->itemAt( 1 )->widget();
343  index = dynamic_cast< QLabel* >( w )->text().at( 0 ).cell() - 'A';
344  association.rcomps << index;
345 
346  koligo = model.components[ index ].oligomer;
347 
348  moles_right += count * koligo;
349  }
350  }
351 
352  if ( moles_right != moles_left )
353  {
354  QMessageBox::information( this,
355  tr( "Equations do not balance" ),
356  tr( "Equation %1 does not balance" ).arg( i + 1 ) );
357  return;
358  }
359 
360  if ( combine_reactants( &association ) )
361  {
362  QMessageBox::information( this,
363  tr( "Combined Identical Reactants" ),
364  tr( "In association %1, two identical reactants"
365  " have been combined into a single one, with"
366  " double the stoichiometry value." ).arg( i + 1 ) );
367  }
368 
369  associations << association;
370  }
371 
372  // Update model associations
373  model.associations = associations;
374 
375  emit done();
376  close();
377 }
378 
379 // Combine any identical reactants
381 {
382  bool combined = false;
383 
384  if ( as->rcomps.size() == 3 && as->stoichs[ 1 ] > 0 )
385  { // 2 reactants and a product: check if reactants are identical
386  int comp1 = as->rcomps[ 0 ];
387  int comp2 = as->rcomps[ 1 ];
388  int stoi1 = as->stoichs[ 0 ];
389  int stoi2 = as->stoichs[ 1 ];
390 
391  combined = ( comp1 == comp2 && stoi1 == stoi2 );
392 
393  if ( combined )
394  { // Identical reactants: combine and double stoichiometry
395  comp2 = as->rcomps [ 2 ]; // Product component
396  stoi2 = as->stoichs[ 2 ]; // Product stoichiometry
397  as->rcomps .resize( 2 ); // Single reactant and product
398  as->stoichs.resize( 2 );
399  as->rcomps [ 0 ] = comp1; // Reactant component
400  as->stoichs[ 0 ] = stoi1 * 2; // Doubled reactant stoich.
401  as->rcomps [ 1 ] = comp2; // Product component
402  as->stoichs[ 1 ] = stoi2; // Product stoichiometry
403  }
404  }
405 
406  return combined;
407 }
408