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