Compare commits

..

10 commits

12 changed files with 26 additions and 217 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.28) cmake_minimum_required(VERSION 3.28)
project(itat_challenge_olympics) project(itat_challange_olympics)
find_package(Qt6 6.2 COMPONENTS Core Quick Quick REQUIRED) find_package(Qt6 6.2 COMPONENTS Core Quick Quick REQUIRED)
@ -8,12 +8,12 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
qt_add_executable(itat_challenge_olympics src/main/main.cpp qt_add_executable(itat_challange_olympics src/main/main.cpp
application.qrc application.qrc
) )
qt_add_qml_module(itat_challenge_olympics qt_add_qml_module(itat_challange_olympics
URI itat URI itat
QML_FILES QML_FILES
res/gui/application.qml res/gui/application.qml
@ -25,8 +25,6 @@ qt_add_qml_module(itat_challenge_olympics
src/model/Competitor.h src/model/Competitor.h
src/model/EventInfo.cpp src/model/EventInfo.cpp
src/model/EventInfo.h src/model/EventInfo.h
src/model/FilterModel.cpp
src/model/FilterModel.h
src/model/SportModel.cpp src/model/SportModel.cpp
src/model/SportModel.h src/model/SportModel.h
@ -82,6 +80,6 @@ qt_add_qml_module(itat_challenge_olympics
res/pictograms/WRG_small.svg res/pictograms/WRG_small.svg
) )
target_link_libraries(itat_challenge_olympics PRIVATE Qt6::Core Qt6::Quick Qt6::Network) target_link_libraries(itat_challange_olympics PRIVATE Qt6::Core Qt6::Quick Qt6::Network)
# target_link_libraries(itat_challenge_olympics PRIVATE d3d12.lib dxgi.lib d3dcompiler.lib dxguid.lib) # target_link_libraries(itat_challange_olympics PRIVATE d3d12.lib dxgi.lib d3dcompiler.lib dxguid.lib)

170
README.md
View file

@ -1,170 +0,0 @@
# Olympia 2024 Events
> View updated Events with its Competitors and Rankings of all Disciplines
*Olympia 2024 Events* always displays up to date information of the 2024
Olympics in Paris. It achieves this by fetching the `olympics.com` API.
We use the Model-View-Delegate pattern to synchronize the API data in C++ with
the UI-Widgets defined in QML. For this we implement the API data as a Model
which can be seen and interacted with by QML Components.
All code, qml definitions and images, etc are compiled into a single binary that
is not dependent on any resources on relative paths anymore.
On startup the default discipline *Archery* will be fetched and shown on the
EventsPage. From here the user has three options. You can change the discipline
from the Dropdown-Menu (Combobox) in the top left (also note the changing
pictograms of the discipline); Filter the EventNames with the Search field in
the top right; or click on an Event.
When clicking on an Event, the user is redirected to the EventInfoPage. Here you
can see Information about all Competitors that took part in the Event. When you
are done, you can go back to the EventsPage with the button in the top left.
## Galery
<table>
<tr>
<td><img src="doc/events_page_combobox.png"/></td>
<td><img src="doc/events_page_textfield.png"/></td>
<td><img src="doc/event_info.png"/></td>
</tr>
<tr>
<td>Select discipline</td>
<td>Filter by Event</td>
<td>View Competitor</td>
</tr>
</table>
## Getting Started
### Dependencies
- Qt6
### Installation
```sh
git clone git@gitlab.kit.edu:ugmgt/itat_challenge_2024.git
# or download release
cd itat_challenge_2024
cmake -B build
cmake --build build
./build/itat_challenge_olympics
```
## Code Structure
### UML Diagram
```plantuml
@startuml
allowmixing
set namespaceSeparator none
skinparam ranksep 10
package C++ <<Frame>> {
class Application {
QGuiApplication app
QmlComponent component
SportModel model
FilterModel<SportModel> filter
}
class SportModel {
String discipline
<EventInfo> model
request(String discipline)
parseData()
}
class FilterModel {
void setFilterFixedString(String)
}
class EventInfo {
String eventName
List<Competitor> competitors
}
class Competitor {
String name
String code
String noc
}
}
package QML <<Frame>> {
component EventInfoPage {
component [Page] as EIPage {
component [ToolBar] as EIToolBar
component [ListView] as EILisView
}
}
EIToolBar -[hidden]- EILisView
component EventsPage {
component [Page] as EPage {
component [ToolBar] as EToolBar
component [Column] as EColumn {
component [Row] as ERow {
component [ComboBox] as EComboBox
component [TextField] as ETextField
}
component [ListView] as EListView
}
}
}
EToolBar -[hidden]- EColumn
ERow -[hidden]- EListView
component application.qml {
component ApplicationWindow {
component StackView
}
}
}
'application.qml -u- a
'Application -r- a
Application *-- "1" SportModel
Application *-- "1" FilterModel
FilterModel "1" o-- "1" SportModel
SportModel *-- "0..*" EventInfo
EventInfo *-- "0..*" Competitor
Application <.l. application.qml
StackView <.. EventInfoPage
StackView <.. EventsPage
EComboBox "request()" .> SportModel
EComboBox -[hidden]u- ETextField
SportModel "View" .> EListView
FilterModel "View" .> EListView
ETextField "Control" .r.> FilterModel
EILisView <. "View" Competitor
cloud api.olympics.com
() REST
REST - api.olympics.com
SportModel -( REST
application.qml -[hidden]u- Application
@enduml
```
## Authors
- **Silas Stulz** - *Initial Work*
- **Gero Beckmann** - *Initial Work*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View file

@ -26,10 +26,10 @@ Page {
ComboBox { ComboBox {
width: 300 width: 200
height: 50 height: 50
displayText: "Discipline: " + currentText displayText: "Disziplin: " + currentText
model: myListModel model: myListModel
textRole: "text" textRole: "text"
@ -228,7 +228,7 @@ Page {
api: "WRG" api: "WRG"
} }
} }
Component.onCompleted: currentIndex = 5;
onActivated: { onActivated: {
if (currentIndex >= 0) { if (currentIndex >= 0) {
console.log(currentValue.api); console.log(currentValue.api);
@ -236,11 +236,22 @@ Page {
} }
} }
} }
TextField { ComboBox {
height: 50
width: 200 width: 200
placeholderText: "Search" height: 50
onTextChanged: filter.setFilterFixedString(text)
displayText: "Sort by: " + currentText
model: ["hu", "hi"]
}
ComboBox {
width: 200
height: 50
displayText: "Filter: " + currentText
model: ["hu", "hi"]
} }
} }
@ -251,7 +262,7 @@ Page {
height: parent.height height: parent.height
width: parent.width width: parent.width
spacing: 20 spacing: 20
model: filter model: sports
delegate: ItemDelegate { delegate: ItemDelegate {
required property string eventName required property string eventName
required property list<QtObject> competitors required property list<QtObject> competitors

View file

@ -20,7 +20,6 @@
// console output // console output
#include <QDebug> #include <QDebug>
// #include <iostream> // #include <iostream>
#include "../model/FilterModel.h"
#include "../model/SportModel.h" #include "../model/SportModel.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -32,10 +31,7 @@ int main(int argc, char *argv[])
SportModel model; SportModel model;
model.request("ARC"); model.request("ARC");
FilterModel filter;
filter.setSourceModel(&model);
objectContext->setContextProperty("sports", &model); objectContext->setContextProperty("sports", &model);
objectContext->setContextProperty("filter", &filter);
QQmlComponent component(&engine, "qrc:/qt/qml/itat/res/gui/application.qml"); QQmlComponent component(&engine, "qrc:/qt/qml/itat/res/gui/application.qml");
QObject *object = component.create(objectContext); QObject *object = component.create(objectContext);

View file

@ -1,8 +0,0 @@
#include "FilterModel.h"
#include "SportModel.h"
FilterModel::FilterModel(QObject *parent)
: QSortFilterProxyModel(parent) {
setFilterRole(SportModel::Role::EventName);
}

View file

@ -1,14 +0,0 @@
#include <QSortFilterProxyModel>
#pragma once
class FilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
FilterModel(QObject *parent = nullptr);
private:
};

View file

View file

@ -63,12 +63,11 @@ QString SportModel::discipline() const {
void SportModel::setDiscipline(const QString &discipline) { void SportModel::setDiscipline(const QString &discipline) {
m_discipline = discipline; m_discipline = discipline;
disciplineChanged();
} }
void SportModel::request(QString discipline) { void SportModel::request(QString discipline) {
setDiscipline(discipline); m_discipline = discipline;
m_reply = m_networkManager.get(QNetworkRequest( k_requestUrl + m_discipline)); m_reply = m_networkManager.get(QNetworkRequest( k_requestUrl + m_discipline));
qDebug() << m_reply; qDebug() << m_reply;
connect(m_reply, &QNetworkReply::finished, this, &SportModel::parseData); connect(m_reply, &QNetworkReply::finished, this, &SportModel::parseData);

View file

@ -17,7 +17,7 @@ class SportModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString discipline READ discipline WRITE setDiscipline NOTIFY disciplineChanged); Q_PROPERTY(QString discipline READ discipline WRITE setDiscipline);
public: public:
enum Role enum Role
@ -62,9 +62,6 @@ public slots:
void request(QString discipline); void request(QString discipline);
void parseData(); void parseData();
signals:
void disciplineChanged();
private: private:
QList<EventInfo *> m_sportList; QList<EventInfo *> m_sportList;
QString m_discipline; QString m_discipline;