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 <kiplatform/ui.h>
28#include <confirm.h>
29#include <pad.h>
30#include <macros.h>
32#include <widgets/wx_grid.h>
33#include <wx/filedlg.h>
34
35#define COL_LABEL 0
36#define COL_AMOUNT 1
37
38// Defines for components view
39#define ROW_LABEL 0
40#define COL_FRONT_SIDE 1
41#define COL_BOTTOM_SIDE 2
42#define COL_TOTAL 3
43
44// Defines for board view
45#define ROW_BOARD_WIDTH 0
46#define ROW_BOARD_HEIGHT 1
47#define ROW_BOARD_AREA 2
48
49
54{
56 excludeNoPins( false ),
57 subtractHoles( false ),
59 {
60 }
61
62 // Flags to remember last checkboxes state
65
66 // Variables to save last report file name and folder
67 bool saveReportInitialized; // true after the 3 next string are initialized
68 wxString saveReportFolder; // last report folder
69 wxString saveReportName; // last report filename
70 wxString m_project; // name of the project used to create the last report
71 // used to reinit last state after a project change
72};
73
74
76
78 DIALOG_BOARD_STATISTICS_BASE( aParentFrame ),
79 m_parentFrame(aParentFrame),
80 m_boardWidth( 0 ),
81 m_boardHeight( 0 ),
82 m_boardArea( 0.0 ),
83 m_hasOutline( false ),
84 m_startLayerColInitialSize( 1 ),
85 m_stopLayerColInitialSize( 1 )
86{
87 m_gridDrills->Connect( wxEVT_GRID_COL_SORT,
88 wxGridEventHandler( DIALOG_BOARD_STATISTICS::drillGridSort ),
89 nullptr, this );
90
93
94 // Make labels for grids
95 wxFont headingFont = KIUI::GetStatusFont( this );
96 m_gridComponents->SetCellValue( ROW_LABEL, COL_FRONT_SIDE, _( "Front Side" ) );
97 m_gridComponents->SetCellFont( ROW_LABEL, COL_FRONT_SIDE, headingFont );
98 m_gridComponents->SetCellValue( ROW_LABEL, COL_BOTTOM_SIDE, _( "Back Side" ) );
99 m_gridComponents->SetCellFont( ROW_LABEL, COL_BOTTOM_SIDE, headingFont );
100 m_gridComponents->SetCellValue( ROW_LABEL, COL_TOTAL, _( "Total" ) );
101 m_gridComponents->SetCellFont( ROW_LABEL, COL_TOTAL, headingFont );
102
103 m_gridBoard->SetCellValue( 0, 0, _( "Width:" ) );
104 m_gridBoard->SetCellAlignment( 0, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
105 m_gridBoard->SetCellValue( 1, 0, _( "Height:" ) );
106 m_gridBoard->SetCellAlignment( 1, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
107 m_gridBoard->SetCellValue( 2, 0, _( "Area:" ) );
108 m_gridBoard->SetCellAlignment( 2, 0, wxALIGN_LEFT, wxALIGN_CENTRE );
109
110 wxGrid* grids[] = { m_gridComponents, m_gridPads, m_gridVias, m_gridBoard };
111
112 for( auto& grid : grids )
113 {
114 // Remove wxgrid's selection boxes
115 grid->SetCellHighlightPenWidth( 0 );
116 grid->SetColMinimalAcceptableWidth( 80 );
117 for( int i = 0; i < grid->GetNumberRows(); i++ )
118 grid->SetCellAlignment( i, COL_LABEL, wxALIGN_LEFT, wxALIGN_CENTRE );
119 }
120
121 wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
122
124 || s_savedDialogState.m_project != Prj().GetProjectFullName() )
125 {
126 fn.SetName( fn.GetName() + wxT( "_report" ) );
127 fn.SetExt( wxT( "txt" ) );
128 s_savedDialogState.saveReportName = fn.GetFullName();
129 s_savedDialogState.saveReportFolder = wxPathOnly( Prj().GetProjectFullName() );
132 }
133
134 // The wxStdDialogButtonSizer wxID_CANCLE button is in fact a close button
135 // Nothing to cancel:
136 m_sdbControlSizerCancel->SetLabel( _( "Close" ) );
137}
138
139
141{
142 m_fpTypes.clear();
143
144 // If you need some more types to be shown, simply add them to the corresponding list
145 m_fpTypes.push_back( FP_LINE_ITEM( FP_THROUGH_HOLE, FP_THROUGH_HOLE, _( "THT:" ) ) );
146 m_fpTypes.push_back( FP_LINE_ITEM( FP_SMD, FP_SMD, _( "SMD:" ) ) );
147 m_fpTypes.push_back( FP_LINE_ITEM( FP_THROUGH_HOLE|FP_SMD, 0, _( "Unspecified:" ) ) );
148
149 m_padTypes.clear();
150 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::PTH, _( "Through hole:" ) ) );
151 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::SMD, _( "SMD:" ) ) );
152 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::CONN, _( "Connector:" ) ) );
153 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::NPTH, _( "NPTH:" ) ) );
154
155 m_viaTypes.clear();
156 m_viaTypes.push_back( LINE_ITEM<VIATYPE>( VIATYPE::THROUGH, _( "Through vias:" ) ) );
157 m_viaTypes.push_back( LINE_ITEM<VIATYPE>( VIATYPE::BLIND_BURIED, _( "Blind/buried:" ) ) );
158 m_viaTypes.push_back( LINE_ITEM<VIATYPE>( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
159
160 // If there not enough rows in grids, append some
161 int appendRows = m_fpTypes.size() + 2 - m_gridComponents->GetNumberRows();
162
163 if( appendRows > 0 )
164 m_gridComponents->AppendRows( appendRows );
165
166 appendRows = m_padTypes.size() + 1 - m_gridPads->GetNumberRows();
167
168 if( appendRows > 0 )
169 m_gridPads->AppendRows( appendRows );
170
171 appendRows = m_viaTypes.size() + 1 - m_gridVias->GetNumberRows();
172
173 if( appendRows )
174 m_gridVias->AppendRows( appendRows );
175}
176
177
179{
182 updateWidets();
183
184 Layout();
185 m_drillsPanel->Layout();
186
187 m_gridDrills->AutoSizeColumns();
190
191 // Add space for the vertical scrollbar, so that it won't overlap with the cells.
192 m_gridDrills->SetMinSize( wxSize( m_gridDrills->GetEffectiveMinSize().x
193 + wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ),
194 60 ) );
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( FP_LINE_ITEM& line : m_fpTypes )
217 {
218 if( ( footprint->GetAttributes() & line.attribute_mask ) == line.attribute_value )
219 {
220 if( footprint->IsFlipped() )
221 line.backSideQty++;
222 else
223 line.frontSideQty++;
224 break;
225 }
226 }
227
228 for( PAD* pad : footprint->Pads() )
229 {
230 // Go through pads types list
231 for( LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
232 {
233 if( pad->GetAttribute() == line.attribute )
234 {
235 line.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 DRILL_LINE_ITEM drill( pad->GetDrillSize().x, pad->GetDrillSize().y,
257 pad->GetDrillShape(),
258 pad->GetAttribute() != PAD_ATTRIB::NPTH,
259 true, top, bottom );
260
261 auto it = m_drillTypes.begin();
262
263 for( ; it != m_drillTypes.end(); ++it )
264 {
265 if( *it == drill )
266 {
267 it->qty++;
268 break;
269 }
270 }
271
272 if( it == m_drillTypes.end() )
273 {
274 drill.qty = 1;
275 m_drillTypes.push_back( drill );
276 m_gridDrills->InsertRows();
277 }
278 }
279 }
280 }
281
282 // Get via counts
283 for( PCB_TRACK* track : board->Tracks() )
284 {
285 if( PCB_VIA* via = dyn_cast<PCB_VIA*>( track ) )
286 {
287 for( LINE_ITEM<VIATYPE>& line : m_viaTypes )
288 {
289 if( via->GetViaType() == line.attribute )
290 {
291 line.qty++;
292 break;
293 }
294 }
295
296 DRILL_LINE_ITEM drill( via->GetDrillValue(), via->GetDrillValue(),
297 PAD_DRILL_SHAPE_CIRCLE, true, false, via->TopLayer(),
298 via->BottomLayer() );
299
300 auto it = m_drillTypes.begin();
301
302 for( ; it != m_drillTypes.end(); ++it )
303 {
304 if( *it == drill )
305 {
306 it->qty++;
307 break;
308 }
309 }
310
311 if( it == m_drillTypes.end() )
312 {
313 drill.qty = 1;
314 m_drillTypes.push_back( drill );
315 m_gridDrills->InsertRows();
316 }
317 }
318 }
319
320 sort( m_drillTypes.begin(), m_drillTypes.end(),
322
323 bool boundingBoxCreated = false; //flag if bounding box initialized
324 BOX2I bbox;
325 SHAPE_POLY_SET polySet;
326 m_hasOutline = board->GetBoardPolygonOutlines( polySet );
327
328 // If board has no Edge Cuts lines, board->GetBoardPolygonOutlines will
329 // return small rectangle, so we double check that
330 bool edgeCutsExists = false;
331
332 for( BOARD_ITEM* drawing : board->Drawings() )
333 {
334 if( drawing->GetLayer() == Edge_Cuts )
335 {
336 edgeCutsExists = true;
337 break;
338 }
339 }
340
341 if( !edgeCutsExists )
342 m_hasOutline = false;
343
344 if( m_hasOutline )
345 {
346 m_boardArea = 0.0;
347
348 for( int i = 0; i < polySet.OutlineCount(); i++ )
349 {
350 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
351 m_boardArea += outline.Area();
352
353 // If checkbox "subtract holes" is checked
354 if( m_checkBoxSubtractHoles->GetValue() )
355 {
356 for( int j = 0; j < polySet.HoleCount( i ); j++ )
357 m_boardArea -= polySet.Hole( i, j ).Area();
358 }
359
360 if( boundingBoxCreated )
361 {
362 bbox.Merge( outline.BBox() );
363 }
364 else
365 {
366 bbox = outline.BBox();
367 boundingBoxCreated = true;
368 }
369 }
370
371 m_boardWidth = bbox.GetWidth();
372 m_boardHeight = bbox.GetHeight();
373 }
374}
375
376
377static wxString formatCount( int aCount )
378{
379 return wxString::Format( wxT( "%i" ), aCount );
380};
381
382
384{
385 int totalPads = 0;
386 int row = 0;
387
388 for( const LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
389 {
390 m_gridPads->SetCellValue( row, COL_LABEL, line.title );
391 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.qty ) );
392 totalPads += line.qty;
393 row++;
394 }
395
396 m_gridPads->SetCellValue( row, COL_LABEL, _( "Total:" ) );
397 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( totalPads ) );
398
399 int totalVias = 0;
400 row = 0;
401
402 for( const LINE_ITEM<VIATYPE>& line : m_viaTypes )
403 {
404 m_gridVias->SetCellValue( row, COL_LABEL, line.title );
405 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( line.qty ) );
406 totalVias += line.qty;
407 row++;
408 }
409
410 m_gridVias->SetCellValue( row, COL_LABEL, _( "Total:" ) );
411 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( totalVias ) );
412
413
414 int totalFront = 0;
415 int totalBack = 0;
416
417 // We don't use row 0, as there labels are
418 row = 1;
419
420 for( const FP_LINE_ITEM& line : m_fpTypes )
421 {
422 m_gridComponents->SetCellValue( row, COL_LABEL, line.title );
423 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( line.frontSideQty ) );
424 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( line.backSideQty ) );
425 m_gridComponents->SetCellValue( row, 3, formatCount( line.frontSideQty + line.backSideQty ) );
426 totalFront += line.frontSideQty;
427 totalBack += line.backSideQty;
428 row++;
429 }
430
431 m_gridComponents->SetCellValue( row, COL_LABEL, _( "Total:" ) );
432 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( totalFront ) );
433 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( totalBack ) );
434 m_gridComponents->SetCellValue( row, COL_TOTAL, formatCount( totalFront + totalBack ) );
435
436 if( m_hasOutline )
437 {
438 m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT,
442 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
445 }
446 else
447 {
448 m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT, _( "unknown" ) );
449 m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT, _( "unknown" ) );
450 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
451 }
452
454
455 m_gridComponents->AutoSize();
456 m_gridPads->AutoSize();
457 m_gridBoard->AutoSize();
458 m_gridVias->AutoSize();
459
461}
462
463
465{
466 BOARD* board = m_parentFrame->GetBoard();
467 int row = 0;
468
469 for( const DRILL_LINE_ITEM& line : m_drillTypes )
470 {
471 wxString shapeStr;
472 wxString startLayerStr;
473 wxString stopLayerStr;
474
475 switch( line.shape )
476 {
477 case PAD_DRILL_SHAPE_CIRCLE: shapeStr = _( "Round" ); break;
478 case PAD_DRILL_SHAPE_OBLONG: shapeStr = _( "Slot" ); break;
479 default: shapeStr = _( "???" ); break;
480 }
481
482 if( line.startLayer == UNDEFINED_LAYER )
483 startLayerStr = _( "N/A" );
484 else
485 startLayerStr = board->GetLayerName( line.startLayer );
486
487 if( line.stopLayer == UNDEFINED_LAYER )
488 stopLayerStr = _( "N/A" );
489 else
490 stopLayerStr = board->GetLayerName( line.stopLayer );
491
492 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_COUNT, formatCount( line.qty ) );
493 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_SHAPE, shapeStr );
494 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_X_SIZE,
495 m_parentFrame->MessageTextFromValue( line.xSize ) );
496 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_Y_SIZE,
497 m_parentFrame->MessageTextFromValue( line.ySize ) );
498 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_PLATED,
499 line.isPlated ? _( "PTH" ) : _( "NPTH" ) );
500 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_VIA_PAD,
501 line.isPad ? _( "Pad" ) : _( "Via" ) );
502 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_START_LAYER, startLayerStr );
503 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerStr );
504
505 row++;
506 }
507}
508
509
510void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
511 bool aUseColLabels,
512 bool aUseFirstColAsLabel )
513{
514 std::vector<int> widths( aGrid->GetNumberCols(), 0 );
515 int rowLabelsWidth = 0;
516
517 // Determine column widths.
518
519 if( aUseColLabels )
520 {
521 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
522 widths[col] = aGrid->GetColLabelValue( col ).length();
523 }
524
525 for( int row = 0; row < aGrid->GetNumberRows(); row++ )
526 {
527 rowLabelsWidth = std::max<int>( rowLabelsWidth, aGrid->GetRowLabelValue( row ).length() );
528
529 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
530 widths[col] = std::max<int>( widths[col], aGrid->GetCellValue( row, col ).length() );
531 }
532
533 // Print the cells.
534
535 wxString tmp;
536
537 // Print column labels.
538
539 aStr << wxT( "|" );
540
541 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
542 {
543 if( aUseColLabels )
544 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetColLabelValue( col ) );
545 else
546 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( 0, col ) );
547
548 aStr << tmp;
549 }
550
551 aStr << wxT( "\n" );
552
553 // Print column label horizontal separators.
554
555 aStr << wxT( "|" );
556
557 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
558 {
559 aStr << wxT( "-" );
560 aStr.Append( '-', widths[col] );
561 aStr << wxT( "-|" );
562 }
563
564 aStr << wxT( "\n" );
565
566 // Print regular cells.
567
568 int firstRow = 0, firstCol = 0;
569
570 if( !aUseColLabels )
571 firstRow = 1;
572
573 if( aUseFirstColAsLabel )
574 firstCol = 1;
575
576 for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
577 {
578 if( aUseFirstColAsLabel )
579 tmp.Printf( wxT( "|%-*s |" ), widths[0], aGrid->GetCellValue( row, 0 ) );
580 else
581 tmp.Printf( wxT( "|" ) );
582
583 aStr << tmp;
584
585 for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
586 {
587 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( row, col ) );
588 aStr << tmp;
589 }
590
591 aStr << wxT( "\n" );
592 }
593}
594
595
597{
598 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_gridDrills );
599
601
602 double remainingWidth = KIPLATFORM::UI::GetUnobscuredSize( m_gridDrills ).x;
603
604 // Find the total current width
605 for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
606 {
608 remainingWidth -= m_gridDrills->GetColSize( i );
609 }
610
611 double scalingFactor = std::max( 1.0,
612 remainingWidth
614 int startLayerColWidth = static_cast<int>( m_startLayerColInitialSize * scalingFactor );
615 int stopLayerColWidth = static_cast<int>( m_stopLayerColInitialSize * scalingFactor );
616
617 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_START_LAYER, startLayerColWidth );
618 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerColWidth );
619}
620
621
622void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
623{
628 updateWidets();
629 Layout();
630 m_drillsPanel->Layout();
631}
632
633
634void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
635{
636 FILE* outFile;
637 wxString msg;
638 wxString boardName;
639
640 wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
641 boardName = fn.GetName();
642 wxFileDialog dlg( this, _( "Save Report File" ), s_savedDialogState.saveReportFolder,
644 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
645
646 if( dlg.ShowModal() == wxID_CANCEL )
647 return;
648
649 s_savedDialogState.saveReportFolder = wxPathOnly( dlg.GetPath() );
650 s_savedDialogState.saveReportName = dlg.GetFilename();
651
652 outFile = wxFopen( dlg.GetPath(), wxT( "wt" ) );
653
654 if( outFile == nullptr )
655 {
656 msg.Printf( _( "Failed to create file '%s'." ), dlg.GetPath() );
657 DisplayErrorMessage( this, msg );
658 return;
659 }
660
661 msg << _( "PCB statistics report\n=====================" ) << wxT( "\n" );
662 msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxT( "\n" );
663 msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << wxT( "\n" );
664 msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << wxT( "\n" );
665
666 msg << wxT( "\n" );
667 msg << _( "Board" ) << wxT( "\n-----\n" );
668
669 if( m_hasOutline )
670 {
671 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " )
672 << m_parentFrame->MessageTextFromValue( m_boardWidth ) << wxT( "\n" );
673 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )
675 msg << wxS( "- " ) << _( "Area" ) + wxS( ": " )
677 msg << wxT( "\n" );
678 }
679 else
680 {
681 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
682 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
683 msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
684 }
685
686 msg << wxT( "\n" );
687 msg << _( "Pads" ) << wxT( "\n----\n" );
688
689 for( auto& type : m_padTypes )
690 msg << wxT( "- " ) << type.title << wxS( " " ) << type.qty << wxT( "\n" );
691
692 msg << wxT( "\n" );
693 msg << _( "Vias" ) << wxT( "\n----\n" );
694
695 for( auto& type : m_viaTypes )
696 msg << wxT( "- " ) << type.title << wxS( " " ) << type.qty << wxT( "\n" );
697
698 // We will save data about components in the table.
699 // We have to calculate column widths
700 std::vector<int> widths;
701 std::vector<wxString> labels{ wxT( "" ), _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
702 wxString tmp;
703
704 widths.reserve( labels.size() );
705
706 for( const wxString& label : labels )
707 widths.push_back( label.size() );
708
709 int frontTotal = 0;
710 int backTotal = 0;
711
712 for( const FP_LINE_ITEM& line : m_fpTypes )
713 {
714 // Get maximum width for left label column
715 widths[0] = std::max<int>( line.title.size(), widths[0] );
716 frontTotal += line.frontSideQty;
717 backTotal += line.backSideQty;
718 }
719
720 // Get maximum width for other columns
721 tmp.Printf( wxT( "%i" ), frontTotal );
722 widths[1] = std::max<int>( tmp.size(), widths[1] );
723 tmp.Printf( wxT( "%i" ), backTotal );
724 widths[2] = std::max<int>( tmp.size(), widths[2] );
725 tmp.Printf( wxT( "%i" ), frontTotal + backTotal );
726 widths[3] = std::max<int>( tmp.size(), widths[3] );
727
728 //Write components amount to file
729 msg << wxT( "\n" );
730 msg << _( "Components" ) << wxT( "\n----------\n" );
731 msg << wxT( "\n" );
732
733 printGridToStringAsTable( m_gridComponents, msg, false, true );
734
735 msg << wxT( "\n" );
736 msg << _( "Drill holes" ) << wxT( "\n-----------\n" );
737 msg << wxT( "\n" );
738
739 printGridToStringAsTable( m_gridDrills, msg, true, false );
740
741 if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
742 {
743 msg.Printf( _( "Error writing file '%s'." ), dlg.GetPath() );
744 DisplayErrorMessage( this, msg );
745 }
746
747 fclose( outFile );
748}
749
750
751void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
752{
753 aEvent.Skip();
755}
756
757void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
758{
759 DRILL_LINE_ITEM::COL_ID colId = static_cast<DRILL_LINE_ITEM::COL_ID>( aEvent.GetCol() );
760 bool ascending = !( m_gridDrills->IsSortingBy( colId )
761 && m_gridDrills->IsSortOrderAscending() );
762
763 sort( m_drillTypes.begin(), m_drillTypes.end(), DRILL_LINE_ITEM::COMPARE( colId, ascending ) );
764
766}
767
768
770{
771}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
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:1893
FOOTPRINTS & Footprints()
Definition: board.h:307
TRACKS & Tracks()
Definition: board.h:304
const wxString & GetFileName() const
Definition: board.h:302
DRAWINGS & Drawings()
Definition: board.h:310
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:400
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:588
Class DIALOG_BOARD_STATISTICS_BASE.
void checkboxClicked(wxCommandEvent &aEvent) override
Save board statistics to a file.
void printGridToStringAsTable(wxGrid *aGrid, wxString &aStr, bool aUseColLabels, bool aUseFirstColAsLabel)
std::deque< FP_LINE_ITEM > m_fpTypes
int m_startLayerColInitialSize
Width of the start layer column as calculated by the wxWidgets autosizing algorithm.
void updateWidets()
Update drills grid.
void saveReportClicked(wxCommandEvent &aEvent) override
std::deque< LINE_ITEM< PAD_ATTRIB > > m_padTypes
void updateDrillGrid()
Print grid to string in tabular format.
~DIALOG_BOARD_STATISTICS()
Get data from the PCB board and print it to dialog.
int m_stopLayerColInitialSize
Width of the stop layer column.
DIALOG_BOARD_STATISTICS(PCB_EDIT_FRAME *aParentFrame)
void drillGridSort(wxGridEvent &aEvent)
void refreshItemsTypes()
< Function to fill up all items types to be shown in the dialog.
void drillGridSize(wxSizeEvent &aEvent) override
bool m_hasOutline
Show if board outline properly defined.
std::deque< DRILL_LINE_ITEM > m_drillTypes
std::deque< LINE_ITEM< VIATYPE > > m_viaTypes
void getDataFromPCB()
Apply data to dialog widgets.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Definition: pad.h:58
BOARD * GetBoard() const
The main frame for Pcbnew.
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:119
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition: project.cpp:131
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
double Area(bool aAbsolute=true) const
Return the area of this chain.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Outline(int aIndex)
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
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 DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:299
This file is part of the common library.
static DIALOG_BOARD_STATISTICS_SAVED_STATE s_savedDialogState
#define ROW_BOARD_AREA
#define COL_AMOUNT
#define ROW_LABEL
#define COL_BOTTOM_SIDE
#define ROW_BOARD_WIDTH
#define COL_LABEL
#define COL_TOTAL
static wxString formatCount(int aCount)
#define COL_FRONT_SIDE
#define ROW_BOARD_HEIGHT
#define _(s)
@ FP_SMD
Definition: footprint.h:68
@ FP_THROUGH_HOLE
Definition: footprint.h:67
wxString TextFileWildcard()
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ Edge_Cuts
Definition: layer_ids.h:113
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:126
wxFont GetStatusFont(wxWindow *aWindow)
Definition: ui_common.cpp:132
@ NPTH
like PAD_PTH, but not plated
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
@ CONN
Like smd, does not appear on the solder paste layer (default)
@ PAD_DRILL_SHAPE_CIRCLE
Definition: pad_shapes.h:70
@ PAD_DRILL_SHAPE_OBLONG
Definition: pad_shapes.h:71
@ BLIND_BURIED
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
Footprint attributes (such as SMD, THT, Virtual and so on), which will be shown in the dialog.
Type information, which will be shown in dialog.
Definition of file extensions used in Kicad.