KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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 switch( footprint->GetSide() )
221 {
222 case F_Cu: line.frontSideQty++; break;
223 case B_Cu: line.backSideQty++; break;
224 default: /* unsided: user-layers only, etc. */ break;
225 }
226
227 break;
228 }
229 }
230
231 for( PAD* pad : footprint->Pads() )
232 {
233 // Go through pads types list
234 for( LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
235 {
236 if( pad->GetAttribute() == line.attribute )
237 {
238 line.qty++;
239 break;
240 }
241 }
242
243 if( pad->GetDrillSize().x > 0 && pad->GetDrillSize().y > 0 )
244 {
245 PCB_LAYER_ID top, bottom;
246
247 if( pad->GetLayerSet().CuStack().empty() )
248 {
249 // The pad is not on any copper layer
250 top = UNDEFINED_LAYER;
251 bottom = UNDEFINED_LAYER;
252 }
253 else
254 {
255 top = pad->GetLayerSet().CuStack().front();
256 bottom = pad->GetLayerSet().CuStack().back();
257 }
258
259 DRILL_LINE_ITEM drill( pad->GetDrillSize().x, pad->GetDrillSize().y,
260 pad->GetDrillShape(),
261 pad->GetAttribute() != PAD_ATTRIB::NPTH,
262 true, top, bottom );
263
264 auto it = m_drillTypes.begin();
265
266 for( ; it != m_drillTypes.end(); ++it )
267 {
268 if( *it == drill )
269 {
270 it->qty++;
271 break;
272 }
273 }
274
275 if( it == m_drillTypes.end() )
276 {
277 drill.qty = 1;
278 m_drillTypes.push_back( drill );
279 m_gridDrills->InsertRows();
280 }
281 }
282 }
283 }
284
285 // Get via counts
286 for( PCB_TRACK* track : board->Tracks() )
287 {
288 if( track->Type() == PCB_VIA_T )
289 {
290 PCB_VIA* via = static_cast<PCB_VIA*>( track );
291
292 for( LINE_ITEM<VIATYPE>& line : m_viaTypes )
293 {
294 if( via->GetViaType() == line.attribute )
295 {
296 line.qty++;
297 break;
298 }
299 }
300
301 DRILL_LINE_ITEM drill( via->GetDrillValue(), via->GetDrillValue(),
302 PAD_DRILL_SHAPE::CIRCLE, true, false, via->TopLayer(),
303 via->BottomLayer() );
304
305 auto it = m_drillTypes.begin();
306
307 for( ; it != m_drillTypes.end(); ++it )
308 {
309 if( *it == drill )
310 {
311 it->qty++;
312 break;
313 }
314 }
315
316 if( it == m_drillTypes.end() )
317 {
318 drill.qty = 1;
319 m_drillTypes.push_back( drill );
320 m_gridDrills->InsertRows();
321 }
322 }
323 }
324
325 sort( m_drillTypes.begin(), m_drillTypes.end(),
327
328 bool boundingBoxCreated = false; //flag if bounding box initialized
329 BOX2I bbox;
330 SHAPE_POLY_SET polySet;
331 m_hasOutline = board->GetBoardPolygonOutlines( polySet );
332
333 // If board has no Edge Cuts lines, board->GetBoardPolygonOutlines will
334 // return small rectangle, so we double check that
335 bool edgeCutsExists = false;
336
337 for( BOARD_ITEM* drawing : board->Drawings() )
338 {
339 if( drawing->GetLayer() == Edge_Cuts )
340 {
341 edgeCutsExists = true;
342 break;
343 }
344 }
345
346 if( !edgeCutsExists )
347 m_hasOutline = false;
348
349 if( m_hasOutline )
350 {
351 m_boardArea = 0.0;
352
353 for( int i = 0; i < polySet.OutlineCount(); i++ )
354 {
355 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
356 m_boardArea += outline.Area();
357
358 // If checkbox "subtract holes" is checked
359 if( m_checkBoxSubtractHoles->GetValue() )
360 {
361 for( int j = 0; j < polySet.HoleCount( i ); j++ )
362 m_boardArea -= polySet.Hole( i, j ).Area();
363
364 for( FOOTPRINT* fp : board->Footprints() )
365 {
366 for( PAD* pad : fp->Pads() )
367 {
368 if( !pad->HasHole() )
369 continue;
370
371 auto hole = pad->GetEffectiveHoleShape();
372 const SEG& seg = hole->GetSeg();
373 double width = hole->GetWidth();
374 double area = seg.Length() * width;
375
376 // Each end of the hole is a half-circle, so together, we have one
377 // full circle. The area of a circle is pi * r^2, so the area of the
378 // hole is pi * (d/2)^2 = pi * 1/4 * d^2.
379 area += M_PI * 0.25 * width * width;
380 m_boardArea -= area;
381 }
382 }
383
384 for( PCB_TRACK* track : board->Tracks() )
385 {
386 if( track->Type() == PCB_VIA_T )
387 {
388 PCB_VIA* via = static_cast<PCB_VIA*>( track );
389 double drill = via->GetDrillValue();
390 m_boardArea -= M_PI * 0.25 * drill * drill;
391 }
392 }
393 }
394
395 if( boundingBoxCreated )
396 {
397 bbox.Merge( outline.BBox() );
398 }
399 else
400 {
401 bbox = outline.BBox();
402 boundingBoxCreated = true;
403 }
404 }
405
406 m_boardWidth = bbox.GetWidth();
407 m_boardHeight = bbox.GetHeight();
408 }
409}
410
411
412static wxString formatCount( int aCount )
413{
414 return wxString::Format( wxT( "%i" ), aCount );
415};
416
417
419{
420 int totalPads = 0;
421 int row = 0;
422
423 for( const LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
424 {
425 m_gridPads->SetCellValue( row, COL_LABEL, line.title );
426 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.qty ) );
427 totalPads += line.qty;
428 row++;
429 }
430
431 m_gridPads->SetCellValue( row, COL_LABEL, _( "Total:" ) );
432 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( totalPads ) );
433
434 int totalVias = 0;
435 row = 0;
436
437 for( const LINE_ITEM<VIATYPE>& line : m_viaTypes )
438 {
439 m_gridVias->SetCellValue( row, COL_LABEL, line.title );
440 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( line.qty ) );
441 totalVias += line.qty;
442 row++;
443 }
444
445 m_gridVias->SetCellValue( row, COL_LABEL, _( "Total:" ) );
446 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( totalVias ) );
447
448
449 int totalFront = 0;
450 int totalBack = 0;
451
452 // We don't use row 0, as there labels are
453 row = 1;
454
455 for( const FP_LINE_ITEM& line : m_fpTypes )
456 {
457 m_gridComponents->SetCellValue( row, COL_LABEL, line.title );
458 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( line.frontSideQty ) );
459 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( line.backSideQty ) );
460 m_gridComponents->SetCellValue( row, 3, formatCount( line.frontSideQty + line.backSideQty ) );
461 totalFront += line.frontSideQty;
462 totalBack += line.backSideQty;
463 row++;
464 }
465
466 m_gridComponents->SetCellValue( row, COL_LABEL, _( "Total:" ) );
467 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( totalFront ) );
468 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( totalBack ) );
469 m_gridComponents->SetCellValue( row, COL_TOTAL, formatCount( totalFront + totalBack ) );
470
471 if( m_hasOutline )
472 {
473 m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT,
477 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
479 EDA_DATA_TYPE::AREA ) );
480 }
481 else
482 {
483 m_gridBoard->SetCellValue( ROW_BOARD_WIDTH, COL_AMOUNT, _( "unknown" ) );
484 m_gridBoard->SetCellValue( ROW_BOARD_HEIGHT, COL_AMOUNT, _( "unknown" ) );
485 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
486 }
487
489
490 m_gridComponents->AutoSize();
491 m_gridPads->AutoSize();
492 m_gridBoard->AutoSize();
493 m_gridVias->AutoSize();
494
496}
497
498
500{
501 BOARD* board = m_parentFrame->GetBoard();
502 int row = 0;
503
504 for( const DRILL_LINE_ITEM& line : m_drillTypes )
505 {
506 wxString shapeStr;
507 wxString startLayerStr;
508 wxString stopLayerStr;
509
510 switch( line.shape )
511 {
512 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
513 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
514 default: shapeStr = _( "???" ); break;
515 }
516
517 if( line.startLayer == UNDEFINED_LAYER )
518 startLayerStr = _( "N/A" );
519 else
520 startLayerStr = board->GetLayerName( line.startLayer );
521
522 if( line.stopLayer == UNDEFINED_LAYER )
523 stopLayerStr = _( "N/A" );
524 else
525 stopLayerStr = board->GetLayerName( line.stopLayer );
526
527 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_COUNT, formatCount( line.qty ) );
528 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_SHAPE, shapeStr );
529 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_X_SIZE,
530 m_parentFrame->MessageTextFromValue( line.xSize ) );
531 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_Y_SIZE,
532 m_parentFrame->MessageTextFromValue( line.ySize ) );
533 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_PLATED,
534 line.isPlated ? _( "PTH" ) : _( "NPTH" ) );
535 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_VIA_PAD,
536 line.isPad ? _( "Pad" ) : _( "Via" ) );
537 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_START_LAYER, startLayerStr );
538 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerStr );
539
540 row++;
541 }
542}
543
544
545void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
546 bool aUseColLabels,
547 bool aUseFirstColAsLabel )
548{
549 std::vector<int> widths( aGrid->GetNumberCols(), 0 );
550 int rowLabelsWidth = 0;
551
552 // Determine column widths.
553
554 if( aUseColLabels )
555 {
556 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
557 widths[col] = aGrid->GetColLabelValue( col ).length();
558 }
559
560 for( int row = 0; row < aGrid->GetNumberRows(); row++ )
561 {
562 rowLabelsWidth = std::max<int>( rowLabelsWidth, aGrid->GetRowLabelValue( row ).length() );
563
564 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
565 widths[col] = std::max<int>( widths[col], aGrid->GetCellValue( row, col ).length() );
566 }
567
568 // Print the cells.
569
570 wxString tmp;
571
572 // Print column labels.
573
574 aStr << wxT( "|" );
575
576 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
577 {
578 if( aUseColLabels )
579 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetColLabelValue( col ) );
580 else
581 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( 0, col ) );
582
583 aStr << tmp;
584 }
585
586 aStr << wxT( "\n" );
587
588 // Print column label horizontal separators.
589
590 aStr << wxT( "|" );
591
592 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
593 {
594 aStr << wxT( "-" );
595 aStr.Append( '-', widths[col] );
596 aStr << wxT( "-|" );
597 }
598
599 aStr << wxT( "\n" );
600
601 // Print regular cells.
602
603 int firstRow = 0, firstCol = 0;
604
605 if( !aUseColLabels )
606 firstRow = 1;
607
608 if( aUseFirstColAsLabel )
609 firstCol = 1;
610
611 for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
612 {
613 if( aUseFirstColAsLabel )
614 tmp.Printf( wxT( "|%-*s |" ), widths[0], aGrid->GetCellValue( row, 0 ) );
615 else
616 tmp.Printf( wxT( "|" ) );
617
618 aStr << tmp;
619
620 for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
621 {
622 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( row, col ) );
623 aStr << tmp;
624 }
625
626 aStr << wxT( "\n" );
627 }
628}
629
630
632{
633 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_gridDrills );
634
636
637 double remainingWidth = KIPLATFORM::UI::GetUnobscuredSize( m_gridDrills ).x;
638
639 // Find the total current width
640 for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
641 {
643 remainingWidth -= m_gridDrills->GetColSize( i );
644 }
645
646 double scalingFactor = std::max( 1.0,
647 remainingWidth
649 int startLayerColWidth = static_cast<int>( m_startLayerColInitialSize * scalingFactor );
650 int stopLayerColWidth = static_cast<int>( m_stopLayerColInitialSize * scalingFactor );
651
652 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_START_LAYER, startLayerColWidth );
653 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerColWidth );
654}
655
656
657void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
658{
663 updateWidets();
664 Layout();
665 m_drillsPanel->Layout();
666}
667
668
669void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
670{
671 FILE* outFile;
672 wxString msg;
673 wxString boardName;
674
675 wxFileName fn = m_parentFrame->GetBoard()->GetFileName();
676 boardName = fn.GetName();
677 wxFileDialog dlg( this, _( "Save Report File" ), s_savedDialogState.saveReportFolder,
679 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
680
681 if( dlg.ShowModal() == wxID_CANCEL )
682 return;
683
684 s_savedDialogState.saveReportFolder = wxPathOnly( dlg.GetPath() );
685 s_savedDialogState.saveReportName = dlg.GetFilename();
686
687 outFile = wxFopen( dlg.GetPath(), wxT( "wt" ) );
688
689 if( outFile == nullptr )
690 {
691 msg.Printf( _( "Failed to create file '%s'." ), dlg.GetPath() );
692 DisplayErrorMessage( this, msg );
693 return;
694 }
695
696 msg << _( "PCB statistics report\n=====================" ) << wxT( "\n" );
697 msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxT( "\n" );
698 msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << wxT( "\n" );
699 msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << wxT( "\n" );
700
701 msg << wxT( "\n" );
702 msg << _( "Board" ) << wxT( "\n-----\n" );
703
704 if( m_hasOutline )
705 {
706 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " )
707 << m_parentFrame->MessageTextFromValue( m_boardWidth ) << wxT( "\n" );
708 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )
710 msg << wxS( "- " ) << _( "Area" ) + wxS( ": " )
711 << m_parentFrame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA );
712 msg << wxT( "\n" );
713 }
714 else
715 {
716 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
717 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
718 msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
719 }
720
721 msg << wxT( "\n" );
722 msg << _( "Pads" ) << wxT( "\n----\n" );
723
724 for( auto& type : m_padTypes )
725 msg << wxT( "- " ) << type.title << wxS( " " ) << type.qty << wxT( "\n" );
726
727 msg << wxT( "\n" );
728 msg << _( "Vias" ) << wxT( "\n----\n" );
729
730 for( auto& type : m_viaTypes )
731 msg << wxT( "- " ) << type.title << wxS( " " ) << type.qty << wxT( "\n" );
732
733 // We will save data about components in the table.
734 // We have to calculate column widths
735 std::vector<int> widths;
736 std::vector<wxString> labels{ wxT( "" ), _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
737 wxString tmp;
738
739 widths.reserve( labels.size() );
740
741 for( const wxString& label : labels )
742 widths.push_back( label.size() );
743
744 int frontTotal = 0;
745 int backTotal = 0;
746
747 for( const FP_LINE_ITEM& line : m_fpTypes )
748 {
749 // Get maximum width for left label column
750 widths[0] = std::max<int>( line.title.size(), widths[0] );
751 frontTotal += line.frontSideQty;
752 backTotal += line.backSideQty;
753 }
754
755 // Get maximum width for other columns
756 tmp.Printf( wxT( "%i" ), frontTotal );
757 widths[1] = std::max<int>( tmp.size(), widths[1] );
758 tmp.Printf( wxT( "%i" ), backTotal );
759 widths[2] = std::max<int>( tmp.size(), widths[2] );
760 tmp.Printf( wxT( "%i" ), frontTotal + backTotal );
761 widths[3] = std::max<int>( tmp.size(), widths[3] );
762
763 //Write components amount to file
764 msg << wxT( "\n" );
765 msg << _( "Components" ) << wxT( "\n----------\n" );
766 msg << wxT( "\n" );
767
768 printGridToStringAsTable( m_gridComponents, msg, false, true );
769
770 msg << wxT( "\n" );
771 msg << _( "Drill holes" ) << wxT( "\n-----------\n" );
772 msg << wxT( "\n" );
773
774 printGridToStringAsTable( m_gridDrills, msg, true, false );
775
776 if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
777 {
778 msg.Printf( _( "Error writing file '%s'." ), dlg.GetPath() );
779 DisplayErrorMessage( this, msg );
780 }
781
782 fclose( outFile );
783}
784
785
786void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
787{
788 aEvent.Skip();
790}
791
792void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
793{
794 DRILL_LINE_ITEM::COL_ID colId = static_cast<DRILL_LINE_ITEM::COL_ID>( aEvent.GetCol() );
795 bool ascending = !( m_gridDrills->IsSortingBy( colId )
796 && m_gridDrills->IsSortOrderAscending() );
797
798 sort( m_drillTypes.begin(), m_drillTypes.end(), DRILL_LINE_ITEM::COMPARE( colId, ascending ) );
799
801}
802
803
805{
806}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:282
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:2373
const FOOTPRINTS & Footprints() const
Definition: board.h:323
const TRACKS & Tracks() const
Definition: board.h:321
const wxString & GetFileName() const
Definition: board.h:319
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:564
const DRAWINGS & Drawings() const
Definition: board.h:325
size_type GetHeight() const
Definition: box2.h:205
size_type GetWidth() const
Definition: box2.h:204
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:623
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:53
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:129
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition: project.cpp:147
Definition: seg.h:42
int Length() const
Return the length (this).
Definition: seg.h:326
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
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int OutlineCount() const
Return the number of outlines in the set.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
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:727
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:186
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:73
@ FP_THROUGH_HOLE
Definition: footprint.h:72
static wxString TextFileWildcard()
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ Edge_Cuts
Definition: layer_ids.h:113
@ B_Cu
Definition: layer_ids.h:95
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:195
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
Definition: ui_common.cpp:130
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
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.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
Definition of file extensions used in Kicad.