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 
27 #include "base_units.h"
28 #include "common.h"
30 
31 #define COL_LABEL 0
32 #define COL_AMOUNT 1
33 
34 // Defines for components view
35 #define ROW_LABEL 0
36 #define COL_FRONT_SIDE 1
37 #define COL_BOTTOM_SIDE 2
38 #define COL_TOTAL 3
39 
40 // Defines for board view
41 #define ROW_BOARD_WIDTH 0
42 #define ROW_BOARD_HEIGHT 1
43 #define ROW_BOARD_AREA 2
44 
49 {
51  excludeNoPins( false ),
52  subtractHoles( false ),
54  {
55  }
56 
57  // Flags to remember last checkboxes state
60 
61  // Variables to save last report file name and folder
62  bool saveReportInitialized; // true after the 3 next string are initialized
63  wxString saveReportFolder; // last report folder
64  wxString saveReportName; // last report filename
65  wxString m_project; // name of the project used to create the last report
66  // used to reinit last state after a project change
67 };
68 
70 
72  DIALOG_BOARD_STATISTICS_BASE( aParentFrame ),
73  m_boardWidth( 0 ),
74  m_boardHeight( 0 ),
75  m_boardArea( 0.0 ),
76  m_hasOutline( false )
77 {
78  m_parentFrame = aParentFrame;
79 
80  m_gridDrills->UseNativeColHeader();
81  m_gridDrills->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_BOARD_STATISTICS::drillGridSort ), NULL, this );
82 
85 
86  // Make labels for grids
87  wxFont headingFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
88  headingFont.SetSymbolicSize( wxFONTSIZE_SMALL );
89  m_gridComponents->SetCellValue( ROW_LABEL, COL_FRONT_SIDE, _( "Front Side" ) );
90  m_gridComponents->SetCellFont( ROW_LABEL, COL_FRONT_SIDE, headingFont );
91  m_gridComponents->SetCellValue( ROW_LABEL, COL_BOTTOM_SIDE, _( "Back Side" ) );
92  m_gridComponents->SetCellFont( ROW_LABEL, COL_BOTTOM_SIDE, headingFont );
93  m_gridComponents->SetCellValue( ROW_LABEL, COL_TOTAL, _( "Total" ) );
94  m_gridComponents->SetCellFont( ROW_LABEL, COL_TOTAL, headingFont );
95 
96  m_gridBoard->SetCellValue( 0, 0, _( "Width:" ) );
97  m_gridBoard->SetCellAlignment( 0, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
98  m_gridBoard->SetCellValue( 1, 0, _( "Height:" ) );
99  m_gridBoard->SetCellAlignment( 1, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
100  m_gridBoard->SetCellValue( 2, 0, _( "Area:" ) );
101  m_gridBoard->SetCellAlignment( 2, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
102 
103  wxGrid* grids[] = { m_gridComponents, m_gridPads, m_gridVias, m_gridBoard };
104  for( auto& grid : grids )
105  {
106  // Remove wxgrid's selection boxes
107  grid->SetCellHighlightPenWidth( 0 );
108  grid->SetColMinimalAcceptableWidth( 80 );
109  for( int i = 0; i < grid->GetNumberRows(); i++ )
110  grid->SetCellAlignment( i, COL_LABEL, wxALIGN_LEFT, wxALIGN_CENTRE );
111  }
112 
113  wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
114 
116  || s_savedDialogState.m_project != Prj().GetProjectFullName() )
117  {
118  fn.SetName( fn.GetName() + "_report" );
119  fn.SetExt( "txt" );
120  s_savedDialogState.saveReportName = fn.GetFullName();
121  s_savedDialogState.saveReportFolder = wxPathOnly( Prj().GetProjectFullName() );
124  }
125 
126  // The wxStdDialogButtonSizer wxID_CANCLE button is in fact a close button
127  // Nothing to cancel:
128  m_sdbControlSizerCancel->SetLabel( _( "Close" ) );
129 }
130 
132 {
133  m_componentsTypes.clear();
134 
135  // If you need some more types to be shown, simply add them to the
136  // corresponding list
137  m_componentsTypes.push_back( componentsType_t( FP_THROUGH_HOLE, _( "THT:" ) ) );
138  m_componentsTypes.push_back( componentsType_t( FP_SMD, _( "SMD:" ) ) );
139 
140  m_padsTypes.clear();
141  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_PTH, _( "Through hole:" ) ) );
142  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_SMD, _( "SMD:" ) ) );
143  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_CONN, _( "Connector:" ) ) );
144  m_padsTypes.push_back( padsType_t( PAD_ATTRIB_NPTH, _( "NPTH:" ) ) );
145 
146  m_viasTypes.clear();
147  m_viasTypes.push_back( viasType_t( VIATYPE::THROUGH, _( "Through vias:" ) ) );
148  m_viasTypes.push_back( viasType_t( VIATYPE::BLIND_BURIED, _( "Blind/buried:" ) ) );
149  m_viasTypes.push_back( viasType_t( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
150 
151  // If there not enough rows in grids, append some
152  int appendRows = m_componentsTypes.size() + 2 - m_gridComponents->GetNumberRows();
153 
154  if( appendRows > 0 )
155  m_gridComponents->AppendRows( appendRows );
156 
157  appendRows = m_padsTypes.size() + 1 - m_gridPads->GetNumberRows();
158 
159  if( appendRows > 0 )
160  m_gridPads->AppendRows( appendRows );
161 
162  appendRows = m_viasTypes.size() + 1 - m_gridVias->GetNumberRows();
163 
164  if( appendRows )
165  m_gridVias->AppendRows( appendRows );
166 }
167 
169 {
171  getDataFromPCB();
172  updateWidets();
173  Layout();
174  drillsPanel->Layout();
176  return true;
177 }
178 
180 {
181  BOARD* board = m_parentFrame->GetBoard();
182 
183  // Get footprints and pads count
184  for( FOOTPRINT* footprint : board->Footprints() )
185  {
186  // Do not proceed footprints with no pads if checkbox checked
187  if( m_checkBoxExcludeComponentsNoPins->GetValue() && ! footprint->Pads().size() )
188  continue;
189 
190  // Go through components types list
191  for( auto& type : m_componentsTypes )
192  {
193  if(( footprint->GetAttributes() & type.attribute ) > 0 )
194  {
195  if( footprint->IsFlipped() )
196  type.backSideQty++;
197  else
198  type.frontSideQty++;
199  break;
200  }
201  }
202 
203  for( PAD* pad : footprint->Pads() )
204  {
205  // Go through pads types list
206  for( auto& type : m_padsTypes )
207  {
208  if( pad->GetAttribute() == type.attribute )
209  {
210  type.qty++;
211  break;
212  }
213  }
214 
215  if( pad->GetDrillSize().x > 0 && pad->GetDrillSize().y > 0 )
216  {
217  PCB_LAYER_ID top, bottom;
218 
219  if( pad->GetLayerSet().CuStack().empty() )
220  {
221  // The pad is not on any copper layer
222  top = UNDEFINED_LAYER;
223  bottom = UNDEFINED_LAYER;
224  }
225  else
226  {
227  top = pad->GetLayerSet().CuStack().front();
228  bottom = pad->GetLayerSet().CuStack().back();
229  }
230 
231  drillType_t drill( pad->GetDrillSize().x, pad->GetDrillSize().y,
232  pad->GetDrillShape(), pad->GetAttribute() != PAD_ATTRIB_NPTH,
233  true, top, bottom );
234 
235  auto it = m_drillTypes.begin();
236 
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( TRACK* track : board->Tracks() )
258  {
259  if( VIA* 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 
275  for( ; it != m_drillTypes.end(); it++ )
276  {
277  if( *it == drill )
278  {
279  it->qty++;
280  break;
281  }
282  }
283 
284  if( it == m_drillTypes.end() )
285  {
286  drill.qty = 1;
287  m_drillTypes.push_back( drill );
288  m_gridDrills->InsertRows();
289  }
290  }
291  }
292 
293  sort( m_drillTypes.begin(), m_drillTypes.end(),
295 
296  bool boundingBoxCreated = false; //flag if bounding box initialized
297  BOX2I bbox;
298  SHAPE_POLY_SET polySet;
299  m_hasOutline = board->GetBoardPolygonOutlines( polySet );
300 
301  // If board has no Edge Cuts lines, board->GetBoardPolygonOutlines will
302  // return small rectangle, so we double check that
303  bool edgeCutsExists = false;
304 
305  for( BOARD_ITEM* drawing : board->Drawings() )
306  {
307  if( drawing->GetLayer() == Edge_Cuts )
308  {
309  edgeCutsExists = true;
310  break;
311  }
312  }
313 
314  if( !edgeCutsExists )
315  m_hasOutline = false;
316 
317  if( m_hasOutline )
318  {
319  m_boardArea = 0.0;
320 
321  for( int i = 0; i < polySet.OutlineCount(); i++ )
322  {
323  SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
324  m_boardArea += std::fabs( outline.Area() );
325 
326  // If checkbox "subtract holes" is checked
327  if( m_checkBoxSubtractHoles->GetValue() )
328  {
329  for( int j = 0; j < polySet.HoleCount( i ); j++ )
330  m_boardArea -= std::fabs( polySet.Hole( i, j ).Area() );
331  }
332 
333  if( boundingBoxCreated )
334  {
335  bbox.Merge( outline.BBox() );
336  }
337  else
338  {
339  bbox = outline.BBox();
340  boundingBoxCreated = true;
341  }
342  }
343 
344  m_boardWidth = bbox.GetWidth();
345  m_boardHeight = bbox.GetHeight();
346  }
347 }
348 
350 {
351  int totalPads = 0;
352  int currentRow = 0;
353 
354  for( auto& type : m_padsTypes )
355  {
356  m_gridPads->SetCellValue( currentRow, COL_LABEL, type.title );
357  m_gridPads->SetCellValue(
358  currentRow, COL_AMOUNT, wxString::Format( wxT( "%i " ), type.qty ) );
359  totalPads += type.qty;
360  currentRow++;
361  }
362 
363  m_gridPads->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
364  m_gridPads->SetCellValue( currentRow, COL_AMOUNT, wxString::Format( "%i ", totalPads ) );
365 
366  int totalVias = 0;
367  currentRow = 0;
368 
369  for( auto& type : m_viasTypes )
370  {
371  m_gridVias->SetCellValue( currentRow, COL_LABEL, type.title );
372  m_gridVias->SetCellValue(
373  currentRow, COL_AMOUNT, wxString::Format( "%i ", type.qty ) );
374  totalVias += type.qty;
375  currentRow++;
376  }
377 
378  m_gridVias->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
379  m_gridVias->SetCellValue( currentRow, COL_AMOUNT, wxString::Format( "%i ", totalVias ) );
380 
381 
382  int totalFront = 0;
383  int totalBack = 0;
384 
385  // We don't use row 0, as there labels are
386  currentRow = 1;
387 
388  for( auto& type : m_componentsTypes )
389  {
390  m_gridComponents->SetCellValue( currentRow, COL_LABEL, type.title );
391  m_gridComponents->SetCellValue(
392  currentRow, COL_FRONT_SIDE, wxString::Format( "%i ", type.frontSideQty ) );
393  m_gridComponents->SetCellValue(
394  currentRow, COL_BOTTOM_SIDE, wxString::Format( "%i ", type.backSideQty ) );
395  m_gridComponents->SetCellValue( currentRow, 3,
396  wxString::Format( wxT( "%i " ), type.frontSideQty + type.backSideQty ) );
397  totalFront += type.frontSideQty;
398  totalBack += type.backSideQty;
399  currentRow++;
400  }
401 
402  m_gridComponents->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
403  m_gridComponents->SetCellValue( currentRow, COL_FRONT_SIDE,
404  wxString::Format( "%i ", totalFront ) );
405  m_gridComponents->SetCellValue( currentRow, COL_BOTTOM_SIDE,
406  wxString::Format( "%i ", totalBack ) );
407  m_gridComponents->SetCellValue( currentRow, COL_TOTAL,
408  wxString::Format( "%i ", totalFront + totalBack ) );
409 
410  if( m_hasOutline )
411  {
412  m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT,
414  m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT,
416  m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
418  }
419  else
420  {
421  m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT, _( "unknown" ) );
422  m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT, _( "unknown" ) );
423  m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
424  }
425 
426  updateDrillGrid();
427 
428  m_gridComponents->AutoSize();
429  m_gridPads->AutoSize();
430  m_gridBoard->AutoSize();
431  m_gridVias->AutoSize();
432  m_gridDrills->AutoSize();
433 
435 }
436 
438 {
439  BOARD* board = m_parentFrame->GetBoard();
440  int currentRow = 0;
441 
442  for( auto& type : m_drillTypes )
443  {
444  wxString shapeStr;
445  wxString startLayerStr;
446  wxString stopLayerStr;
447 
448  switch( type.shape )
449  {
451  shapeStr = _( "Round" );
452  break;
454  shapeStr = _( "Slot" );
455  break;
456  default:
457  shapeStr = _( "???" );
458  break;
459  }
460 
461  if( type.startLayer == UNDEFINED_LAYER )
462  startLayerStr = _( "N/A" );
463  else
464  startLayerStr = board->GetLayerName( type.startLayer );
465 
466  if( type.stopLayer == UNDEFINED_LAYER )
467  stopLayerStr = _( "N/A" );
468  else
469  stopLayerStr = board->GetLayerName( type.stopLayer );
470 
471  m_gridDrills->SetCellValue(
472  currentRow, drillType_t::COL_COUNT, wxString::Format( "%i", type.qty ) );
473  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_SHAPE, shapeStr );
474  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_X_SIZE,
475  MessageTextFromValue( GetUserUnits(), type.xSize ) );
476  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_Y_SIZE,
477  MessageTextFromValue( GetUserUnits(), type.ySize ) );
478  m_gridDrills->SetCellValue(
479  currentRow, drillType_t::COL_PLATED, type.isPlated ? _( "PTH" ) : _( "NPTH" ) );
480  m_gridDrills->SetCellValue(
481  currentRow, drillType_t::COL_VIA_PAD, type.isPad ? _( "Pad" ) : _( "Via" ) );
482  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_START_LAYER, startLayerStr );
483  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_STOP_LAYER, stopLayerStr );
484 
485  currentRow++;
486  }
487 }
488 
489 void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
490  bool aUseRowLabels, bool aUseColLabels, bool aUseFirstColAsLabel )
491 {
492  std::vector<int> widths( aGrid->GetNumberCols(), 0 );
493  int rowLabelsWidth = 0;
494 
495  // Determine column widths.
496 
497  if( aUseColLabels )
498  {
499  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
500  widths[col] = aGrid->GetColLabelValue( col ).length();
501  }
502 
503  for( int row = 0; row < aGrid->GetNumberRows(); row++ )
504  {
505  rowLabelsWidth = std::max<int>( rowLabelsWidth, aGrid->GetRowLabelValue( row ).length() );
506 
507  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
508  widths[col] = std::max<int>( widths[col], aGrid->GetCellValue( row, col ).length() );
509  }
510 
511  // Print the cells.
512 
513  wxString tmp;
514 
515  // Print column labels.
516 
517  aStr << "|";
518 
519  if( aUseRowLabels )
520  {
521  aStr.Append( ' ', rowLabelsWidth );
522  aStr << " |";
523  }
524 
525  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
526  {
527  if( aUseColLabels )
528  tmp.Printf( " %*s |", widths[col], aGrid->GetColLabelValue( col ) );
529  else
530  tmp.Printf( " %*s |", widths[col], aGrid->GetCellValue( 0, col ) );
531  aStr << tmp;
532  }
533 
534  aStr << "\n";
535 
536  // Print column label horizontal separators.
537 
538  aStr << "|";
539 
540  if( aUseRowLabels )
541  {
542  aStr.Append( '-', rowLabelsWidth );
543  aStr << "-|";
544  }
545 
546  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
547  {
548  aStr << "-";
549  aStr.Append( '-', widths[col] );
550  aStr << "-|";
551  }
552 
553  aStr << "\n";
554 
555  // Print regular cells.
556 
557  int firstRow = 0, firstCol = 0;
558 
559  if( !aUseColLabels )
560  firstRow = 1;
561 
562  if( !aUseRowLabels && aUseFirstColAsLabel )
563  firstCol = 1;
564 
565  for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
566  {
567  if( aUseRowLabels )
568  tmp.Printf( "|%-*s |", rowLabelsWidth, aGrid->GetRowLabelValue( row ) );
569  else if( aUseFirstColAsLabel )
570  tmp.Printf( "|%-*s |", widths[0], aGrid->GetCellValue( row, 0 ) );
571  else
572  tmp.Printf( "|" );
573  aStr << tmp;
574 
575  for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
576  {
577  tmp.Printf( " %*s |", widths[col], aGrid->GetCellValue( row, col ) );
578  aStr << tmp;
579  }
580  aStr << "\n";
581  }
582 }
583 
585 {
586  int newTotalWidth = m_gridDrills->GetClientSize().GetWidth();
587  int curTotalWidth = 0;
588 
589  // Find the total current width
590  for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
591  {
593  curTotalWidth += m_gridDrills->GetColSize( i );
594  }
595 
596  // Resize the last two columns to fill all available space
597 
598  int remainingWidth = newTotalWidth - curTotalWidth;
599 
600  m_gridDrills->SetColSize( drillType_t::COL_START_LAYER, remainingWidth / 2 );
601  m_gridDrills->SetColSize( drillType_t::COL_STOP_LAYER, remainingWidth - remainingWidth / 2 );
602 
603  m_gridDrills->Refresh();
604 }
605 
606 // If any checkbox clicked, we have to refresh dialog data
607 void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
608 {
612  getDataFromPCB();
613  updateWidets();
614  Layout();
615  drillsPanel->Layout();
616 }
617 
618 void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
619 {
620  FILE* outFile;
621  wxString msg;
622  wxString boardName;
623 
624  wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
625  boardName = fn.GetName();
626  wxFileDialog saveFileDialog( this, _( "Save Report File" ),
630  wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
631 
632  if( saveFileDialog.ShowModal() == wxID_CANCEL )
633  return;
634 
635  s_savedDialogState.saveReportFolder = wxPathOnly( saveFileDialog.GetPath() );
636  s_savedDialogState.saveReportName = saveFileDialog.GetFilename();
637 
638  outFile = wxFopen( saveFileDialog.GetPath(), "wt" );
639 
640  if( outFile == NULL )
641  {
642  msg.Printf( _( "Unable to create file \"%s\"" ), saveFileDialog.GetPath() );
643  DisplayErrorMessage( this, msg );
644  return;
645  }
646 
647  msg << _( "PCB statistics report\n=====================" ) << "\n";
648  msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << "\n";
649  msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << "\n";
650  msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << "\n";
651 
652  msg << "\n";
653  msg << _( "Board" ) << "\n-----\n";
654 
655  if( m_hasOutline )
656  {
657  msg << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << MessageTextFromValue( GetUserUnits(), m_boardWidth ) << "\n";
658  msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )<< MessageTextFromValue( GetUserUnits(), m_boardHeight ) << "\n";
659  msg << wxS( "- " ) << _( "Area" ) + wxS( ": " ) << MessageTextFromValue( GetUserUnits(), m_boardArea, true, EDA_DATA_TYPE::AREA );
660  msg << "\n";
661  }
662  else
663  {
664  msg << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << _( "unknown" ) << "\n";
665  msg << wxS( "- " ) << _( "Height" ) << wxS( ": " ) << _( "unknown" ) << "\n";
666  msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << "\n";
667  }
668 
669  msg << "\n";
670  msg << _( "Pads" ) << "\n----\n";
671 
672  for( auto& type : m_padsTypes )
673  msg << "- " << type.title << " " << type.qty << "\n";
674 
675  msg << "\n";
676  msg << _( "Vias" ) << "\n----\n";
677 
678  for( auto& type : m_viasTypes )
679  msg << "- " << type.title << " " << type.qty << "\n";
680 
681  // We will save data about components in the table.
682  // We have to calculate column widths
683  std::vector<int> widths;
684  std::vector<wxString> labels{ "", _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
685  wxString tmp;
686 
687  widths.reserve( labels.size() );
688  for( const auto& label : labels )
689  widths.push_back( label.size() );
690 
691  int frontTotal = 0;
692  int backTotal = 0;
693 
694  for( const auto& type : m_componentsTypes )
695  {
696  // Get maximum width for left label column
697  widths[0] = std::max<int>( type.title.size(), widths[0] );
698  frontTotal += type.frontSideQty;
699  backTotal += type.backSideQty;
700  }
701 
702  // Get maximum width for other columns
703  tmp.Printf( "%i", frontTotal );
704  widths[1] = std::max<int>( tmp.size(), widths[1] );
705  tmp.Printf( "%i", backTotal );
706  widths[2] = std::max<int>( tmp.size(), widths[2] );
707  tmp.Printf( "%i", frontTotal + backTotal );
708  widths[3] = std::max<int>( tmp.size(), widths[3] );
709 
710  //Write components amount to file
711  msg << "\n";
712  msg << _( "Components" ) << "\n----------\n";
713  msg << "\n";
714 
715  printGridToStringAsTable( m_gridComponents, msg, false, false, true );
716 
717  msg << "\n";
718  msg << _( "Drill holes" ) << "\n-----------\n";
719  msg << "\n";
720 
721  printGridToStringAsTable( m_gridDrills, msg, false, true, false );
722 
723  if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
724  {
725  msg.Printf( _( "Error writing to file \"%s\"" ), saveFileDialog.GetPath() );
726  DisplayErrorMessage( this, msg );
727  }
728 
729  fclose( outFile );
730 }
731 
732 void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
733 {
734  aEvent.Skip();
736 }
737 
738 void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
739 {
740  drillType_t::COL_ID colId = static_cast<drillType_t::COL_ID>( aEvent.GetCol() );
741  bool ascending =
742  !( m_gridDrills->IsSortingBy( colId ) && m_gridDrills->IsSortOrderAscending() );
743 
744  sort( m_drillTypes.begin(), m_drillTypes.end(), drillType_t::COMPARE( colId, ascending ) );
745 
746  updateDrillGrid();
747 }
748 
750 {
751 }
bool m_hasOutline
Hold all components types to be shown in the dialog.
typeContainer_t< VIATYPE > viasType_t
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:125
Definition: track.h:343
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:342
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:1862
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:266
Implementation of conversion functions that require both schematic and board internal units.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
void refreshItemsTypes()
< Function to fill up all items types to be shown in the dialog.
#define COL_AMOUNT
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:81
viasTypeList_t m_viasTypes
Hold all drill hole types to be shown in the dialog.
#define ROW_BOARD_AREA
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
void getDataFromPCB()
Apply data to dialog widgets.
Footprint attributes (such as SMD, THT, Virtual and so on), which will be shown in the dialog.
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:85
const wxString & GetFileName() const
Definition: board.h:291
void drillGridSize(wxSizeEvent &aEvent) override
#define ROW_BOARD_HEIGHT
double m_boardArea
Show if board outline properly defined.
EDA_UNITS GetUserUnits() const
Definition: dialog_shim.h:120
#define COL_TOTAL
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
PCB_LAYER_ID
A quick note on layer IDs:
void drillGridSort(wxGridEvent &aEvent)
~DIALOG_BOARD_STATISTICS()
Get data from the PCB board and print it to dialog.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
#define COL_BOTTOM_SIDE
#define NULL
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
#define ROW_LABEL
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
coord_type GetWidth() const
Definition: box2.h:197
#define COL_FRONT_SIDE
FOOTPRINTS & Footprints()
Definition: board.h:296
void updateDrillGrid()
Print grid to string in tabular format.
Definition of file extensions used in Kicad.
DIALOG_BOARD_STATISTICS(PCB_EDIT_FRAME *aParentFrame)
typeContainer_t< PAD_ATTR_T > padsType_t
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
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:386
Like smd, does not appear on the solder paste layer (default) note also has a special attribute in Ge...
Definition: pad_shapes.h:82
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
void printGridToStringAsTable(wxGrid *aGrid, wxString &aStr, bool aUseRowLabels, bool aUseColLabels, bool aUseFirstColAsLabel)
#define ROW_BOARD_WIDTH
void updateWidets()
Update drills grid.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
Class DIALOG_BOARD_STATISTICS_BASE.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:189
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
Plated through hole pad.
Definition: pad_shapes.h:80
The main frame for Pcbnew.
The common library.
coord_type GetHeight() const
Definition: box2.h:198
wxString TextFileWildcard()
void saveReportClicked(wxCommandEvent &aEvent) override
padsTypeList_t m_padsTypes
Hold all vias types to be shown in the dialog.
virtual const wxString GetProjectName() const
Return the short name of the project.
BOARD * GetBoard() const
#define COL_LABEL
Definition: pad.h:60
static DIALOG_BOARD_STATISTICS_SAVED_STATE s_savedDialogState
void checkboxClicked(wxCommandEvent &aEvent) override
Save board statistics to a file.
componentsTypeList_t m_componentsTypes
Hold all pads types to be shown in the dialog.
DRAWINGS & Drawings()
Definition: board.h:299
TRACKS & Tracks()
Definition: board.h:293
Struct containing the dialog last saved state.
Definition: track.h:83