Skip to content

Commit feaf326

Browse files
committed
Project files.
1 parent 8859a7d commit feaf326

File tree

9 files changed

+484
-0
lines changed

9 files changed

+484
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@
3030
*.exe
3131
*.out
3232
*.app
33+
34+
build

CMakeLists.txt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
3+
project(CallsCounter VERSION 0.1 LANGUAGES CXX)
4+
5+
set(CMAKE_AUTOUIC ON)
6+
set(CMAKE_AUTOMOC ON)
7+
set(CMAKE_AUTORCC ON)
8+
9+
set(CMAKE_CXX_STANDARD 17)
10+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
11+
12+
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Sql)
13+
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Sql)
14+
15+
set(PROJECT_SOURCES
16+
main.cpp
17+
mainwindow.cpp
18+
mainwindow.hpp
19+
mainwindow.ui
20+
database.hpp
21+
database.cpp
22+
)
23+
24+
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
25+
qt_add_executable(CallsCounter
26+
MANUAL_FINALIZATION
27+
${PROJECT_SOURCES}
28+
)
29+
# Define target properties for Android with Qt 6 as:
30+
# set_property(TARGET CallsCounter APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
31+
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
32+
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
33+
else()
34+
if(ANDROID)
35+
add_library(CallsCounter SHARED
36+
${PROJECT_SOURCES}
37+
)
38+
# Define properties for Android with Qt 5 after find_package() calls as:
39+
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
40+
else()
41+
add_executable(CallsCounter
42+
${PROJECT_SOURCES}
43+
)
44+
endif()
45+
endif()
46+
47+
target_link_libraries(CallsCounter PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Sql)
48+
49+
set_target_properties(CallsCounter PROPERTIES
50+
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
51+
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
52+
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
53+
MACOSX_BUNDLE TRUE
54+
WIN32_EXECUTABLE TRUE
55+
)
56+
57+
install(TARGETS CallsCounter
58+
BUNDLE DESTINATION .
59+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
60+
61+
if(QT_VERSION_MAJOR EQUAL 6)
62+
qt_finalize_executable(CallsCounter)
63+
endif()

assets/icon.ico

105 KB
Binary file not shown.

database.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include "database.hpp"
2+
3+
Database::Database(QObject *parent)
4+
: QObject{parent}
5+
{
6+
m_db = QSqlDatabase::addDatabase("QSQLITE");
7+
m_query = QSqlQuery(m_db);
8+
9+
setDBEnvironmentUp();
10+
createDB();
11+
}
12+
13+
Database::~Database()
14+
{
15+
if (m_db.isOpen())
16+
m_db.close();
17+
}
18+
19+
void Database::setDBEnvironmentUp()
20+
{
21+
auto parentDir = QString("%1%2%3%4%5").arg(QDir::homePath(), QDir::separator(),
22+
"Documents", QDir::separator(), "CallsCounter");
23+
auto fullPath = QString("%1%2%3").arg(parentDir, QDir::separator(), "calls.sqlite3");
24+
QDir dir(parentDir);
25+
if (not dir.exists(parentDir)) {
26+
dir.mkdir(parentDir);
27+
}
28+
29+
m_db.setDatabaseName(fullPath);
30+
}
31+
32+
void Database::createDB()
33+
{
34+
if (not m_db.open()) {
35+
emit error("Database couldn't be opened, thus couldn't be created.");
36+
return;
37+
}
38+
39+
QString statement = "CREATE TABLE IF NOT EXISTS Calls (\
40+
id INTEGER PRIMARY KEY AUTOINCREMENT,\
41+
calls INTERGER NOT NULL,\
42+
date DATETIME NOT NULL UNIQUE,\
43+
username TEXT NOT NULL)";
44+
45+
if (not m_query.exec(statement)) {
46+
emit error("Database couldn't be created.");
47+
emit error(m_query.lastError().text());
48+
}
49+
50+
m_db.close();
51+
}
52+
53+
bool Database::open()
54+
{
55+
bool result;
56+
if (not (result = m_db.open())) {
57+
emit error("Database couldn't be opened.");
58+
}
59+
return result;
60+
}
61+
62+
void Database::close()
63+
{
64+
m_db.close();
65+
}
66+
67+
bool Database::exec(const QString& statement)
68+
{
69+
bool result;
70+
if (not (result = m_query.exec(statement))) {
71+
emit error("Statement couldn't be executed.");
72+
emit error(m_query.lastError().text());
73+
}
74+
75+
return result;
76+
}
77+
78+
int Database::records() const
79+
{
80+
return m_query.size();
81+
}
82+
83+
QSqlRecord Database::record()
84+
{
85+
m_query.first();
86+
return m_query.record();
87+
}
88+
89+
QVariant Database::value(int index) const
90+
{
91+
return m_query.value(index);
92+
}

database.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef DATABASE_H
2+
#define DATABASE_H
3+
4+
#include <QDir>
5+
#include <QSqlDatabase>
6+
#include <QSqlError>
7+
#include <QSqlRecord>
8+
#include <QSqlQuery>
9+
#include <QObject>
10+
11+
class Database : public QObject
12+
{
13+
Q_OBJECT
14+
QSqlDatabase m_db;
15+
QSqlQuery m_query;
16+
17+
void setDBEnvironmentUp();
18+
void createDB();
19+
public:
20+
explicit Database(QObject *parent = nullptr);
21+
~Database();
22+
bool open();
23+
void close();
24+
bool exec(const QString& statement);
25+
int records() const;
26+
QSqlRecord record();
27+
QVariant value(int index) const;
28+
signals:
29+
void error(QString message);
30+
};
31+
32+
#endif // DATABASE_H

main.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <QApplication>
2+
#include <QIcon>
3+
4+
#include "mainwindow.hpp"
5+
6+
int main(int argc, char *argv[])
7+
{
8+
QApplication a(argc, argv);
9+
MainWindow w;
10+
w.setWindowIcon(QIcon("assets/icon.ico"));
11+
w.show();
12+
return a.exec();
13+
}

mainwindow.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include "mainwindow.hpp"
2+
#include "./ui_mainwindow.h"
3+
4+
MainWindow::MainWindow(QWidget *parent)
5+
: QMainWindow(parent)
6+
, m_ui(new Ui::MainWindow)
7+
, m_calls(0)
8+
, m_db()
9+
{
10+
m_ui->setupUi(this);
11+
12+
connect(&m_db, &Database::error, this, &MainWindow::error);
13+
connect(m_ui->newButton, &QPushButton::clicked, this, &MainWindow::newCall);
14+
connect(m_ui->saveButton, &QPushButton::clicked, this, &MainWindow::saveCalls);
15+
connect(&m_timer, &QTimer::timeout, this, &MainWindow::setDateTime);
16+
17+
m_username = qgetenv("USER");
18+
if (m_username.isEmpty())
19+
m_username = qgetenv("USERNAME");
20+
m_ui->usernameLabel->setText("Usuario: " + m_username);
21+
22+
setDateTime();
23+
setTodaysCalls();
24+
25+
m_timer.setInterval(1'000);
26+
m_timer.start();
27+
}
28+
29+
MainWindow::~MainWindow()
30+
{
31+
delete m_ui;
32+
}
33+
34+
void MainWindow::setLabel()
35+
{
36+
m_ui->messageLabel->setText(QString("Hoy has registrado %1 %2.")
37+
.arg(QString::number(m_calls), m_calls == 1 ? "llamada" : "llamadas"));
38+
}
39+
40+
void MainWindow::setDateTime()
41+
{
42+
m_datetime = QDateTime::currentDateTime().toString("dd-MM-yyyy");
43+
m_ui->datetimeLabel->setText(QString("Fecha: %1").arg(m_datetime));
44+
}
45+
46+
void MainWindow::setTodaysCalls()
47+
{
48+
if (not m_db.open())
49+
return;
50+
auto statement = QString("SELECT id, calls FROM Calls WHERE date='%1' ORDER BY id DESC LIMIT 1").arg(m_datetime);
51+
if (not m_db.exec(statement)) {
52+
m_db.close();
53+
return;
54+
}
55+
56+
auto record { m_db.record() };
57+
int index { record.indexOf("calls") };
58+
m_calls = m_db.value(index).toInt();
59+
if (m_calls == -1)
60+
m_calls = 0;
61+
62+
setLabel();
63+
m_db.close();
64+
}
65+
66+
void MainWindow::error(const QString& message)
67+
{
68+
QMessageBox::critical(this, "Error", message);
69+
}
70+
71+
bool MainWindow::shouldUpdate()
72+
{
73+
auto statement = QString("SELECT COUNT(id) AS fields FROM Calls WHERE username='%1' AND date='%2'")
74+
.arg(m_username, m_datetime);
75+
if (not m_db.exec(statement))
76+
return false;
77+
auto record { m_db.record() };
78+
int index { record.indexOf("fields") };
79+
int fields { m_db.value(index).toInt() };
80+
if (fields == 0)
81+
return false;
82+
return true;
83+
}
84+
85+
void MainWindow::newCall()
86+
{
87+
++m_calls;
88+
setLabel();
89+
}
90+
91+
void MainWindow::saveCalls()
92+
{
93+
if (not m_db.open())
94+
return;
95+
96+
bool update = shouldUpdate();
97+
QString statement;
98+
99+
if (update) {
100+
// I prefer to use IDs in the WHERE clause, but we have no problem since datetime cannot be duplicated.
101+
statement = QString("UPDATE Calls SET calls='%1' WHERE username='%2' AND date='%3'")
102+
.arg(QString::number(m_calls), m_username, m_datetime);
103+
} else {
104+
statement = QString("INSERT INTO Calls (calls, date, username) VALUES ('%1', '%2', '%3')")
105+
.arg(QString::number(m_calls), m_datetime, m_username);
106+
}
107+
108+
if (not m_db.exec(statement)) {
109+
m_db.close();
110+
return;
111+
}
112+
113+
m_db.close();
114+
115+
QMessageBox::information(this, "Information", "Calls have been saved.");
116+
}

mainwindow.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#ifndef MAINWINDOW_H
2+
#define MAINWINDOW_H
3+
4+
#include <QDateTime>
5+
#include <QDir>
6+
#include <QMainWindow>
7+
#include <QMessageBox>
8+
#include <QSqlRecord>
9+
#include <QTimer>
10+
11+
#include "database.hpp"
12+
13+
QT_BEGIN_NAMESPACE
14+
namespace Ui { class MainWindow; }
15+
QT_END_NAMESPACE
16+
17+
class MainWindow : public QMainWindow
18+
{
19+
Q_OBJECT
20+
Ui::MainWindow *m_ui;
21+
int m_calls;
22+
QString m_datetime;
23+
QString m_username;
24+
QTimer m_timer; // To set datetime.
25+
Database m_db;
26+
27+
void setLabel();
28+
void setDateTime();
29+
void setTodaysCalls();
30+
bool shouldUpdate();
31+
public:
32+
MainWindow(QWidget* parent = nullptr);
33+
~MainWindow();
34+
private slots:
35+
void error(const QString& message);
36+
void newCall();
37+
void saveCalls();
38+
};
39+
40+
#endif // MAINWINDOW_H

0 commit comments

Comments
 (0)