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 The 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>
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_DIMS 0
47#define ROW_BOARD_AREA 1
48#define ROW_FRONT_COPPER_AREA 2
49#define ROW_BACK_COPPER_AREA 3
50
51
56{
58 excludeNoPins( false ),
59 subtractHoles( false ),
62 {
63 }
64
65 // Flags to remember last checkboxes state
69
70 // Variables to save last report file name and folder
71 bool saveReportInitialized; // true after the 3 next string are initialized
72 wxString saveReportFolder; // last report folder
73 wxString saveReportName; // last report filename
74 wxString m_project; // name of the project used to create the last report
75 // used to reinit last state after a project change
76};
77
78
80
82 DIALOG_BOARD_STATISTICS_BASE( aParentFrame ),
83 m_frame( aParentFrame ),
84 m_boardWidth( 0 ),
85 m_boardHeight( 0 ),
86 m_boardArea( 0.0 ),
87 m_frontCopperArea( 0.0 ),
88 m_backCopperArea( 0.0 ),
89 m_hasOutline( false ),
90 m_startLayerColInitialSize( 1 ),
91 m_stopLayerColInitialSize( 1 )
92{
93 m_gridDrills->Connect( wxEVT_GRID_COL_SORT,
94 wxGridEventHandler( DIALOG_BOARD_STATISTICS::drillGridSort ),
95 nullptr, this );
96
100
101 wxFont labelFont = KIUI::GetSmallInfoFont( this );
102 m_componentsLabel->SetFont( labelFont );
103 m_boardLabel->SetFont( labelFont );
104 m_padsLabel->SetFont( labelFont );
105 m_viasLabel->SetFont( labelFont );
106
107 // Make labels for grids
108 m_gridComponents->SetCellValue( ROW_LABEL, COL_FRONT_SIDE, _( "Front Side" ) );
109 m_gridComponents->SetCellValue( ROW_LABEL, COL_BOTTOM_SIDE, _( "Back Side" ) );
110 m_gridComponents->SetCellValue( ROW_LABEL, COL_TOTAL, _( "Total" ) );
111
112 wxFont headingFont = KIUI::GetStatusFont( this );
113
114 for( int col = COL_AMOUNT; col < m_gridComponents->GetNumberCols(); col++ )
115 m_gridComponents->SetCellFont( ROW_LABEL, col, headingFont );
116
117 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_LABEL, _( "Dimensions:" ) );
118 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_LABEL, _( "Area:" ) );
119 m_gridBoard->SetCellValue( ROW_FRONT_COPPER_AREA, COL_LABEL, _( "Front copper area:" ) );
120 m_gridBoard->SetCellValue( ROW_BACK_COPPER_AREA, COL_LABEL, _( "Back copper area:" ) );
121
123 {
124 // Remove wxgrid's selection boxes
125 grid->SetCellHighlightPenWidth( 0 );
126 grid->SetColMinimalAcceptableWidth( 80 );
127
128 for( int row = 0; row < grid->GetNumberRows(); row++ )
129 {
130 grid->SetCellAlignment( row, COL_LABEL, wxALIGN_LEFT, wxALIGN_CENTRE );
131
132 for( int col = COL_AMOUNT; col < grid->GetNumberCols(); col++ )
133 grid->SetCellAlignment( row, col, wxALIGN_RIGHT, wxALIGN_CENTER );
134 }
135 }
136
137 wxFileName fn = m_frame->GetBoard()->GetFileName();
138
140 || s_savedDialogState.m_project != Prj().GetProjectFullName() )
141 {
142 fn.SetName( fn.GetName() + wxT( "_report" ) );
143 fn.SetExt( wxT( "txt" ) );
144 s_savedDialogState.saveReportName = fn.GetFullName();
145 s_savedDialogState.saveReportFolder = wxPathOnly( Prj().GetProjectFullName() );
148 }
149
150 // The wxStdDialogButtonSizer wxID_CANCLE button is in fact a close button
151 // Nothing to cancel:
152 m_sdbControlSizerCancel->SetLabel( _( "Close" ) );
153}
154
155
157{
158 m_fpTypes.clear();
159
160 // If you need some more types to be shown, simply add them to the corresponding list
161 m_fpTypes.push_back( FP_LINE_ITEM( FP_THROUGH_HOLE, FP_THROUGH_HOLE, _( "THT:" ) ) );
162 m_fpTypes.push_back( FP_LINE_ITEM( FP_SMD, FP_SMD, _( "SMD:" ) ) );
163 m_fpTypes.push_back( FP_LINE_ITEM( FP_THROUGH_HOLE|FP_SMD, 0, _( "Unspecified:" ) ) );
164
165 m_padTypes.clear();
166 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::PTH, _( "Through hole:" ) ) );
167 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::SMD, _( "SMD:" ) ) );
168 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::CONN, _( "Connector:" ) ) );
169 m_padTypes.push_back( LINE_ITEM<PAD_ATTRIB>( PAD_ATTRIB::NPTH, _( "NPTH:" ) ) );
170
171 m_viaTypes.clear();
172 m_viaTypes.push_back( LINE_ITEM<VIATYPE>( VIATYPE::THROUGH, _( "Through vias:" ) ) );
173 m_viaTypes.push_back( LINE_ITEM<VIATYPE>( VIATYPE::BLIND_BURIED, _( "Blind/buried:" ) ) );
174 m_viaTypes.push_back( LINE_ITEM<VIATYPE>( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
175
176 // If there not enough rows in grids, append some
177 int appendRows = (int) m_fpTypes.size() + 2 - m_gridComponents->GetNumberRows();
178
179 if( appendRows > 0 )
180 m_gridComponents->AppendRows( appendRows );
181
182 appendRows = (int) m_padTypes.size() + 1 - m_gridPads->GetNumberRows();
183
184 if( appendRows > 0 )
185 m_gridPads->AppendRows( appendRows );
186
187 appendRows = (int) m_viaTypes.size() + 1 - m_gridVias->GetNumberRows();
188
189 if( appendRows )
190 m_gridVias->AppendRows( appendRows );
191}
192
193
195{
198 updateWidets();
199
200 Layout();
201 m_drillsPanel->Layout();
202
203 m_gridDrills->AutoSizeColumns();
206
207 // Add space for the vertical scrollbar, so that it won't overlap with the cells.
208 m_gridDrills->SetMinSize( wxSize( m_gridDrills->GetEffectiveMinSize().x
209 + wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ),
210 60 ) );
211
213
215 return true;
216}
217
218
220{
221 BOARD* board = m_frame->GetBoard();
222 SHAPE_POLY_SET frontCopper;
223 SHAPE_POLY_SET backCopper;
224 SHAPE_POLY_SET frontHoles;
225 SHAPE_POLY_SET backHoles;
226
227 m_drillTypes.clear();
229
230 // Get footprints and pads count
231 for( FOOTPRINT* footprint : board->Footprints() )
232 {
233 // Do not proceed footprints with no pads if checkbox checked
234 if( m_checkBoxExcludeComponentsNoPins->GetValue() && ! footprint->Pads().size() )
235 continue;
236
237 // Go through components types list
238 for( FP_LINE_ITEM& line : m_fpTypes )
239 {
240 if( ( footprint->GetAttributes() & line.attribute_mask ) == line.attribute_value )
241 {
242 switch( footprint->GetSide() )
243 {
244 case F_Cu: line.frontSideQty++; break;
245 case B_Cu: line.backSideQty++; break;
246 default: /* unsided: user-layers only, etc. */ break;
247 }
248
249 break;
250 }
251 }
252
253 for( PAD* pad : footprint->Pads() )
254 {
255 // Go through pads types list
256 for( LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
257 {
258 if( pad->GetAttribute() == line.attribute )
259 {
260 line.qty++;
261 break;
262 }
263 }
264
265 if( pad->GetDrillSize().x > 0 && pad->GetDrillSize().y > 0 )
266 {
267 PCB_LAYER_ID top, bottom;
268
269 if( pad->GetLayerSet().CuStack().empty() )
270 {
271 // The pad is not on any copper layer
272 top = UNDEFINED_LAYER;
273 bottom = UNDEFINED_LAYER;
274 }
275 else
276 {
277 top = pad->GetLayerSet().CuStack().front();
278 bottom = pad->GetLayerSet().CuStack().back();
279 }
280
281 DRILL_LINE_ITEM drill( pad->GetDrillSize().x, pad->GetDrillSize().y,
282 pad->GetDrillShape(),
283 pad->GetAttribute() != PAD_ATTRIB::NPTH,
284 true, top, bottom );
285
286 auto it = m_drillTypes.begin();
287
288 for( ; it != m_drillTypes.end(); ++it )
289 {
290 if( *it == drill )
291 {
292 it->qty++;
293 break;
294 }
295 }
296
297 if( it == m_drillTypes.end() )
298 {
299 drill.qty = 1;
300 m_drillTypes.push_back( drill );
301 m_gridDrills->InsertRows();
302 }
303 }
304 }
305 }
306
307 // Get via counts
308 for( PCB_TRACK* track : board->Tracks() )
309 {
310 if( track->Type() == PCB_VIA_T )
311 {
312 PCB_VIA* via = static_cast<PCB_VIA*>( track );
313
314 for( LINE_ITEM<VIATYPE>& line : m_viaTypes )
315 {
316 if( via->GetViaType() == line.attribute )
317 {
318 line.qty++;
319 break;
320 }
321 }
322
323 DRILL_LINE_ITEM drill( via->GetDrillValue(), via->GetDrillValue(),
324 PAD_DRILL_SHAPE::CIRCLE, true, false, via->TopLayer(),
325 via->BottomLayer() );
326
327 auto it = m_drillTypes.begin();
328
329 for( ; it != m_drillTypes.end(); ++it )
330 {
331 if( *it == drill )
332 {
333 it->qty++;
334 break;
335 }
336 }
337
338 if( it == m_drillTypes.end() )
339 {
340 drill.qty = 1;
341 m_drillTypes.push_back( drill );
342 m_gridDrills->InsertRows();
343 }
344 }
345 }
346
347 sort( m_drillTypes.begin(), m_drillTypes.end(),
349
350 SHAPE_POLY_SET polySet;
351 m_hasOutline = board->GetBoardPolygonOutlines( polySet );
352
353 if( m_hasOutline )
354 {
355 m_boardArea = 0.0;
356
357 for( int i = 0; i < polySet.OutlineCount(); i++ )
358 {
359 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
360 m_boardArea += outline.Area();
361
362 // If checkbox "subtract holes" is checked
363 if( m_checkBoxSubtractHoles->GetValue() )
364 {
365 for( int j = 0; j < polySet.HoleCount( i ); j++ )
366 m_boardArea -= polySet.Hole( i, j ).Area();
367
368 for( FOOTPRINT* fp : board->Footprints() )
369 {
370 for( PAD* pad : fp->Pads() )
371 {
372 if( !pad->HasHole() )
373 continue;
374
375 auto hole = pad->GetEffectiveHoleShape();
376 const SEG& seg = hole->GetSeg();
377 double width = hole->GetWidth();
378 double area = seg.Length() * width;
379
380 // Each end of the hole is a half-circle, so together, we have one
381 // full circle. The area of a circle is pi * r^2, so the area of the
382 // hole is pi * (d/2)^2 = pi * 1/4 * d^2.
383 area += M_PI * 0.25 * width * width;
384 m_boardArea -= area;
385 }
386 }
387
388 for( PCB_TRACK* track : board->Tracks() )
389 {
390 if( track->Type() == PCB_VIA_T )
391 {
392 PCB_VIA* via = static_cast<PCB_VIA*>( track );
393 double drill = via->GetDrillValue();
394 m_boardArea -= M_PI * 0.25 * drill * drill;
395 }
396 }
397 }
398 }
399
400 // Compute the bounding box to get a rectangular size
401 // We use the polySet bounding box, not the board bounding box, because
402 // we do not want the thickness of graphic items defining the board outlines
403 // to be taken in account to calculate the physical board bbox
404 BOX2I bbox = polySet.BBox();
405
406 m_boardWidth = (int) bbox.GetWidth();
407 m_boardHeight = (int) bbox.GetHeight();
408 }
409
410 board->RunOnChildren(
411 [&]( BOARD_ITEM* child )
412 {
413 if( child->Type() == PCB_FOOTPRINT_T
414 || child->Type() == PCB_GROUP_T
415 || child->Type() == PCB_GENERATOR_T )
416 {
417 // Wait for recursion into children
418 return;
419 }
420
421 if( child->IsOnLayer( F_Cu ) )
422 child->TransformShapeToPolySet( frontCopper, F_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
423
424 if( child->IsOnLayer( B_Cu ) )
425 child->TransformShapeToPolySet( backCopper, B_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
426
427 if( child->Type() == PCB_PAD_T )
428 {
429 PAD* pad = static_cast<PAD*>( child );
430
431 if( pad->HasHole() )
432 {
433 pad->TransformHoleToPolygon( frontHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
434 pad->TransformHoleToPolygon( backHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
435 }
436 }
437 else if( child->Type() == PCB_VIA_T )
438 {
439 PCB_VIA* via = static_cast<PCB_VIA*>( child );
440 VECTOR2I center = via->GetPosition();
441 int R = via->GetDrillValue() / 2;
442
443 if( via->IsOnLayer( F_Cu ) )
445
446 if( via->IsOnLayer( B_Cu ) )
448 }
449 },
450 RECURSE_MODE::RECURSE );
451
452 if( m_checkBoxSubtractHolesFromCopper->GetValue() )
453 {
454 frontCopper.BooleanSubtract( frontHoles );
455 backCopper.BooleanSubtract( backHoles );
456 }
457
458 m_frontCopperArea = frontCopper.Area();
459 m_backCopperArea = backCopper.Area();
460}
461
462
463static wxString formatCount( int aCount )
464{
465 return wxString::Format( wxT( "%i" ), aCount );
466};
467
468
470{
471 int totalPads = 0;
472 int row = 0;
473
474 for( const LINE_ITEM<PAD_ATTRIB>& line : m_padTypes )
475 {
476 m_gridPads->SetCellValue( row, COL_LABEL, line.title );
477 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( line.qty ) );
478 totalPads += line.qty;
479 row++;
480 }
481
482 m_gridPads->SetCellValue( row, COL_LABEL, _( "Total:" ) );
483 m_gridPads->SetCellValue( row, COL_AMOUNT, formatCount( totalPads ) );
484
485 int totalVias = 0;
486 row = 0;
487
488 for( const LINE_ITEM<VIATYPE>& line : m_viaTypes )
489 {
490 m_gridVias->SetCellValue( row, COL_LABEL, line.title );
491 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( line.qty ) );
492 totalVias += line.qty;
493 row++;
494 }
495
496 m_gridVias->SetCellValue( row, COL_LABEL, _( "Total:" ) );
497 m_gridVias->SetCellValue( row, COL_AMOUNT, formatCount( totalVias ) );
498
499
500 int totalFront = 0;
501 int totalBack = 0;
502
503 // We don't use row 0, as there labels are
504 row = 1;
505
506 for( const FP_LINE_ITEM& line : m_fpTypes )
507 {
508 m_gridComponents->SetCellValue( row, COL_LABEL, line.title );
509 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( line.frontSideQty ) );
510 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( line.backSideQty ) );
511 m_gridComponents->SetCellValue( row, 3, formatCount( line.frontSideQty + line.backSideQty ) );
512 totalFront += line.frontSideQty;
513 totalBack += line.backSideQty;
514 row++;
515 }
516
517 m_gridComponents->SetCellValue( row, COL_LABEL, _( "Total:" ) );
518 m_gridComponents->SetCellValue( row, COL_FRONT_SIDE, formatCount( totalFront ) );
519 m_gridComponents->SetCellValue( row, COL_BOTTOM_SIDE, formatCount( totalBack ) );
520 m_gridComponents->SetCellValue( row, COL_TOTAL, formatCount( totalFront + totalBack ) );
521
522 if( m_hasOutline )
523 {
524 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_AMOUNT,
525 wxString::Format( wxT( "%s x %s" ),
528 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT,
529 m_frame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA ) );
530 }
531 else
532 {
533 m_gridBoard->SetCellValue( ROW_BOARD_DIMS, COL_AMOUNT, _( "unknown" ) );
534 m_gridBoard->SetCellValue( ROW_BOARD_AREA, COL_AMOUNT, _( "unknown" ) );
535 }
536
538 m_frame->MessageTextFromValue( m_frontCopperArea, true, EDA_DATA_TYPE::AREA ) );
540 m_frame->MessageTextFromValue( m_backCopperArea, true, EDA_DATA_TYPE::AREA ) );
541
543
544 m_gridComponents->AutoSize();
545 m_gridPads->AutoSize();
546 m_gridBoard->AutoSize();
547 m_gridVias->AutoSize();
548
550}
551
552
554{
555 BOARD* board = m_frame->GetBoard();
556 int row = 0;
557
558 for( const DRILL_LINE_ITEM& line : m_drillTypes )
559 {
560 wxString shapeStr;
561 wxString startLayerStr;
562 wxString stopLayerStr;
563
564 switch( line.shape )
565 {
566 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
567 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
568 default: shapeStr = _( "???" ); break;
569 }
570
571 if( line.startLayer == UNDEFINED_LAYER )
572 startLayerStr = _( "N/A" );
573 else
574 startLayerStr = board->GetLayerName( line.startLayer );
575
576 if( line.stopLayer == UNDEFINED_LAYER )
577 stopLayerStr = _( "N/A" );
578 else
579 stopLayerStr = board->GetLayerName( line.stopLayer );
580
581 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_COUNT, formatCount( line.qty ) );
582 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_SHAPE, shapeStr );
583 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_X_SIZE, m_frame->MessageTextFromValue( line.xSize ) );
584 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_Y_SIZE, m_frame->MessageTextFromValue( line.ySize ) );
585 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_PLATED, line.isPlated ? _( "PTH" ) : _( "NPTH" ) );
586 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_VIA_PAD, line.isPad ? _( "Pad" ) : _( "Via" ) );
587 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_START_LAYER, startLayerStr );
588 m_gridDrills->SetCellValue( row, DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerStr );
589
590 row++;
591 }
592}
593
594
595void DIALOG_BOARD_STATISTICS::printGridToStringAsTable( wxGrid* aGrid, wxString& aStr,
596 bool aUseColLabels,
597 bool aUseFirstColAsLabel )
598{
599 std::vector<int> widths( aGrid->GetNumberCols(), 0 );
600 int rowLabelsWidth = 0;
601
602 // Determine column widths.
603
604 if( aUseColLabels )
605 {
606 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
607 widths[col] = (int) aGrid->GetColLabelValue( col ).length();
608 }
609
610 for( int row = 0; row < aGrid->GetNumberRows(); row++ )
611 {
612 rowLabelsWidth = std::max( rowLabelsWidth, (int) aGrid->GetRowLabelValue( row ).length() );
613
614 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
615 widths[col] = std::max( widths[col], (int) aGrid->GetCellValue( row, col ).length() );
616 }
617
618 // Print the cells.
619
620 wxString tmp;
621
622 // Print column labels.
623
624 aStr << wxT( "|" );
625
626 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
627 {
628 if( aUseColLabels )
629 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetColLabelValue( col ) );
630 else
631 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( 0, col ) );
632
633 aStr << tmp;
634 }
635
636 aStr << wxT( "\n" );
637
638 // Print column label horizontal separators.
639
640 aStr << wxT( "|" );
641
642 for( int col = 0; col < aGrid->GetNumberCols(); col++ )
643 {
644 aStr << wxT( "-" );
645 aStr.Append( '-', widths[col] );
646 aStr << wxT( "-|" );
647 }
648
649 aStr << wxT( "\n" );
650
651 // Print regular cells.
652
653 int firstRow = 0, firstCol = 0;
654
655 if( !aUseColLabels )
656 firstRow = 1;
657
658 if( aUseFirstColAsLabel )
659 firstCol = 1;
660
661 for( int row = firstRow; row < aGrid->GetNumberRows(); row++ )
662 {
663 if( aUseFirstColAsLabel )
664 tmp.Printf( wxT( "|%-*s |" ), widths[0], aGrid->GetCellValue( row, 0 ) );
665 else
666 tmp.Printf( wxT( "|" ) );
667
668 aStr << tmp;
669
670 for( int col = firstCol; col < aGrid->GetNumberCols(); col++ )
671 {
672 tmp.Printf( wxT( " %*s |" ), widths[col], aGrid->GetCellValue( row, col ) );
673 aStr << tmp;
674 }
675
676 aStr << wxT( "\n" );
677 }
678}
679
680
682{
683 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_gridDrills );
684
686
687 double remainingWidth = KIPLATFORM::UI::GetUnobscuredSize( m_gridDrills ).x;
688
689 // Find the total current width
690 for( int i = 0; i < m_gridDrills->GetNumberCols(); i++ )
691 {
693 remainingWidth -= m_gridDrills->GetColSize( i );
694 }
695
696 double scalingFactor = std::max( 1.0,
698 int startLayerColWidth = static_cast<int>( m_startLayerColInitialSize * scalingFactor );
699 int stopLayerColWidth = static_cast<int>( m_stopLayerColInitialSize * scalingFactor );
700
701 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_START_LAYER, startLayerColWidth );
702 m_gridDrills->SetColSize( DRILL_LINE_ITEM::COL_STOP_LAYER, stopLayerColWidth );
703}
704
705
706void DIALOG_BOARD_STATISTICS::checkboxClicked( wxCommandEvent& aEvent )
707{
713 updateWidets();
714 Layout();
715 m_drillsPanel->Layout();
716}
717
718
719void DIALOG_BOARD_STATISTICS::saveReportClicked( wxCommandEvent& aEvent )
720{
721 FILE* outFile;
722 wxString msg;
723 wxString boardName;
724
725 wxFileName fn = m_frame->GetBoard()->GetFileName();
726 boardName = fn.GetName();
727 wxFileDialog dlg( this, _( "Save Report File" ), s_savedDialogState.saveReportFolder,
729 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
730
731 if( dlg.ShowModal() == wxID_CANCEL )
732 return;
733
734 s_savedDialogState.saveReportFolder = wxPathOnly( dlg.GetPath() );
735 s_savedDialogState.saveReportName = dlg.GetFilename();
736
737 outFile = wxFopen( dlg.GetPath(), wxT( "wt" ) );
738
739 if( outFile == nullptr )
740 {
741 msg.Printf( _( "Failed to create file '%s'." ), dlg.GetPath() );
742 DisplayErrorMessage( this, msg );
743 return;
744 }
745
746 msg << _( "PCB statistics report\n=====================" ) << wxT( "\n" );
747 msg << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxT( "\n" );
748 msg << wxS( "- " ) << _( "Project" ) << wxS( ": " )<< Prj().GetProjectName() << wxT( "\n" );
749 msg << wxS( "- " ) << _( "Board name" ) << wxS( ": " )<< boardName << wxT( "\n" );
750
751 msg << wxT( "\n" );
752 msg << _( "Board" ) << wxT( "\n-----\n" );
753
754 if( m_hasOutline )
755 {
756 msg << wxS( "- " ) << _( "Width" ) << wxS( ": " )
757 << m_frame->MessageTextFromValue( m_boardWidth ) << wxT( "\n" );
758 msg << wxS( "- " ) << _( "Height" ) << wxS( ": " )
759 << m_frame->MessageTextFromValue( m_boardHeight ) << wxT( "\n" );
760 msg << wxS( "- " ) << _( "Area" ) + wxS( ": " )
761 << m_frame->MessageTextFromValue( m_boardArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
762 }
763 else
764 {
765 msg << wxS( "- " ) << _( "Dimensions" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
766 msg << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxT( "\n" );
767 }
768
769 msg << wxS( "- " ) << _( "Front copper area" ) + wxS( ": " )
770 << m_frame->MessageTextFromValue( m_frontCopperArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
771 msg << wxS( "- " ) << _( "Back copper area" ) + wxS( ": " )
772 << m_frame->MessageTextFromValue( m_backCopperArea, true, EDA_DATA_TYPE::AREA ) << wxT( "\n" );
773
774 msg << wxT( "\n" );
775 msg << _( "Pads" ) << wxT( "\n----\n" );
776
777 for( auto& type : m_padTypes )
778 msg << wxT( "- " ) << type.title << wxS( " " ) << type.qty << wxT( "\n" );
779
780 msg << wxT( "\n" );
781 msg << _( "Vias" ) << wxT( "\n----\n" );
782
783 for( auto& type : m_viaTypes )
784 msg << wxT( "- " ) << type.title << wxS( " " ) << type.qty << wxT( "\n" );
785
786 // We will save data about components in the table.
787 // We have to calculate column widths
788 std::vector<int> widths;
789 std::vector<wxString> labels{ wxT( "" ), _( "Front Side" ), _( "Back Side" ), _( "Total" ) };
790 wxString tmp;
791
792 widths.reserve( labels.size() );
793
794 for( const wxString& label : labels )
795 widths.push_back( (int) label.size() );
796
797 int frontTotal = 0;
798 int backTotal = 0;
799
800 for( const FP_LINE_ITEM& line : m_fpTypes )
801 {
802 // Get maximum width for left label column
803 widths[0] = std::max( (int) line.title.size(), widths[0] );
804 frontTotal += line.frontSideQty;
805 backTotal += line.backSideQty;
806 }
807
808 // Get maximum width for other columns
809 tmp.Printf( wxT( "%i" ), frontTotal );
810 widths[1] = std::max( (int) tmp.size(), widths[1] );
811 tmp.Printf( wxT( "%i" ), backTotal );
812 widths[2] = std::max( (int) tmp.size(), widths[2] );
813 tmp.Printf( wxT( "%i" ), frontTotal + backTotal );
814 widths[3] = std::max( (int) tmp.size(), widths[3] );
815
816 //Write components amount to file
817 msg << wxT( "\n" );
818 msg << _( "Components" ) << wxT( "\n----------\n" );
819 msg << wxT( "\n" );
820
821 printGridToStringAsTable( m_gridComponents, msg, false, true );
822
823 msg << wxT( "\n" );
824 msg << _( "Drill holes" ) << wxT( "\n-----------\n" );
825 msg << wxT( "\n" );
826
827 printGridToStringAsTable( m_gridDrills, msg, true, false );
828
829 if( fprintf( outFile, "%s", TO_UTF8( msg ) ) < 0 )
830 {
831 msg.Printf( _( "Error writing file '%s'." ), dlg.GetPath() );
832 DisplayErrorMessage( this, msg );
833 }
834
835 fclose( outFile );
836}
837
838
839void DIALOG_BOARD_STATISTICS::drillGridSize( wxSizeEvent& aEvent )
840{
841 aEvent.Skip();
843}
844
845void DIALOG_BOARD_STATISTICS::drillGridSort( wxGridEvent& aEvent )
846{
847 DRILL_LINE_ITEM::COL_ID colId = static_cast<DRILL_LINE_ITEM::COL_ID>( aEvent.GetCol() );
848 bool ascending = !( m_gridDrills->IsSortingBy( colId )
849 && m_gridDrills->IsSortOrderAscending() );
850
851 sort( m_drillTypes.begin(), m_drillTypes.end(), DRILL_LINE_ITEM::COMPARE( colId, ascending ) );
852
854}
855
856
858{
859}
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_LOW_DEF
Definition: base_units.h:128
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc) const
Convert the item shape to a polyset.
Definition: board_item.h:426
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:317
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
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:2524
const FOOTPRINTS & Footprints() const
Definition: board.h:358
const TRACKS & Tracks() const
Definition: board.h:356
const wxString & GetFileName() const
Definition: board.h:354
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:614
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Definition: board.cpp:525
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr size_type GetHeight() const
Definition: box2.h:215
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...
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:109
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Definition: pad.h:54
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:142
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition: project.cpp:160
Definition: seg.h:42
int Length() const
Return the length (this).
Definition: seg.h:333
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.
Represent a set of closed polygons.
double Area()
Return the area of this poly set.
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.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
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:816
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:203
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:203
This file is part of the common library.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
static DIALOG_BOARD_STATISTICS_SAVED_STATE s_savedDialogState
#define ROW_BOARD_AREA
#define COL_AMOUNT
#define ROW_LABEL
#define COL_BOTTOM_SIDE
#define ROW_BACK_COPPER_AREA
#define COL_LABEL
#define COL_TOTAL
#define ROW_FRONT_COPPER_AREA
#define ROW_BOARD_DIMS
static wxString formatCount(int aCount)
#define COL_FRONT_SIDE
#define _(s)
@ FP_SMD
Definition: footprint.h:81
@ FP_THROUGH_HOLE
Definition: footprint.h:80
static wxString TextFileWildcard()
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Cu
Definition: layer_ids.h:65
@ 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: wxgtk/ui.cpp:252
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
Definition: ui_common.cpp:132
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:162
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:429
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.
VECTOR2I center
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition: typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
Definition of file extensions used in Kicad.