Cppcheck
statsdialog.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "statsdialog.h"
20 
21 #include "checkstatistics.h"
22 #include "projectfile.h"
23 #include "showtypes.h"
24 
25 #include "ui_statsdialog.h"
26 
27 #include <QApplication>
28 #include <QClipboard>
29 #include <QDate>
30 #include <QFileDialog>
31 #include <QFileInfo>
32 #include <QFont>
33 #include <QLabel>
34 #include <QLineEdit>
35 #include <QMimeData>
36 #include <QPageSize>
37 #include <QPlainTextEdit>
38 #include <QPrinter>
39 #include <QPushButton>
40 #include <QStringList>
41 #include <QTextDocument>
42 #include <QWidget>
43 #include <Qt>
44 
45 #ifdef QT_CHARTS_LIB
46 #include "common.h"
47 
48 #include <QAbstractSeries>
49 #include <QChartView>
50 #include <QDateTime>
51 #include <QDateTimeAxis>
52 #include <QDir>
53 #include <QFile>
54 #include <QIODevice>
55 #include <QLayout>
56 #include <QLineSeries>
57 #include <QList>
58 #include <QPainter>
59 #include <QPointF>
60 #include <QRegularExpression>
61 #include <QTextStream>
62 #include <QValueAxis>
63 
64 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
65 QT_CHARTS_USE_NAMESPACE
66 #endif
67 
68 static QLineSeries *numberOfReports(const QString &fileName, const QString &severity);
69 static QChartView *createChart(const QString &statsFile, const QString &tool);
70 #endif
71 
72 static const QString CPPCHECK("cppcheck");
73 
74 StatsDialog::StatsDialog(QWidget *parent)
75  : QDialog(parent),
76  mUI(new Ui::StatsDialog)
77 {
78  mUI->setupUi(this);
79 
80  QFont font("courier");
81  font.setStyleHint(QFont::Monospace);
82  mUI->mCheckersReport->setFont(font);
83 
84  setWindowFlags(Qt::Window);
85 
86  connect(mUI->mCopyToClipboard, &QPushButton::pressed, this, &StatsDialog::copyToClipboard);
87  connect(mUI->mPDFexport, &QPushButton::pressed, this, &StatsDialog::pdfExport);
88 }
89 
91 {
92  delete mUI;
93 }
94 
95 void StatsDialog::setProject(const ProjectFile* projectFile)
96 {
97  if (projectFile) {
98  mUI->mProject->setText(projectFile->getRootPath());
99  mUI->mPaths->setText(projectFile->getCheckPaths().join(";"));
100  mUI->mIncludePaths->setText(projectFile->getIncludeDirs().join(";"));
101  mUI->mDefines->setText(projectFile->getDefines().join(";"));
102  mUI->mUndefines->setText(projectFile->getUndefines().join(";"));
103 #ifndef QT_CHARTS_LIB
104  mUI->mTabHistory->setVisible(false);
105 #else
106  QString statsFile;
107  if (!projectFile->getBuildDir().isEmpty()) {
108  const QString prjpath = QFileInfo(projectFile->getFilename()).absolutePath();
109  const QString buildDir = prjpath + '/' + projectFile->getBuildDir();
110  if (QDir(buildDir).exists()) {
111  statsFile = buildDir + "/statistics.txt";
112  }
113  }
114  mUI->mLblHistoryFile->setText(tr("File: ") + (statsFile.isEmpty() ? tr("No cppcheck build dir") : statsFile));
115  if (!statsFile.isEmpty()) {
116  QChartView *chartView = createChart(statsFile, "cppcheck");
117  mUI->mTabHistory->layout()->addWidget(chartView);
118  if (projectFile->getClangAnalyzer()) {
119  chartView = createChart(statsFile, CLANG_ANALYZER);
120  mUI->mTabHistory->layout()->addWidget(chartView);
121  }
122  if (projectFile->getClangTidy()) {
123  chartView = createChart(statsFile, CLANG_TIDY);
124  mUI->mTabHistory->layout()->addWidget(chartView);
125  }
126  }
127 #endif
128  } else {
129  mUI->mProject->setText(QString());
130  mUI->mPaths->setText(QString());
131  mUI->mIncludePaths->setText(QString());
132  mUI->mDefines->setText(QString());
133  mUI->mUndefines->setText(QString());
134  }
135 }
136 
137 void StatsDialog::setPathSelected(const QString& path)
138 {
139  mUI->mPath->setText(path);
140 }
141 
143 {
144  mUI->mNumberOfFilesScanned->setText(QString::number(num));
145 }
146 
147 void StatsDialog::setScanDuration(double seconds)
148 {
149  // Factor the duration into units (days/hours/minutes/seconds)
150  int secs = seconds;
151  const int days = secs / (24 * 60 * 60);
152  secs -= days * (24 * 60 * 60);
153  const int hours = secs / (60 * 60);
154  secs -= hours * (60 * 60);
155  const int mins = secs / 60;
156  secs -= mins * 60;
157 
158  // Concatenate the two most significant units (e.g. "1 day and 3 hours")
159  QStringList parts;
160  if (days)
161  parts << ((days == 1) ? tr("1 day") : tr("%1 days").arg(days));
162  if (hours)
163  parts << ((hours == 1) ? tr("1 hour") : tr("%1 hours").arg(hours));
164  if (mins && parts.size() < 2)
165  parts << ((mins == 1) ? tr("1 minute") : tr("%1 minutes").arg(mins));
166  if (secs && parts.size() < 2)
167  parts << ((secs == 1) ? tr("1 second") : tr("%1 seconds").arg(secs));
168 
169  // For durations < 1s, show the fraction of a second (e.g. "0.7 seconds")
170  if (parts.isEmpty())
171  parts << tr("0.%1 seconds").arg(int(10.0 *(seconds - secs)));
172 
173  mUI->mScanDuration->setText(parts.join(tr(" and ")));
174 }
176 {
177  const QString Stat = QString(
178  "<center><h1>%1 %2</h1></center>\n"
179  "<font color=\"red\"><h3>%3 : %4</h3></font>\n"
180  "<font color=\"green\"><h3>%5 : %6</h3></font>\n"
181  "<font color=\"orange\"><h3>%7 : %8</h3></font>\n"
182  "<font color=\"blue\"><h3>%9 : %10</h3></font>\n"
183  "<font color=\"blue\"><h3>%11 : %12</h3></font>\n"
184  "<font color=\"purple\"><h3>%13 : %14</h3></font>\n")
185  .arg(tr("Statistics"))
186  .arg(QDate::currentDate().toString("dd.MM.yyyy"))
187  .arg(tr("Errors"))
189  .arg(tr("Warnings"))
191  .arg(tr("Style warnings"))
193  .arg(tr("Portability warnings"))
195  .arg(tr("Performance warnings"))
197  .arg(tr("Information messages"))
199 
200  QString fileName = QFileDialog::getSaveFileName((QWidget*)nullptr, tr("Export PDF"), QString(), "*.pdf");
201  if (QFileInfo(fileName).suffix().isEmpty()) {
202  fileName.append(".pdf");
203  }
204  QPrinter printer(QPrinter::PrinterResolution);
205  printer.setOutputFormat(QPrinter::PdfFormat);
206  printer.setPageSize(QPageSize(QPageSize::A4));
207  printer.setOutputFileName(fileName);
208 
209  QTextDocument doc;
210  doc.setHtml(Stat);
211  // doc.setPageSize(printer.pageRect().size());
212  doc.print(&printer);
213 
214 }
215 
217 {
218  QClipboard *clipboard = QApplication::clipboard();
219  if (!clipboard)
220  return;
221 
222  const QString projSettings(tr("Project Settings"));
223  const QString project(tr("Project"));
224  const QString paths(tr("Paths"));
225  const QString incPaths(tr("Include paths"));
226  const QString defines(tr("Defines"));
227  const QString undefines(tr("Undefines"));
228  const QString prevScan(tr("Previous Scan"));
229  const QString selPath(tr("Path selected"));
230  const QString numFiles(tr("Number of files scanned"));
231  const QString duration(tr("Scan duration"));
232  const QString stats(tr("Statistics"));
233  const QString errors(tr("Errors"));
234  const QString warnings(tr("Warnings"));
235  const QString style(tr("Style warnings"));
236  const QString portability(tr("Portability warnings"));
237  const QString performance(tr("Performance warnings"));
238  const QString information(tr("Information messages"));
239 
240  // Plain text summary
241  const QString settings = QString(
242  "%1\n"
243  "\t%2:\t%3\n"
244  "\t%4:\t%5\n"
245  "\t%6:\t%7\n"
246  "\t%8:\t%9\n"
247  "\t%10:\t%11\n"
248  )
249  .arg(projSettings)
250  .arg(project)
251  .arg(mUI->mProject->text())
252  .arg(paths)
253  .arg(mUI->mPaths->text())
254  .arg(incPaths)
255  .arg(mUI->mIncludePaths->text())
256  .arg(defines)
257  .arg(mUI->mDefines->text())
258  .arg(undefines)
259  .arg(mUI->mUndefines->text());
260 
261  const QString previous = QString(
262  "%1\n"
263  "\t%2:\t%3\n"
264  "\t%4:\t%5\n"
265  "\t%6:\t%7\n"
266  )
267  .arg(prevScan)
268  .arg(selPath)
269  .arg(mUI->mPath->text())
270  .arg(numFiles)
271  .arg(mUI->mNumberOfFilesScanned->text())
272  .arg(duration)
273  .arg(mUI->mScanDuration->text());
274 
275  const QString statistics = QString(
276  "%1\n"
277  "\t%2:\t%3\n"
278  "\t%4:\t%5\n"
279  "\t%6:\t%7\n"
280  "\t%8:\t%9\n"
281  "\t%10:\t%11\n"
282  "\t%12:\t%13\n"
283  )
284  .arg(stats)
285  .arg(errors)
287  .arg(warnings)
289  .arg(style)
291  .arg(portability)
293  .arg(performance)
295  .arg(information)
297 
298  const QString textSummary = settings + previous + statistics;
299 
300  // HTML summary
301  const QString htmlSettings = QString(
302  "<h3>%1<h3>\n"
303  "<table>\n"
304  " <tr><th>%2:</th><td>%3</td></tr>\n"
305  " <tr><th>%4:</th><td>%5</td></tr>\n"
306  " <tr><th>%6:</th><td>%7</td></tr>\n"
307  " <tr><th>%8:</th><td>%9</td></tr>\n"
308  " <tr><th>%10:</th><td>%11</td></tr>\n"
309  "</table>\n"
310  )
311  .arg(projSettings)
312  .arg(project)
313  .arg(mUI->mProject->text())
314  .arg(paths)
315  .arg(mUI->mPaths->text())
316  .arg(incPaths)
317  .arg(mUI->mIncludePaths->text())
318  .arg(defines)
319  .arg(mUI->mDefines->text())
320  .arg(undefines)
321  .arg(mUI->mUndefines->text());
322 
323  const QString htmlPrevious = QString(
324  "<h3>%1</h3>\n"
325  "<table>\n"
326  " <tr><th>%2:</th><td>%3</td></tr>\n"
327  " <tr><th>%4:</th><td>%5</td></tr>\n"
328  " <tr><th>%6:</th><td>%7</td></tr>\n"
329  "</table>\n"
330  )
331  .arg(prevScan)
332  .arg(selPath)
333  .arg(mUI->mPath->text())
334  .arg(numFiles)
335  .arg(mUI->mNumberOfFilesScanned->text())
336  .arg(duration)
337  .arg(mUI->mScanDuration->text());
338 
339  const QString htmlStatistics = QString(
340  "<h3>%1</h3>\n"
341  " <tr><th>%2:</th><td>%3</td></tr>\n"
342  " <tr><th>%4:</th><td>%5</td></tr>\n"
343  " <tr><th>%6:</th><td>%7</td></tr>\n"
344  " <tr><th>%8:</th><td>%9</td></tr>\n"
345  " <tr><th>%10:</th><td>%11</td></tr>\n"
346  " <tr><th>%12:</th><td>%13</td></tr>\n"
347  "</table>\n"
348  )
349  .arg(stats)
350  .arg(errors)
352  .arg(warnings)
354  .arg(style)
356  .arg(portability)
358  .arg(performance)
360  .arg(information)
362 
363  const QString htmlSummary = htmlSettings + htmlPrevious + htmlStatistics;
364 
365  auto *mimeData = new QMimeData();
366  mimeData->setText(textSummary);
367  mimeData->setHtml(htmlSummary);
368  clipboard->setMimeData(mimeData);
369 }
370 
372 {
373  mStatistics = stats;
374  mUI->mLblErrors->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowErrors)));
375  mUI->mLblWarnings->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings)));
376  mUI->mLblStyle->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowStyle)));
377  mUI->mLblPortability->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPortability)));
378  mUI->mLblPerformance->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance)));
379  mUI->mLblInformation->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowInformation)));
380  mUI->mLblActiveCheckers->setText(QString::number(stats->getNumberOfActiveCheckers()));
381  mUI->mCheckersReport->setPlainText(stats->getCheckersReport());
382 }
383 
384 #ifdef QT_CHARTS_LIB
385 QChartView *createChart(const QString &statsFile, const QString &tool)
386 {
387  auto *chart = new QChart;
388  chart->addSeries(numberOfReports(statsFile, tool + "-error"));
389  chart->addSeries(numberOfReports(statsFile, tool + "-warning"));
390  chart->addSeries(numberOfReports(statsFile, tool + "-style"));
391  chart->addSeries(numberOfReports(statsFile, tool + "-performance"));
392  chart->addSeries(numberOfReports(statsFile, tool + "-portability"));
393 
394  auto *axisX = new QDateTimeAxis;
395  axisX->setTitleText("Date");
396  chart->addAxis(axisX, Qt::AlignBottom);
397 
398  for (QAbstractSeries *s : chart->series()) {
399  s->attachAxis(axisX);
400  }
401 
402  auto *axisY = new QValueAxis;
403  axisY->setLabelFormat("%i");
404  axisY->setTitleText("Count");
405  chart->addAxis(axisY, Qt::AlignLeft);
406 
407  qreal maxY = 0;
408  for (QAbstractSeries *s : chart->series()) {
409  s->attachAxis(axisY);
410  if (const auto *ls = dynamic_cast<const QLineSeries*>(s)) {
411  for (QPointF p : ls->points()) {
412  if (p.y() > maxY)
413  maxY = p.y();
414  }
415  }
416  }
417  axisY->setMax(maxY);
418 
419  //chart->createDefaultAxes();
420  chart->setTitle(tool);
421 
422  auto *chartView = new QChartView(chart);
423  chartView->setRenderHint(QPainter::Antialiasing);
424  return chartView;
425 }
426 
427 QLineSeries *numberOfReports(const QString &fileName, const QString &severity)
428 {
429  auto *series = new QLineSeries();
430  series->setName(severity);
431  QFile f(fileName);
432  if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
433  quint64 t = 0;
434  QTextStream in(&f);
435  while (!in.atEnd()) {
436  QString line = in.readLine();
437  static const QRegularExpression rxdate("^\\[(\\d\\d)\\.(\\d\\d)\\.(\\d\\d\\d\\d)\\]$");
438  const QRegularExpressionMatch matchRes = rxdate.match(line);
439  if (matchRes.hasMatch()) {
440  const int y = matchRes.captured(3).toInt();
441  const int m = matchRes.captured(2).toInt();
442  const int d = matchRes.captured(1).toInt();
443  QDateTime dt;
444  dt.setDate(QDate(y,m,d));
445  if (t == dt.toMSecsSinceEpoch())
446  t += 1000;
447  else
448  t = dt.toMSecsSinceEpoch();
449  }
450  if (line.startsWith(severity + ':')) {
451  const int y = line.mid(1+severity.length()).toInt();
452  series->append(t, y);
453  }
454  }
455  }
456  return series;
457 }
458 #endif
A class for check statistics.
const QString & getCheckersReport() const
unsigned getCount(const QString &tool, ShowTypes::ShowType type) const
Return statistics for given type.
int getNumberOfActiveCheckers() const
A class that reads and writes project files.
Definition: projectfile.h:46
const QString & getFilename() const
Get filename for the project file.
Definition: projectfile.h:257
const QString & getRootPath() const
Get project root path.
Definition: projectfile.h:78
bool getClangAnalyzer() const
Definition: projectfile.h:217
const QStringList & getDefines() const
Get list of defines.
Definition: projectfile.h:122
QStringList getCheckPaths() const
Get list of paths to check.
Definition: projectfile.h:138
bool getClangTidy() const
Definition: projectfile.h:225
const QStringList & getUndefines() const
Get list of undefines.
Definition: projectfile.h:130
QStringList getIncludeDirs() const
Get list of include directories.
Definition: projectfile.h:114
const QString & getBuildDir() const
Definition: projectfile.h:82
@ ShowPerformance
Definition: showtypes.h:47
@ ShowInformation
Definition: showtypes.h:49
@ ShowPortability
Definition: showtypes.h:48
@ ShowErrors
Definition: showtypes.h:50
@ ShowWarnings
Definition: showtypes.h:46
@ ShowStyle
Definition: showtypes.h:45
A dialog that shows project and scan statistics.
Definition: statsdialog.h:40
void setScanDuration(double seconds)
Sets the number of seconds to display beside "Scan Duration:".
~StatsDialog() override
Definition: statsdialog.cpp:90
void setNumberOfFilesScanned(int num)
Sets the number to display beside "Number of Files Scanned:".
void pdfExport()
Ui::StatsDialog * mUI
Definition: statsdialog.h:75
void copyToClipboard()
void setProject(const ProjectFile *projectFile)
Sets the project to extract statistics from.
Definition: statsdialog.cpp:95
void setStatistics(const CheckStatistics *stats)
Sets the numbers of different error/warnings found.
void setPathSelected(const QString &path)
Sets the string to display beside "Path Selected:".
const CheckStatistics * mStatistics
Definition: statsdialog.h:76
StatsDialog(QWidget *parent=nullptr)
Definition: statsdialog.cpp:74
std::string toString(Color c)
Definition: color.cpp:54
@ portability
Portability warning.
@ style
Style warning.
@ information
Checking information.
@ performance
Performance warning.
#define CLANG_TIDY
Definition: common.h:29
#define CLANG_ANALYZER
Definition: common.h:28
Definition: aboutdialog.h:27
static const QString CPPCHECK("cppcheck")