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