KiCad PCB EDA Suite
dialog_board_statistics.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2019 Alexander Shuklin, jasuramme@gmail.com
5  * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 
28 
29 #define COL_LABEL 0
30 #define COL_AMOUNT 1
31 
32 // Defines for components view
33 #define ROW_LABEL 0
34 #define COL_FRONT_SIDE 1
35 #define COL_BOTTOM_SIDE 2
36 #define COL_TOTAL 3
37 
38 // Defines for board view
39 #define ROW_BOARD_WIDTH 0
40 #define ROW_BOARD_HEIGHT 1
41 #define ROW_BOARD_AREA 2
42 
47 {
49  excludeNoPins( false ),
50  subtractHoles( false ),
52  {
53  }
54 
55  // Flags to remember last checkboxes state
58 
59  // Variables to save last report file name and folder
60  bool saveReportInitialized; // true after the 3 next string are initialized
61  wxString saveReportFolder; // last report folder
62  wxString saveReportName; // last report filename
63  wxString m_project; // name of the project used to create the last report
64  // used to reinit last state after a project change
65 };
66 
68 
70  DIALOG_BOARD_STATISTICS_BASE( aParentFrame ),
71  m_boardWidth( 0 ),
72  m_boardHeight( 0 ),
73  m_boardArea( 0.0 ),
74  m_hasOutline( false )
75 {
76  m_parentFrame = aParentFrame;
77 
78  m_gridDrills->UseNativeColHeader();
79  m_gridDrills->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_BOARD_STATISTICS::drillGridSort ), NULL, this );
80 
83 
84  // Make labels for grids
85  wxFont headingFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
86  headingFont.SetSymbolicSize( wxFONTSIZE_SMALL );
87  m_gridComponents->SetCellValue( ROW_LABEL, COL_FRONT_SIDE, _( "Front Side" ) );
88  m_gridComponents->SetCellFont( ROW_LABEL, COL_FRONT_SIDE, headingFont );
89  m_gridComponents->SetCellValue( ROW_LABEL, COL_BOTTOM_SIDE, _( "Back Side" ) );
90  m_gridComponents->SetCellFont( ROW_LABEL, COL_BOTTOM_SIDE, headingFont );
91  m_gridComponents->SetCellValue( ROW_LABEL, COL_TOTAL, _( "Total" ) );
92  m_gridComponents->SetCellFont( ROW_LABEL, COL_TOTAL, headingFont );
93 
94  m_gridBoard->SetCellValue( 0, 0, _( "Width:" ) );
95  m_gridBoard->SetCellAlignment( 0, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
96  m_gridBoard->SetCellValue( 1, 0, _( "Height:" ) );
97  m_gridBoard->SetCellAlignment( 1, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
98  m_gridBoard->SetCellValue( 2, 0, _( "Area:" ) );
99  m_gridBoard->SetCellAlignment( 2, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
100 
101  wxGrid* grids[] = { m_gridComponents, m_gridPads, m_gridVias, m_gridBoard };
102  for( auto& grid : grids )
103  {
104  // Remove wxgrid's selection boxes
105  grid->SetCellHighlightPenWidth( 0 );
106  grid->SetColMinimalAcceptableWidth( 80 );
107  for( int i = 0; i < grid->GetNumberRows(); i++ )
108  grid->SetCellAlignment( i, COL_LABEL, wxALIGN_LEFT, wxALIGN_CENTRE );
109  }
110 
111  wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
112 
114  || s_savedDialogState.m_project != Prj().GetProjectFullName() )
115  {
116  fn.SetName( fn.GetName() + "_report" );
117  fn.SetExt( "txt" );
118  s_savedDialogState.saveReportName = fn.GetFullName();
119  s_savedDialogState.saveReportFolder = wxPathOnly( Prj().GetProjectFullName() );
122  }
123 
124  // The wxStdDialogButtonSizer wxID_CANCLE button is in fact a close button
125  // Nothing to cancel:
126  m_sdbControlSizerCancel->SetLabel( _( "Close" ) );
127 }
128 
130 {
131  m_componentsTypes.clear();
132 
133  // If you need some more types to be shown, simply add them to the
134  // corresponding list
135  m_componentsTypes.push_back( componentsType_t( MOD_DEFAULT, _( "THT:" ) ) );
136  m_componentsTypes.push_back( componentsType_t( MOD_CMS, _( "SMD:" ) ) );
137  m_componentsTypes.push_back( componentsType_t( MOD_VIRTUAL, _( "Virtual:" ) ) );
138 
139  m_padsTypes.clear();
140  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_STANDARD, _( "Through hole:" ) ) );
141  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_SMD, _( "SMD:" ) ) );
142  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_CONN, _( "Connector:" ) ) );
143  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_HOLE_NOT_PLATED, _( "NPTH:" ) ) );
144 
145  m_viasTypes.clear();
146  m_viasTypes.push_back( viasType_t( VIA_THROUGH, _( "Through vias:" ) ) );
147  m_viasTypes.push_back( viasType_t( VIA_BLIND_BURIED, _( "Blind/buried:" ) ) );
148  m_viasTypes.push_back( viasType_t( VIA_MICROVIA, _( "Micro vias:" ) ) );
149 
150  // If there not enough rows in grids, append some
151  int appendRows = m_componentsTypes.size() + 2 - m_gridComponents->GetNumberRows();
152 
153  if( appendRows > 0 )
154  m_gridComponents->AppendRows( appendRows );
155 
156  appendRows = m_padsTypes.size() + 1 - m_gridPads->GetNumberRows();
157 
158  if( appendRows > 0 )
159  m_gridPads->AppendRows( appendRows );
160 
161  appendRows = m_viasTypes.size() + 1 - m_gridVias->GetNumberRows();
162 
163  if( appendRows )
164  m_gridVias->AppendRows( appendRows );
165 }
166 
168 {
170  getDataFromPCB();
171  updateWidets();
172  Layout();
173  drillsPanel->Layout();
175  return true;
176 }
177 
179 {
180  auto board = m_parentFrame->GetBoard();
181 
182  // Get modules and pads count
183  for( MODULE* module : board->Modules() )
184  {
185  auto& pads = module->Pads();
186 
187  // Do not proceed modules with no pads if checkbox checked
188  if( m_checkBoxExcludeComponentsNoPins->GetValue() && !pads.size() )
189  continue;
190 
191  // Go through components types list
192  for( auto& type : m_componentsTypes )
193  {
194  if( module->GetAttributes() == type.attribute )
195  {
196  if( module->IsFlipped() )
197  type.backSideQty++;
198  else
199  type.frontSideQty++;
200  break;
201  }
202  }
203 
204  for( auto& pad : pads )
205  {
206  // Go through pads types list
207  for( auto& type : m_padsTypes )
208  {
209  if( pad->GetAttribute() == type.attribute )
210  {
211  type.qty++;
212  break;
213  }
214  }
215 
216  if( pad->GetDrillSize().x > 0 && pad->GetDrillSize().y > 0 )
217  {
218  PCB_LAYER_ID top, bottom;
219 
220  if( pad->GetLayerSet().CuStack().empty() )
221  {
222  // The pad is not on any copper layer
223  top = UNDEFINED_LAYER;
224  bottom = UNDEFINED_LAYER;
225  }
226  else
227  {
228  top = pad->GetLayerSet().CuStack().front();
229  bottom = pad->GetLayerSet().CuStack().back();
230  }
231 
232  drillType_t drill( pad->GetDrillSize().x, pad->GetDrillSize().y,
233  pad->GetDrillShape(), pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED,
234  true, top, bottom );
235 
236  auto it = m_drillTypes.begin();
237  for( ; it != m_drillTypes.end(); it++ )
238  {
239  if( *it == drill )
240  {
241  it->qty++;
242  break;
243  }
244  }
245 
246  if( it == m_drillTypes.end() )
247  {
248  drill.qty = 1;
249  m_drillTypes.push_back( drill );
250  m_gridDrills->InsertRows();
251  }
252  }
253  }
254  }
255 
256  // Get via counts
257  for( auto& track : board->Tracks() )
258  {
259  if( auto via = dyn_cast<VIA*>( track ) )
260  {
261  for( auto& type : m_viasTypes )
262  {
263  if( via->GetViaType() == type.attribute )
264  {
265  type.qty++;
266  break;
267  }
268  }
269 
270  drillType_t drill( via->GetDrillValue(), via->GetDrillValue(), PAD_DRILL_SHAPE_CIRCLE,
271  true, false, via->TopLayer(), via->BottomLayer() );
272 
273  auto it = m_drillTypes.begin();
274  for( ; it != m_drillTypes.end(); it++ )
275  {
276  if( *it == drill )
277  {
278  it->qty++;
279  break;
280  }
281  }
282 
283  if( it == m_drillTypes.end() )
284  {
285  drill.qty = 1;
286  m_drillTypes.push_back( drill );
287  m_gridDrills->InsertRows();
288  }
289  }
290  }
291 
292  sort( m_drillTypes.begin(), m_drillTypes.end(),
294 
295  bool boundingBoxCreated = false; //flag if bounding box initialized
296  BOX2I bbox;
297  SHAPE_POLY_SET polySet;
298  m_hasOutline = board->GetBoardPolygonOutlines( polySet );
299 
300  // If board has no Edge Cuts lines, board->GetBoardPolygonOutlines will
301  // return small rectangle, so we double check that
302  bool edgeCutsExists = false;
303  for( auto& drawing : board->Drawings() )
304  {
305  if( drawing->GetLayer() == Edge_Cuts )
306  {
307  edgeCutsExists = true;
308  break;
309  }
310  }
311 
312  if( !edgeCutsExists )
313  m_hasOutline = false;
314 
315  if( m_hasOutline )
316  {
317  m_boardArea = 0.0;
318 
319  for( int i = 0; i < polySet.OutlineCount(); i++ )
320  {
321  SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
322  m_boardArea += std::fabs( outline.Area() );
323 
324  // If checkbox "subtract holes" is checked
325  if( m_checkBoxSubtractHoles->GetValue() )
326  {
327  for( int j = 0; j < polySet.HoleCount( i ); j++ )
328  m_boardArea -= std::fabs( polySet.Hole( i, j ).Area() );
329  }
330 
331  if( boundingBoxCreated )
332  {
333  bbox.Merge( outline.BBox() );
334  }
335  else
336  {
337  bbox = outline.BBox();
338  boundingBoxCreated = true;
339  }
340  }
341 
342  if( GetUserUnits() == INCHES )
343  m_boardArea /= ( IU_PER_MILS * IU_PER_MILS * 1000000 );
344  else
345  m_boardArea /= ( IU_PER_MM * IU_PER_MM );
346 
347  m_boardWidth = bbox.GetWidth();
348  m_boardHeight = bbox.GetHeight();
349  }
350 }
351 
353 {
354  int totalPads = 0;
355  int currentRow = 0;
356 
357  for( auto& type : m_padsTypes )
358  {
359  m_gridPads->SetCellValue( currentRow, COL_LABEL, type.title );
360  m_gridPads->SetCellValue(
361  currentRow, COL_AMOUNT, wxString::Format( wxT( "%i " ), type.qty ) );
362  totalPads += type.qty;
363  currentRow++;
364  }
365 
366  m_gridPads->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
367  m_gridPads->SetCellValue( currentRow, COL_AMOUNT, wxString::Format( "%i ", totalPads ) );
368 
369  int totalVias = 0;
370  currentRow = 0;
371 
372  for( auto& type : m_viasTypes )
373  {
374  m_gridVias->SetCellValue( currentRow, COL_LABEL, type.title );
375  m_gridVias->SetCellValue(
376  currentRow, COL_AMOUNT, wxString::Format( "%i ", type.qty ) );
377  totalVias += type.qty;
378  currentRow++;
379  }
380 
381  m_gridVias->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
382  m_gridVias->SetCellValue( currentRow, COL_AMOUNT, wxString::Format( "%i ", totalVias ) );
383 
384 
385  int totalFront = 0;
386  int totalBack = 0;
387 
388  // We don't use row 0, as there labels are
389  currentRow = 1;
390 
391  for( auto& type : m_componentsTypes )
392  {
393  m_gridComponents->SetCellValue( currentRow, COL_LABEL, type.title );
394  m_gridComponents->SetCellValue(
395  currentRow, COL_FRONT_SIDE, wxString::Format( "%i ", type.frontSideQty ) );
396  m_gridComponents->SetCellValue(
397  currentRow, COL_BOTTOM_SIDE, wxString::Format( "%i ", type.backSideQty ) );
398  m_gridComponents->SetCellValue( currentRow, 3,
399  wxString::Format( wxT( "%i " ), type.frontSideQty + type.backSideQty ) );
400  totalFront += type.frontSideQty;
401  totalBack += type.backSideQty;
402  currentRow++;
403  }
404 
405  m_gridComponents->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
406  m_gridComponents->SetCellValue( currentRow, COL_FRONT_SIDE,
407  wxString::Format( "%i ", totalFront ) );
408  m_gridComponents->SetCellValue( currentRow, COL_BOTTOM_SIDE,
409  wxString::Format( "%i ", totalBack ) );
410  m_gridComponents->SetCellValue( currentRow, COL_TOTAL,
411  wxString::Format( "%i ", totalFront + totalBack ) );
412 
413  if( m_hasOutline )
414  {
415  m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT,
417  m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT,
419  m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
420  wxString::Format( wxT( "%.3f %s²" ),
421  m_boardArea,
423  }
424  else
425  {
426  m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT, _( "unknown" ) );
427  m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT, _( "unknown" ) );
428  m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
429  }
430 
431  updateDrillGrid();
432 
433  m_gridComponents->AutoSize();
434  m_gridPads->AutoSize();
435  m_gridBoard->AutoSize();
436  m_gridVias->AutoSize();
437  m_gridDrills->AutoSize();
438 
440 }
441 
443 {
444  BOARD* board = m_parentFrame->GetBoard();
445  int currentRow = 0;
446 
447  for( auto& type : m_drillTypes )
448  {
449  wxString shapeStr;
450  wxString startLayerStr;
451  wxString stopLayerStr;
452 
453  switch( type.shape )
454  {
456  shapeStr = _( "Round" );
457  break;
459  shapeStr = _( "Slot" );
460  break;
461  default:
462  shapeStr = _( "???" );
463  break;
464  }
465 
466  if( type.startLayer == UNDEFINED_LAYER )
467  startLayerStr = _( "N/A" );
468  else
469  startLayerStr = board->GetLayerName( type.startLayer );
470 
471  if( type.stopLayer == UNDEFINED_LAYER )
472  stopLayerStr = _( "N/A" );
473  else
474  stopLayerStr = board->GetLayerName( type.stopLayer );
475 
476  m_gridDrills->SetCellValue(
477  currentRow, drillType_t::COL_COUNT, wxString::Format( "%i", type.qty ) );
478  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_SHAPE, shapeStr );
479  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_X_SIZE,
480  MessageTextFromValue( GetUserUnits(), type.xSize ) );
481  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_Y_SIZE,
482  MessageTextFromValue( GetUserUnits(), type.ySize ) );
483  m_gridDrills->SetCellValue(
484  currentRow, drillType_t::COL_PLATED, type.isPlated ? _( "PTH" ) : _( "NPTH" ) );
485  m_gridDrills->SetCellValue(
486  currentRow, drillType_t::COL_VIA_PAD, type.isPad ? _( "Pad" ) : _( "Via" ) );
487  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_START_LAYER, startLayerStr );
488  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_STOP_LAYER, stopLayerStr );
489 
490  currentRow++;
491  }
492 }
493 
494 void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
495  bool aUseRowLabels, bool aUseColLabels, bool aUseFirstColAsLabel )
496 {
497  std::vector<int> widths( aGrid->GetNumberCols(), 0 );
498  int rowLabelsWidth = 0;
499 
500  // Determine column widths.
501 
502  if( aUseColLabels )
503  {
504  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
505  widths[col] = aGrid->GetColLabelValue( col ).length();
506  }
507 
508  for( int row = 0; row < aGrid->GetNumberRows(); row++ )
509  {
510  rowLabelsWidth = std::max<int>( rowLabelsWidth, aGrid->GetRowLabelValue( row ).length() );
511 
512  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
513  widths[col] = std::max<int>( widths[col], aGrid->GetCellValue( row, col ).length() );
514  }
515 
516  // Print the cells.
517 
518  wxString tmp;
519 
520  // Print column labels.
521 
522  aStr << "|";
523 
524  if( aUseRowLabels )
525  {
526  aStr.Append( ' ', rowLabelsWidth );
527  aStr << " |";
528  }
529 
530  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
531  {
532  if( aUseColLabels )
533  tmp.Printf( " %*s |", widths[col], aGrid->GetColLabelValue( col ) );
534  else
535  tmp.Printf( " %*s |", widths[col], aGrid->GetCellValue( 0, col ) );
536  aStr << tmp;
537  }
538 
539  aStr << "\n";
540 
541  // Print column label horizontal separators.
542 
543  aStr << "|";
544 
545  if( aUseRowLabels )
546  {
547  aStr.Append( '-', rowLabelsWidth );
548  aStr << "-|";
549  }
550 
551  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
552  {
553  aStr << "-";
554  aStr.Append( '-', widths[col] );
555  aStr << "-|";
556  }
557 
558  aStr << "\n";
559 
560  // Print regular cells.
561 
562  int firstRow = 0, firstCol = 0;
563 
564  if( !aUseColLabels )
565  firstRow = 1;
566 
567  if( !aUseRowLabels && aUseFirstColAsLabel )
568  firstCol = 1;
569 
570  for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
571  {
572  if( aUseRowLabels )
573  tmp.Printf( "|%-*s |", rowLabelsWidth, aGrid->GetRowLabelValue( row ) );
574  else if( aUseFirstColAsLabel )
575  tmp.Printf( "|%-*s |", widths[0], aGrid->GetCellValue( row, 0 ) );
576  else
577  tmp.Printf( "|" );
578  aStr << tmp;
579 
580  for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
581  {
582  tmp.Printf( " %*s |", widths[col], aGrid->GetCellValue( row, col ) );
583  aStr << tmp;
584  }
585  aStr << "\n";
586  }
587 }
588 
590 {
591  int newTotalWidth = m_gridDrills->GetClientSize().GetWidth();
592  int curTotalWidth = 0;
593 
594  // Find the total current width
595  for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
596  {
598  curTotalWidth += m_gridDrills->GetColSize( i );
599  }
600 
601  // Resize the last two columns to fill all available space
602 
603  int remainingWidth = newTotalWidth - curTotalWidth;
604 
605  m_gridDrills->SetColSize( drillType_t::COL_START_LAYER, remainingWidth / 2 );
606  m_gridDrills->SetColSize( drillType_t::COL_STOP_LAYER, remainingWidth - remainingWidth / 2 );
607 
608  m_gridDrills->Refresh();
609 }
610 
611 // If any checkbox clicked, we have to refresh dialog data
612 void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
613 {
617  getDataFromPCB();
618  updateWidets();
619  Layout();
620  drillsPanel->Layout();
621 }
622 
623 void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
624 {
625  FILE* outFile;
626  wxString msg;
627  wxString boardName;
628 
629  wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
630  boardName = fn.GetName();
631  wxFileDialog saveFileDialog( this, _( "Save Report File" ),
635  wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
636 
637  if( saveFileDialog.ShowModal() == wxID_CANCEL )
638  return;
639 
640  s_savedDialogState.saveReportFolder = wxPathOnly( saveFileDialog.GetPath() );
641  s_savedDialogState.saveReportName = saveFileDialog.GetFilename();
642 
643  outFile = wxFopen( saveFileDialog.GetPath(), "wt" );
644 
645  if( outFile == NULL )
646  {
647  msg.Printf( _( "Unable to create file \"%s\"" ), saveFileDialog.GetPath() );
648  DisplayErrorMessage( this, msg );
649  return;
650  }
651 
652  msg << _( "PCB statistics report\n=====================" ) << "\n";
653  msg << _( "- Date: " ) << wxDateTime::Now().Format() << "\n";
654  msg << _( "- Project: " ) << Prj().GetProjectName() << "\n";
655  msg << _( "- Board name: " ) << boardName << "\n";
656 
657  msg << "\n";
658  msg << _( "Board\n-----" ) << "\n";
659 
660  if( m_hasOutline )
661  {
662  msg << _( "- Width: " ) << MessageTextFromValue( GetUserUnits(), m_boardWidth ) << "\n";
663  msg << _( "- Height: " ) << MessageTextFromValue( GetUserUnits(), m_boardHeight ) << "\n";
664  msg << _( "- Area: " )
665  << wxString::Format(
666  wxT( "%.3f %s^2" ), m_boardArea, GetAbbreviatedUnitsLabel( GetUserUnits() ) )
667  << "\n";
668  }
669  else
670  {
671  msg << _( "- Width: " ) << _( "unknown" ) << "\n";
672  msg << _( "- Height: " ) << _( "unknown" ) << "\n";
673  msg << _( "- Area: " ) << _( "unknown" ) << "\n";
674  }
675 
676  msg << "\n";
677  msg << _( "Pads\n----" ) << "\n";
678 
679  for( auto& type : m_padsTypes )
680  msg << "- " << type.title << " " << type.qty << "\n";
681 
682  msg << "\n";
683  msg << _( "Vias\n----" ) << "\n";
684 
685  for( auto& type : m_viasTypes )
686  msg << "- " << type.title << " " << type.qty << "\n";
687 
688  // We will save data about components in the table.
689  // We have to calculate column widths
690  std::vector<int> widths;
691  std::vector<wxString> labels{ "", _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
692  wxString tmp;
693 
694  for( auto label : labels )
695  widths.push_back( label.size() );
696 
697  int frontTotal = 0;
698  int backTotal = 0;
699 
700  for( auto type : m_componentsTypes )
701  {
702  // Get maximum width for left label column
703  widths[0] = std::max<int>( type.title.size(), widths[0] );
704  frontTotal += type.frontSideQty;
705  backTotal += type.backSideQty;
706  }
707 
708  // Get maximum width for other columns
709  tmp.Printf( "%i", frontTotal );
710  widths[1] = std::max<int>( tmp.size(), widths[1] );
711  tmp.Printf( "%i", backTotal );
712  widths[2] = std::max<int>( tmp.size(), widths[2] );
713  tmp.Printf( "%i", frontTotal + backTotal );
714  widths[3] = std::max<int>( tmp.size(), widths[3] );
715 
716  //Write components amount to file
717  msg << "\n";
718  msg << _( "Components\n----------" ) << "\n";
719  msg << "\n";
720 
721  printGridToStringAsTable( m_gridComponents, msg, false, false, true );
722 
723  msg << "\n";
724  msg << _( "Drill holes\n-----------" ) << "\n";
725  msg << "\n";
726 
727  printGridToStringAsTable( m_gridDrills, msg, false, true, false );
728 
729  if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
730  {
731  msg.Printf( _( "Error writing to file \"%s\"" ), saveFileDialog.GetPath() );
732  DisplayErrorMessage( this, msg );
733  }
734 
735  fclose( outFile );
736 }
737 
738 void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
739 {
740  aEvent.Skip();
742 }
743 
744 void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
745 {
746  drillType_t::COL_ID colId = static_cast<drillType_t::COL_ID>( aEvent.GetCol() );
747  bool ascending =
748  !( m_gridDrills->IsSortingBy( colId ) && m_gridDrills->IsSortOrderAscending() );
749 
750  sort( m_drillTypes.begin(), m_drillTypes.end(), drillType_t::COMPARE( colId, ascending ) );
751 
752  updateDrillGrid();
753 }
754 
756 {
757 }
bool m_hasOutline
Shows if board outline properly defined
typeContainer_t< VIATYPE_T > viasType_t
int OutlineCount() const
Returns the number of outlines in the set
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Function GetLayerName returns the name of a layer given by aLayer.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:249
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:66
void refreshItemsTypes()
Function to fill up all items types to be shown in the dialog.
bool TransferDataToWindow() override
Get data from the PCB board and print it to dialog
#define COL_AMOUNT
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:62
Set for modules listed in the automatic insertion list (usually SMD footprints)
Definition: class_module.h:77
viasTypeList_t m_viasTypes
Holds all vias types to be shown in the dialog
#define ROW_BOARD_AREA
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Returns the reference to aHole-th hole in the aIndex-th outline
void getDataFromPCB()
Gets data from board
Struct holds information about component type (such as SMD, THT, Virtual and so on),...
const wxString & GetFileName() const
Definition: class_board.h:216
void drillGridSize(wxSizeEvent &aEvent) override
#define ROW_BOARD_HEIGHT
#define COL_TOTAL
wxString MessageTextFromValue(EDA_UNITS_T aUnits, int aValue, bool aUseMils)
Definition: base_units.cpp:125
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:48
PCB_LAYER_ID
A quick note on layer IDs:
void drillGridSort(wxGridEvent &aEvent)
const BOX2I BBox(int aClearance=0) const override
Function BBox()
#define COL_BOTTOM_SIDE
Class SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
#define ROW_LABEL
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
coord_type GetWidth() const
Definition: box2.h:195
#define COL_FRONT_SIDE
Definition: common.h:134
void updateDrillGrid()
Updates drills grid
Definition of file extensions used in Kicad.
DIALOG_BOARD_STATISTICS(PCB_EDIT_FRAME *aParentFrame)
typeContainer_t< PAD_ATTR_T > padsType_t
VTBL_ENTRY const wxString GetProjectFullName() const
Function GetProjectFullName returns the full path and name of the project.
Definition: project.cpp:96
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Function Merge modifies the position and size of the rectangle in order to contain aRect.
Definition: box2.h:384
Like smd, does not appear on the solder paste layer (default) note also has a special attribute in Ge...
Definition: pad_shapes.h:63
#define _(s)
int HoleCount(int aOutline) const
Returns the number of holes in a given outline
void printGridToStringAsTable(wxGrid *aGrid, wxString &aStr, bool aUseRowLabels, bool aUseColLabels, bool aUseFirstColAsLabel)
Prints grid to string in tabular format
double Area() const
#define ROW_BOARD_WIDTH
default
Definition: class_module.h:76
void updateWidets()
Applies data to dialog widgets
Class DIALOG_BOARD_STATISTICS_BASE.
EDA_UNITS_T GetUserUnits() const
Definition: dialog_shim.h:135
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:161
Class SHAPE_LINE_CHAIN.
Virtual component: when created by copper shapes on board (Like edge card connectors,...
Definition: class_module.h:79
size_t i
Definition: json11.cpp:649
Class PCB_EDIT_FRAME is the main frame for Pcbnew.
#define IU_PER_MILS
Definition: plotter.cpp:136
coord_type GetHeight() const
Definition: box2.h:196
wxString TextFileWildcard()
void saveReportClicked(wxCommandEvent &aEvent) override
Save board statistics to a file
padsTypeList_t m_padsTypes
Holds all pads types to be shown in the dialog
VTBL_ENTRY const wxString GetProjectName() const
Function GetProjectName returns the short name of the project.
Definition: project.cpp:108
BOARD * GetBoard() const
#define COL_LABEL
static DIALOG_BOARD_STATISTICS_SAVED_STATE s_savedDialogState
void checkboxClicked(wxCommandEvent &aEvent) override
drillTypeList_t m_drillTypes
Holds all drill hole types to be shown in the dialog
componentsTypeList_t m_componentsTypes
Holds all components types to be shown in the dialog
Struct containing the dialog last saved state.
wxString GetAbbreviatedUnitsLabel(EDA_UNITS_T aUnit, bool aUseMils)
Get the units string for a given units type.
Definition: base_units.cpp:450