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 <macros.h>
30 #include <wx/filedlg.h>
31 
32 #define COL_LABEL 0
33 #define COL_AMOUNT 1
34 
35 // Defines for components view
36 #define ROW_LABEL 0
37 #define COL_FRONT_SIDE 1
38 #define COL_BOTTOM_SIDE 2
39 #define COL_TOTAL 3
40 
41 // Defines for board view
42 #define ROW_BOARD_WIDTH 0
43 #define ROW_BOARD_HEIGHT 1
44 #define ROW_BOARD_AREA 2
45 
50 {
52  excludeNoPins( false ),
53  subtractHoles( false ),
55  {
56  }
57 
58  // Flags to remember last checkboxes state
61 
62  // Variables to save last report file name and folder
63  bool saveReportInitialized; // true after the 3 next string are initialized
64  wxString saveReportFolder; // last report folder
65  wxString saveReportName; // last report filename
66  wxString m_project; // name of the project used to create the last report
67  // used to reinit last state after a project change
68 };
69 
71 
73  DIALOG_BOARD_STATISTICS_BASE( aParentFrame ),
74  m_boardWidth( 0 ),
75  m_boardHeight( 0 ),
76  m_boardArea( 0.0 ),
77  m_hasOutline( false )
78 {
79  m_parentFrame = aParentFrame;
80 
81  m_gridDrills->UseNativeColHeader();
82  m_gridDrills->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_BOARD_STATISTICS::drillGridSort ), NULL, this );
83 
86 
87  // Make labels for grids
88  wxFont headingFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
89  headingFont.SetSymbolicSize( wxFONTSIZE_SMALL );
90  m_gridComponents->SetCellValue( ROW_LABEL, COL_FRONT_SIDE, _( "Front Side" ) );
91  m_gridComponents->SetCellFont( ROW_LABEL, COL_FRONT_SIDE, headingFont );
92  m_gridComponents->SetCellValue( ROW_LABEL, COL_BOTTOM_SIDE, _( "Back Side" ) );
93  m_gridComponents->SetCellFont( ROW_LABEL, COL_BOTTOM_SIDE, headingFont );
94  m_gridComponents->SetCellValue( ROW_LABEL, COL_TOTAL, _( "Total" ) );
95  m_gridComponents->SetCellFont( ROW_LABEL, COL_TOTAL, headingFont );
96 
97  m_gridBoard->SetCellValue( 0, 0, _( "Width:" ) );
98  m_gridBoard->SetCellAlignment( 0, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
99  m_gridBoard->SetCellValue( 1, 0, _( "Height:" ) );
100  m_gridBoard->SetCellAlignment( 1, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
101  m_gridBoard->SetCellValue( 2, 0, _( "Area:" ) );
102  m_gridBoard->SetCellAlignment( 2, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
103 
104  wxGrid* grids[] = { m_gridComponents, m_gridPads, m_gridVias, m_gridBoard };
105  for( auto& grid : grids )
106  {
107  // Remove wxgrid's selection boxes
108  grid->SetCellHighlightPenWidth( 0 );
109  grid->SetColMinimalAcceptableWidth( 80 );
110  for( int i = 0; i < grid->GetNumberRows(); i++ )
111  grid->SetCellAlignment( i, COL_LABEL, wxALIGN_LEFT, wxALIGN_CENTRE );
112  }
113 
114  wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
115 
117  || s_savedDialogState.m_project != Prj().GetProjectFullName() )
118  {
119  fn.SetName( fn.GetName() + "_report" );
120  fn.SetExt( "txt" );
121  s_savedDialogState.saveReportName = fn.GetFullName();
122  s_savedDialogState.saveReportFolder = wxPathOnly( Prj().GetProjectFullName() );
125  }
126 
127  // The wxStdDialogButtonSizer wxID_CANCLE button is in fact a close button
128  // Nothing to cancel:
129  m_sdbControlSizerCancel->SetLabel( _( "Close" ) );
130 }
131 
133 {
134  m_componentsTypes.clear();
135 
136  // If you need some more types to be shown, simply add them to the
137  // corresponding list
138  m_componentsTypes.push_back( componentsType_t( FP_THROUGH_HOLE, _( "THT:" ) ) );
139  m_componentsTypes.push_back( componentsType_t( FP_SMD, _( "SMD:" ) ) );
140 
141  m_padsTypes.clear();
142  m_padsTypes.push_back( padsType_t( PAD_ATTRIB::PTH, _( "Through hole:" ) ) );
143  m_padsTypes.push_back( padsType_t( PAD_ATTRIB::SMD, _( "SMD:" ) ) );
144  m_padsTypes.push_back( padsType_t( PAD_ATTRIB::CONN, _( "Connector:" ) ) );
145  m_padsTypes.push_back( padsType_t( PAD_ATTRIB::NPTH, _( "NPTH:" ) ) );
146 
147  m_viasTypes.clear();
148  m_viasTypes.push_back( viasType_t( VIATYPE::THROUGH, _( "Through vias:" ) ) );
149  m_viasTypes.push_back( viasType_t( VIATYPE::BLIND_BURIED, _( "Blind/buried:" ) ) );
150  m_viasTypes.push_back( viasType_t( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
151 
152  // If there not enough rows in grids, append some
153  int appendRows = m_componentsTypes.size() + 2 - m_gridComponents->GetNumberRows();
154 
155  if( appendRows > 0 )
156  m_gridComponents->AppendRows( appendRows );
157 
158  appendRows = m_padsTypes.size() + 1 - m_gridPads->GetNumberRows();
159 
160  if( appendRows > 0 )
161  m_gridPads->AppendRows( appendRows );
162 
163  appendRows = m_viasTypes.size() + 1 - m_gridVias->GetNumberRows();
164 
165  if( appendRows )
166  m_gridVias->AppendRows( appendRows );
167 }
168 
170 {
172  getDataFromPCB();
173  updateWidets();
174  Layout();
175  drillsPanel->Layout();
177  return true;
178 }
179 
181 {
182  BOARD* board = m_parentFrame->GetBoard();
183 
184  // Get footprints and pads count
185  for( FOOTPRINT* footprint : board->Footprints() )
186  {
187  // Do not proceed footprints with no pads if checkbox checked
188  if( m_checkBoxExcludeComponentsNoPins->GetValue() && ! footprint->Pads().size() )
189  continue;
190 
191  // Go through components types list
192  for( auto& type : m_componentsTypes )
193  {
194  if(( footprint->GetAttributes() & type.attribute ) > 0 )
195  {
196  if( footprint->IsFlipped() )
197  type.backSideQty++;
198  else
199  type.frontSideQty++;
200  break;
201  }
202  }
203 
204  for( PAD* pad : footprint->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::NPTH,
234  true, top, bottom );
235 
236  auto it = m_drillTypes.begin();
237 
238  for( ; it != m_drillTypes.end(); ++it )
239  {
240  if( *it == drill )
241  {
242  it->qty++;
243  break;
244  }
245  }
246 
247  if( it == m_drillTypes.end() )
248  {
249  drill.qty = 1;
250  m_drillTypes.push_back( drill );
251  m_gridDrills->InsertRows();
252  }
253  }
254  }
255  }
256 
257  // Get via counts
258  for( TRACK* track : board->Tracks() )
259  {
260  if( VIA* via = dyn_cast<VIA*>( track ) )
261  {
262  for( auto& type : m_viasTypes )
263  {
264  if( via->GetViaType() == type.attribute )
265  {
266  type.qty++;
267  break;
268  }
269  }
270 
271  drillType_t drill( via->GetDrillValue(), via->GetDrillValue(), PAD_DRILL_SHAPE_CIRCLE,
272  true, false, via->TopLayer(), via->BottomLayer() );
273 
274  auto it = m_drillTypes.begin();
275 
276  for( ; it != m_drillTypes.end(); ++it )
277  {
278  if( *it == drill )
279  {
280  it->qty++;
281  break;
282  }
283  }
284 
285  if( it == m_drillTypes.end() )
286  {
287  drill.qty = 1;
288  m_drillTypes.push_back( drill );
289  m_gridDrills->InsertRows();
290  }
291  }
292  }
293 
294  sort( m_drillTypes.begin(), m_drillTypes.end(),
296 
297  bool boundingBoxCreated = false; //flag if bounding box initialized
298  BOX2I bbox;
299  SHAPE_POLY_SET polySet;
300  m_hasOutline = board->GetBoardPolygonOutlines( polySet );
301 
302  // If board has no Edge Cuts lines, board->GetBoardPolygonOutlines will
303  // return small rectangle, so we double check that
304  bool edgeCutsExists = false;
305 
306  for( BOARD_ITEM* drawing : board->Drawings() )
307  {
308  if( drawing->GetLayer() == Edge_Cuts )
309  {
310  edgeCutsExists = true;
311  break;
312  }
313  }
314 
315  if( !edgeCutsExists )
316  m_hasOutline = false;
317 
318  if( m_hasOutline )
319  {
320  m_boardArea = 0.0;
321 
322  for( int i = 0; i < polySet.OutlineCount(); i++ )
323  {
324  SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
325  m_boardArea += std::fabs( outline.Area() );
326 
327  // If checkbox "subtract holes" is checked
328  if( m_checkBoxSubtractHoles->GetValue() )
329  {
330  for( int j = 0; j < polySet.HoleCount( i ); j++ )
331  m_boardArea -= std::fabs( polySet.Hole( i, j ).Area() );
332  }
333 
334  if( boundingBoxCreated )
335  {
336  bbox.Merge( outline.BBox() );
337  }
338  else
339  {
340  bbox = outline.BBox();
341  boundingBoxCreated = true;
342  }
343  }
344 
345  m_boardWidth = bbox.GetWidth();
346  m_boardHeight = bbox.GetHeight();
347  }
348 }
349 
351 {
352  int totalPads = 0;
353  int currentRow = 0;
354 
355  for( const auto& type : m_padsTypes )
356  {
357  m_gridPads->SetCellValue( currentRow, COL_LABEL, type.title );
358  m_gridPads->SetCellValue(
359  currentRow, COL_AMOUNT, wxString::Format( wxT( "%i " ), type.qty ) );
360  totalPads += type.qty;
361  currentRow++;
362  }
363 
364  m_gridPads->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
365  m_gridPads->SetCellValue( currentRow, COL_AMOUNT, wxString::Format( "%i ", totalPads ) );
366 
367  int totalVias = 0;
368  currentRow = 0;
369 
370  for( const auto& type : m_viasTypes )
371  {
372  m_gridVias->SetCellValue( currentRow, COL_LABEL, type.title );
373  m_gridVias->SetCellValue(
374  currentRow, COL_AMOUNT, wxString::Format( "%i ", type.qty ) );
375  totalVias += type.qty;
376  currentRow++;
377  }
378 
379  m_gridVias->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
380  m_gridVias->SetCellValue( currentRow, COL_AMOUNT, wxString::Format( "%i ", totalVias ) );
381 
382 
383  int totalFront = 0;
384  int totalBack = 0;
385 
386  // We don't use row 0, as there labels are
387  currentRow = 1;
388 
389  for( const auto& type : m_componentsTypes )
390  {
391  m_gridComponents->SetCellValue( currentRow, COL_LABEL, type.title );
392  m_gridComponents->SetCellValue(
393  currentRow, COL_FRONT_SIDE, wxString::Format( "%i ", type.frontSideQty ) );
394  m_gridComponents->SetCellValue(
395  currentRow, COL_BOTTOM_SIDE, wxString::Format( "%i ", type.backSideQty ) );
396  m_gridComponents->SetCellValue( currentRow, 3,
397  wxString::Format( wxT( "%i " ), type.frontSideQty + type.backSideQty ) );
398  totalFront += type.frontSideQty;
399  totalBack += type.backSideQty;
400  currentRow++;
401  }
402 
403  m_gridComponents->SetCellValue( currentRow, COL_LABEL, _( "Total:" ) );
404  m_gridComponents->SetCellValue( currentRow, COL_FRONT_SIDE,
405  wxString::Format( "%i ", totalFront ) );
406  m_gridComponents->SetCellValue( currentRow, COL_BOTTOM_SIDE,
407  wxString::Format( "%i ", totalBack ) );
408  m_gridComponents->SetCellValue( currentRow, COL_TOTAL,
409  wxString::Format( "%i ", totalFront + totalBack ) );
410 
411  if( m_hasOutline )
412  {
413  m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT,
415  m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT,
417  m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
419  }
420  else
421  {
422  m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT, _( "unknown" ) );
423  m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT, _( "unknown" ) );
424  m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
425  }
426 
427  updateDrillGrid();
428 
429  m_gridComponents->AutoSize();
430  m_gridPads->AutoSize();
431  m_gridBoard->AutoSize();
432  m_gridVias->AutoSize();
433  m_gridDrills->AutoSize();
434 
436 }
437 
439 {
440  BOARD* board = m_parentFrame->GetBoard();
441  int currentRow = 0;
442 
443  for( const auto& type : m_drillTypes )
444  {
445  wxString shapeStr;
446  wxString startLayerStr;
447  wxString stopLayerStr;
448 
449  switch( type.shape )
450  {
452  shapeStr = _( "Round" );
453  break;
455  shapeStr = _( "Slot" );
456  break;
457  default:
458  shapeStr = _( "???" );
459  break;
460  }
461 
462  if( type.startLayer == UNDEFINED_LAYER )
463  startLayerStr = _( "N/A" );
464  else
465  startLayerStr = board->GetLayerName( type.startLayer );
466 
467  if( type.stopLayer == UNDEFINED_LAYER )
468  stopLayerStr = _( "N/A" );
469  else
470  stopLayerStr = board->GetLayerName( type.stopLayer );
471 
472  m_gridDrills->SetCellValue(
473  currentRow, drillType_t::COL_COUNT, wxString::Format( "%i", type.qty ) );
474  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_SHAPE, shapeStr );
475  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_X_SIZE,
476  MessageTextFromValue( GetUserUnits(), type.xSize ) );
477  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_Y_SIZE,
478  MessageTextFromValue( GetUserUnits(), type.ySize ) );
479  m_gridDrills->SetCellValue(
480  currentRow, drillType_t::COL_PLATED, type.isPlated ? _( "PTH" ) : _( "NPTH" ) );
481  m_gridDrills->SetCellValue(
482  currentRow, drillType_t::COL_VIA_PAD, type.isPad ? _( "Pad" ) : _( "Via" ) );
483  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_START_LAYER, startLayerStr );
484  m_gridDrills->SetCellValue( currentRow, drillType_t::COL_STOP_LAYER, stopLayerStr );
485 
486  currentRow++;
487  }
488 }
489 
490 void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
491  bool aUseRowLabels, bool aUseColLabels, bool aUseFirstColAsLabel )
492 {
493  std::vector<int> widths( aGrid->GetNumberCols(), 0 );
494  int rowLabelsWidth = 0;
495 
496  // Determine column widths.
497 
498  if( aUseColLabels )
499  {
500  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
501  widths[col] = aGrid->GetColLabelValue( col ).length();
502  }
503 
504  for( int row = 0; row < aGrid->GetNumberRows(); row++ )
505  {
506  rowLabelsWidth = std::max<int>( rowLabelsWidth, aGrid->GetRowLabelValue( row ).length() );
507 
508  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
509  widths[col] = std::max<int>( widths[col], aGrid->GetCellValue( row, col ).length() );
510  }
511 
512  // Print the cells.
513 
514  wxString tmp;
515 
516  // Print column labels.
517 
518  aStr << "|";
519 
520  if( aUseRowLabels )
521  {
522  aStr.Append( ' ', rowLabelsWidth );
523  aStr << " |";
524  }
525 
526  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
527  {
528  if( aUseColLabels )
529  tmp.Printf( " %*s |", widths[col], aGrid->GetColLabelValue( col ) );
530  else
531  tmp.Printf( " %*s |", widths[col], aGrid->GetCellValue( 0, col ) );
532  aStr << tmp;
533  }
534 
535  aStr << "\n";
536 
537  // Print column label horizontal separators.
538 
539  aStr << "|";
540 
541  if( aUseRowLabels )
542  {
543  aStr.Append( '-', rowLabelsWidth );
544  aStr << "-|";
545  }
546 
547  for( int col = 0; col < aGrid->GetNumberCols(); col++ )
548  {
549  aStr << "-";
550  aStr.Append( '-', widths[col] );
551  aStr << "-|";
552  }
553 
554  aStr << "\n";
555 
556  // Print regular cells.
557 
558  int firstRow = 0, firstCol = 0;
559 
560  if( !aUseColLabels )
561  firstRow = 1;
562 
563  if( !aUseRowLabels && aUseFirstColAsLabel )
564  firstCol = 1;
565 
566  for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
567  {
568  if( aUseRowLabels )
569  tmp.Printf( "|%-*s |", rowLabelsWidth, aGrid->GetRowLabelValue( row ) );
570  else if( aUseFirstColAsLabel )
571  tmp.Printf( "|%-*s |", widths[0], aGrid->GetCellValue( row, 0 ) );
572  else
573  tmp.Printf( "|" );
574  aStr << tmp;
575 
576  for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
577  {
578  tmp.Printf( " %*s |", widths[col], aGrid->GetCellValue( row, col ) );
579  aStr << tmp;
580  }
581  aStr << "\n";
582  }
583 }
584 
586 {
587  int newTotalWidth = m_gridDrills->GetClientSize().GetWidth();
588  int curTotalWidth = 0;
589 
590  // Find the total current width
591  for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
592  {
594  curTotalWidth += m_gridDrills->GetColSize( i );
595  }
596 
597  // Resize the last two columns to fill all available space
598 
599  int remainingWidth = newTotalWidth - curTotalWidth;
600 
601  m_gridDrills->SetColSize( drillType_t::COL_START_LAYER, remainingWidth / 2 );
602  m_gridDrills->SetColSize( drillType_t::COL_STOP_LAYER, remainingWidth - remainingWidth / 2 );
603 
604  m_gridDrills->Refresh();
605 }
606 
607 // If any checkbox clicked, we have to refresh dialog data
608 void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
609 {
613  getDataFromPCB();
614  updateWidets();
615  Layout();
616  drillsPanel->Layout();
617 }
618 
619 void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
620 {
621  FILE* outFile;
622  wxString msg;
623  wxString boardName;
624 
625  wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
626  boardName = fn.GetName();
627  wxFileDialog saveFileDialog( this, _( "Save Report File" ),
631  wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
632 
633  if( saveFileDialog.ShowModal() == wxID_CANCEL )
634  return;
635 
636  s_savedDialogState.saveReportFolder = wxPathOnly( saveFileDialog.GetPath() );
637  s_savedDialogState.saveReportName = saveFileDialog.GetFilename();
638 
639  outFile = wxFopen( saveFileDialog.GetPath(), "wt" );
640 
641  if( outFile == NULL )
642  {
643  msg.Printf( _( "Unable to create file \"%s\"" ), saveFileDialog.GetPath() );
644  DisplayErrorMessage( this, msg );
645  return;
646  }
647 
648  msg << _( "PCB statistics report\n=====================" ) << "\n";
649  msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << "\n";
650  msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << "\n";
651  msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << "\n";
652 
653  msg << "\n";
654  msg << _( "Board" ) << "\n-----\n";
655 
656  if( m_hasOutline )
657  {
658  msg << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << MessageTextFromValue( GetUserUnits(), m_boardWidth ) << "\n";
659  msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )<< MessageTextFromValue( GetUserUnits(), m_boardHeight ) << "\n";
660  msg << wxS( "- " ) << _( "Area" ) + wxS( ": " ) << MessageTextFromValue( GetUserUnits(), m_boardArea, true, EDA_DATA_TYPE::AREA );
661  msg << "\n";
662  }
663  else
664  {
665  msg << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << _( "unknown" ) << "\n";
666  msg << wxS( "- " ) << _( "Height" ) << wxS( ": " ) << _( "unknown" ) << "\n";
667  msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << "\n";
668  }
669 
670  msg << "\n";
671  msg << _( "Pads" ) << "\n----\n";
672 
673  for( auto& type : m_padsTypes )
674  msg << "- " << type.title << " " << type.qty << "\n";
675 
676  msg << "\n";
677  msg << _( "Vias" ) << "\n----\n";
678 
679  for( auto& type : m_viasTypes )
680  msg << "- " << type.title << " " << type.qty << "\n";
681 
682  // We will save data about components in the table.
683  // We have to calculate column widths
684  std::vector<int> widths;
685  std::vector<wxString> labels{ "", _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
686  wxString tmp;
687 
688  widths.reserve( labels.size() );
689  for( const auto& label : labels )
690  widths.push_back( label.size() );
691 
692  int frontTotal = 0;
693  int backTotal = 0;
694 
695  for( const auto& type : m_componentsTypes )
696  {
697  // Get maximum width for left label column
698  widths[0] = std::max<int>( type.title.size(), widths[0] );
699  frontTotal += type.frontSideQty;
700  backTotal += type.backSideQty;
701  }
702 
703  // Get maximum width for other columns
704  tmp.Printf( "%i", frontTotal );
705  widths[1] = std::max<int>( tmp.size(), widths[1] );
706  tmp.Printf( "%i", backTotal );
707  widths[2] = std::max<int>( tmp.size(), widths[2] );
708  tmp.Printf( "%i", frontTotal + backTotal );
709  widths[3] = std::max<int>( tmp.size(), widths[3] );
710 
711  //Write components amount to file
712  msg << "\n";
713  msg << _( "Components" ) << "\n----------\n";
714  msg << "\n";
715 
716  printGridToStringAsTable( m_gridComponents, msg, false, false, true );
717 
718  msg << "\n";
719  msg << _( "Drill holes" ) << "\n-----------\n";
720  msg << "\n";
721 
722  printGridToStringAsTable( m_gridDrills, msg, false, true, false );
723 
724  if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
725  {
726  msg.Printf( _( "Error writing to file \"%s\"" ), saveFileDialog.GetPath() );
727  DisplayErrorMessage( this, msg );
728  }
729 
730  fclose( outFile );
731 }
732 
733 void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
734 {
735  aEvent.Skip();
737 }
738 
739 void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
740 {
741  drillType_t::COL_ID colId = static_cast<drillType_t::COL_ID>( aEvent.GetCol() );
742  bool ascending =
743  !( m_gridDrills->IsSortingBy( colId ) && m_gridDrills->IsSortOrderAscending() );
744 
745  sort( m_drillTypes.begin(), m_drillTypes.end(), drillType_t::COMPARE( colId, ascending ) );
746 
747  updateDrillGrid();
748 }
749 
751 {
752 }
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:90
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:1925
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:265
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.
Like smd, does not appear on the solder paste layer (default)
#define COL_AMOUNT
Smd pad, appears on the solder paste layer (default)
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.
const wxString & GetFileName() const
Definition: board.h:300
void drillGridSize(wxSizeEvent &aEvent) override
Plated through hole pad.
This file contains miscellaneous commonly used macros and functions.
#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
like PAD_PTH, but not plated
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:305
void updateDrillGrid()
Print grid to string in tabular format.
Definition of file extensions used in Kicad.
DIALOG_BOARD_STATISTICS(PCB_EDIT_FRAME *aParentFrame)
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:117
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
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:190
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
The main frame for Pcbnew.
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.
Definition: project.cpp:129
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:308
TRACKS & Tracks()
Definition: board.h:302
Struct containing the dialog last saved state.
Definition: track.h:83
typeContainer_t< PAD_ATTRIB > padsType_t