I have a group of custom widgets that I am integrating into Qt designer and when I attempt to build it, it throws LNK2005, LNK2019, and a LNK1120. It seeems to be focusing around my QtMaterialFabPlugin file so I will provide the example code based upon that. It also basically says that every function of QtMaterialFabPlugin is already defined in the corresponding .obj file. Here is my relevant code, and any help on this is greatly appreciated.
QtMaterialFabPlugin.h:
#ifndef QTMATERIALFABPLUGIN_H
#define QTMATERIALFABPLUGIN_H
#include <QDesignerCustomWidgetInterface>
class QtMaterialFabPlugin : public QObject, public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
QtMaterialFabPlugin(QObject *parent = 0);
bool isContainer() const;
bool isInitialized() const;
QIcon icon() const;
QString domXml() const;
QString group() const;
QString includeFile() const;
QString name() const;
QString toolTip() const;
QString whatsThis() const;
QWidget *createWidget(QWidget *parent);
void initialize(QDesignerFormEditorInterface *core);
private:
bool m_initialized;
};
#endif
QtMaterialFabPlugin.cpp:
#include "qtmaterialfab.h"
#include "qtmaterialfabplugin.h"
#include <QtPlugin>
QtMaterialFabPlugin::QtMaterialFabPlugin(QObject* parent) : QObject(parent) {
m_initialized = false;
}
void QtMaterialFabPlugin::initialize(QDesignerFormEditorInterface*) {
if (m_initialized) return;
m_initialized = true;
}
bool QtMaterialFabPlugin::isInitialized() const { return m_initialized; }
QWidget* QtMaterialFabPlugin::createWidget(QWidget* parent) {
return new QtMaterialFab(parent);
}
QString QtMaterialFabPlugin::name() const {
return QLatin1String("QtMaterialFab");
}
QString QtMaterialFabPlugin::group() const {
return QLatin1String("Material Widgets");
}
QIcon QtMaterialFabPlugin::icon() const { return QIcon(); }
QString QtMaterialFabPlugin::toolTip() const { return QLatin1String(""); }
QString QtMaterialFabPlugin::whatsThis() const { return QLatin1String(""); }
bool QtMaterialFabPlugin::isContainer() const { return false; }
QString QtMaterialFabPlugin::domXml() const {
return QLatin1String(
"<widget class=\"QtMaterialFab\" name=\"qtMaterialFab\">\n<widget>\n");
}
QString QtMaterialFabPlugin::includeFile() const {
return QLatin1String("qtmaterialfab.h");
}
Material-Widgets.pro:
CONFIG += plugin debug_and_release
TARGET = $$qtLibraryTarget(qtmaterialwidgetsplugin)
TEMPLATE =
HEADERS = qtmaterialappbarplugin.h qtmaterialautocompleteplugin.h qtmaterialavatarplugin.h qtmaterialbadgeplugin.h qtmaterialcheckboxplugin.h qtmaterialcircularprogressplugin.h qtmaterialdialogplugin.h qtmaterialdrawerplugin.h qtmaterialfabplugin.h qtmaterialflatbuttonplugin.h qtmaterialiconbuttonplugin.h qtmateriallistplugin.h qtmateriallistitemplugin.h qtmaterialmenuplugin.h qtmaterialprogressplugin.h qtmaterialradiobuttonplugin.h qtmaterialraisedbuttonplugin.h qtmaterialscrollbarplugin.h qtmaterialsliderplugin.h qtmaterialsnackbarplugin.h qtmaterialtableplugin.h qtmaterialtabsplugin.h qtmaterialtextfieldplugin.h qtmaterialtoggleplugin.h qtmaterialwidgets.h
SOURCES = qtmaterialsliderplugin.cpp qtmaterialappbarplugin.cpp qtmaterialautocompleteplugin.cpp qtmaterialavatarplugin.cpp qtmaterialbadgeplugin.cpp qtmaterialcheckboxplugin.cpp qtmaterialcircularprogressplugin.cpp qtmaterialdialogplugin.cpp qtmaterialdrawerplugin.cpp qtmaterialfabplugin.cpp qtmaterialflatbuttonplugin.cpp qtmaterialiconbuttonplugin.cpp qtmateriallistplugin.cpp qtmateriallistitemplugin.cpp qtmaterialmenuplugin.cpp qtmaterialprogressplugin.cpp qtmaterialradiobuttonplugin.cpp qtmaterialraisedbuttonplugin.cpp qtmaterialscrollbarplugin.cpp qtmaterialsnackbarplugin.cpp qtmaterialtableplugin.cpp qtmaterialtabsplugin.cpp qtmaterialtextfieldplugin.cpp qtmaterialtoggleplugin.cpp qtmaterialwidgets.cpp
RESOURCES = icons.qrc
LIBS += -L.
greaterThan(QT_MAJOR_VERSION, 4) {
QT += designer
} else {
CONFIG += designer
}
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
include(qtmaterialextrafiles.pri)
include(qtmaterialraisedbutton.pri)
include(qtmateriallist.pri)
include(qtmaterialradiobutton.pri)
include(qtmaterialslider.pri)
include(qtmaterialtextfield.pri)
include(qtmaterialtoggle.pri)
include(qtmaterialdialog.pri)
include(qtmaterialtable.pri)
include(qtmaterialautocomplete.pri)
include(qtmaterialbadge.pri)
include(qtmaterialcheckbox.pri)
include(qtmaterialavatar.pri)
include(qtmaterialcircularprogress.pri)
include(qtmaterialappbar.pri)
include(qtmaterialfab.pri)
include(qtmaterialdrawer.pri)
include(qtmaterialsnackbar.pri)
include(qtmaterialmenu.pri)
include(qtmateriallistitem.pri)
include(qtmaterialflatbutton.pri)
include(qtmaterialtabs.pri)
include(qtmaterialiconbutton.pri)
include(qtmaterialscrollbar.pri)
include(qtmaterialprogress.pri)
Whoops, I just had to add the
TEMPLATE = lib
to the .pro file.
I'm creating a QList<> in C++ using a QML ListView to display it. The application runs without errors but the ListView stubbornly remains empty.
The QML will show a rectangle for the presence of each list item.
I checked the UI code by creating a list in QML.
It displays correctly for the QML created list.
Here's my QML:
import Processes 1.0
...
ListView {
id: qInterfaceList
height: parent.height;
width: parent.width;
model: myModel
orientation: ListView.Vertical
delegate:
Rectangle {
height: 30;
width: 120;
border.color: "red"
border.width: 3
}
The C++ code that creates and registers the list object:
// Register C++ classes as a QML type named Processes (version 1.0)
qmlRegisterType<Process>("Processes", 1, 0, "Process");
QQmlApplicationEngine engine;
// read the configuration file
Config conf;
if ( conf.read() )
{
QQmlContext* ctxt = engine.rootContext();
if ( ctxt )
{
qDebug()
<< "--- conf.Interfaces: "
<< conf.Interfaces.length()
;
ConfigInterface c;
QVariant v = QVariant::fromValue( conf.Interfaces );
qDebug()
<< "--- ConfigInterface: "
<< v
<< "--- typeName: "
<< v.typeName()
;
ctxt->setContextProperty("myModel", QVariant::fromValue( conf.Interfaces ));
}
}
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
To debug I output information about list from C++ and QML:
In C++ the count of list items is correct.
In C++ the conversion to QVariant is working.
In QML it sees the defined list.
The debugging output:
Debugging starts
--- conf.Interfaces: 65
--- ConfigInterface: QVariant(QList<ConfigInterface*>, ) --- typeName: QList<ConfigInterface*>
qml: myModel: QVariant(QList<ConfigInterface*>)
Debugging has finished
Any ideas what's wrong or how to debug this?
Thanks
EDIT: Here's the class being used as a list item
Class declaration:
class ConfigInterface : public QObject
{
Q_OBJECT
Q_PROPERTY(QString sql READ getTag WRITE setTag NOTIFY tagChanged)
Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged)
public:
/*explicit*/ ConfigInterface();
/*explicit*/ ConfigInterface(QObject *parent);
~ConfigInterface();
// Copy constructor needed because these are copied when added to a QList
ConfigInterface(const ConfigInterface &p2) {_tag = p2._tag; _description = p2._description; }
QString getDescription() const;
void setDescription(QString&);
QString getTag() const;
void setTag(QString&);
signals:
void tagChanged(QString);
void descriptionChanged(QString);
public:
QString _tag;
QString _description;
QString QueryTemplate;
QString ConnectString;
QString MinimumId;
};
Q_DECLARE_METATYPE(ConfigInterface*)
C++ code:
ConfigInterface::ConfigInterface()
: QObject( nullptr )
{
}
ConfigInterface::ConfigInterface(QObject* parent)
: QObject(parent)
{
}
ConfigInterface::~ConfigInterface()
{
}
QString ConfigInterface::getTag() const
{
return _tag;
}
void ConfigInterface::setTag(QString& str)
{
_tag = str;
emit tagChanged(_tag);
}
The main problem is caused because it has a list of ConfigInterface *, according to the examples provided in the documentation should be a list of QObject *:
class Config{
[...]
public:
QList<QObject *> Interfaces;
[...]
};
In addition to this you should get the following warning:
/..../configinterface.h:17: warning: base class ‘class QObject’ should be explicitly initialized in the copy constructor [-Wextra]
ConfigInterface(const ConfigInterface &p2) {_tag = p2._tag; _description = p2._description; }
^~~~~~~~~~~~~~~
This is caused because QObject and its derived classes must not have a copy constructor or assignment operator, For more information read the following:
http://doc.qt.io/qt-5/qobject.html#no-copy-constructor-or-assignment-operator
Another improvement is that both constructors can be united in only one, in the end their class could have the following structure:
configinterface.h
#ifndef CONFIGINTERFACE_H
#define CONFIGINTERFACE_H
#include <QObject>
class ConfigInterface : public QObject
{
Q_OBJECT
Q_PROPERTY(QString sql READ getTag WRITE setTag NOTIFY tagChanged)
Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged)
public:
ConfigInterface(QObject *parent=Q_NULLPTR);
~ConfigInterface();
QString getTag() const;
void setTag(const QString &tag);
QString getDescription() const;
void setDescription(const QString &description);
signals:
void tagChanged(QString);
void descriptionChanged(QString);
private:
QString _tag;
QString _description;
QString QueryTemplate;
QString ConnectString;
QString MinimumId;
};
#endif // CONFIGINTERFACE_H
configinterface.cpp
#include "configinterface.h"
ConfigInterface::ConfigInterface(QObject* parent)
: QObject(parent)
{
}
ConfigInterface::~ConfigInterface()
{
}
QString ConfigInterface::getDescription() const
{
return _description;
}
void ConfigInterface::setDescription(const QString &description)
{
if(_description == description)
return;
emit descriptionChanged(description);
_description = description;
}
QString ConfigInterface::getTag() const
{
return _tag;
}
void ConfigInterface::setTag(const QString &tag)
{
if(tag == _tag)
return;
emit tagChanged(tag);
_tag = tag;
}
A project uses custom frame widget that implements a kind of "cover" that hides widgets (think of it as of security cover that prevents pressing buttons).This is a required visual design. Qt version is 4.6
#ifndef CQFRAME_H
#define CQFRAME_H
#include <QFrame>
#include <QtDesigner/QDesignerExportWidget>
//! [0] //! [1]
class CQFrame : public QFrame
// our agreement about "custom" widgets is to start them with CQ
{
Q_OBJECT
Q_ENUMS(FrameColorStyle)
Q_PROPERTY(FrameColorStyle colorStyle READ getColorStyle WRITE setColorStyle)
Q_PROPERTY(bool border READ border WRITE setBorder)
//! [0]
private:
bool curCover;
QFrame *frameCover;
public:
enum FrameColorStyle {fcDark, fcLight, fcTransparent, fcSystemDefault, fcRed, fcGreen, fcBlue};
CQFrame(QWidget *parent = 0);
void setCoverPropertie();
void setCover(bool state);
protected:
void resizeEvent(QResizeEvent *event);
FrameColorStyle m_colorStyle;
QString pstylebord;
bool m_border;
//! [2]
};
//! [1] //! [2]
#endif
I omitted getters and setters that are irrelevant to the problem. Here is implementation:
void CQFrame::setCoverPropertie()
{
QString str, strAlpha, gradient1, gradient2;
strAlpha.setNum(200);
gradient1 = "rgba("+str.setNum(cwDisableColor.red())+", "
+str.setNum(cwDisableColor.green())
+", "+str.setNum(cwDisableColor.blue())
+" ," +strAlpha+ " )";
gradient2 = "rgba("+str.setNum(cwLbColor.red())+", "
+str.setNum(cwLbColor.green())+", "
+str.setNum(cwLbColor.blue())+" ," +strAlpha+ " )";
QStackedLayout *stackedLayout = new QStackedLayout(this);
frameCover = new QFrame(this);
frameCover->setGeometry(rect());
frameCover->setStyleSheet("QFrame{border:5px solid "+strLbColor+"; "
"border-radius: 10px; background-color: "
"qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.5, stop: 0 "
+gradient1+" , stop: 1 "+gradient2+"); }");
stackedLayout->addWidget(frameCover);
stackedLayout->setStackingMode(QStackedLayout::StackAll);
}
void CQFrame::setCover(bool state)
{
frameCover->setVisible(curCover = state);
}
void CQFrame::resizeEvent(QResizeEvent *event)
{
if (curCover)
frameCover->setGeometry(rect());
}
The design isn't mine, I was asked to fix strange visual glitches it experiences. This "frame" is used in Qt designer as one of widgets. After a while suddenly everything resizes, which prompted question "what is wrong with this code". Qt fires warning about attempt to add layout while one already exist: I suppose that may cause a problem, because a frame must have only one layout at time? Code generated by Qt Creator looks something like
void setupUi(CQFrame *CQWnd1T2SeparateONForm)
{
if (CQWnd1T2SeparateONForm->objectName().isEmpty())
CQWnd1T2SeparateONForm->setObjectName(QString::fromUtf8("CQWnd1T2SeparateONForm"));
CQWnd1T2SeparateONForm->resize(735, 241);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(CQWnd1T2SeparateONForm->sizePolicy().hasHeightForWidth());
CQWnd1T2SeparateONForm->setSizePolicy(sizePolicy);
gridLayout = new QGridLayout(CQWnd1T2SeparateONForm); // warning here
}
There is similar problem with standard QMainWindow which always got own "special" layout, which Qt Creator solves automatically, by adding a central widget to the layout and everything else is added to that widget. What I don't know that is how to simulate same behavior with a custom widget with Qt Creator plugin.
Or what alternative design for CQFrame can be used. CQFrame reused in dozen project, in about 30+ panels, so reuse of code for them all is a strict requirement.
Current plugin is very basic:
class QDESIGNER_WIDGET_EXPORT CQFramePlugin : public QObject,
public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
CQFramePlugin(QObject *parent = 0);
bool isContainer() const;
bool isInitialized() const;
QIcon icon() const;
QString domXml() const;
QString group() const;
QString includeFile() const;
QString name() const;
QString toolTip() const;
QString whatsThis() const;
QWidget *createWidget(QWidget *parent);
void initialize(QDesignerFormEditorInterface *core);
private:
bool initialized;
};
.cpp for it:
#include "cqframe.h"
#include "cqframeplugin.h"
#include <QtPlugin>
CQFramePlugin::CQFramePlugin(QObject *parent)
: QObject(parent)
{
initialized = false;
}
void CQFramePlugin::initialize(QDesignerFormEditorInterface * /* core */)
{
if (initialized)
return;
initialized = true;
}
bool CQFramePlugin::isInitialized() const
{
return initialized;
}
QWidget *CQFramePlugin::createWidget(QWidget *parent)
{
return new CQFrame(parent);
}
QString CQFramePlugin::name() const
{
return "CQFrame";
}
QString CQFramePlugin::group() const
{
return "CustomWidgets";
}
QIcon CQFramePlugin::icon() const
{
return QIcon(":/Resources/frame_icon.png");
}
QString CQFramePlugin::toolTip() const
{
return "";
}
QString CQFramePlugin::whatsThis() const
{
return "";
}
bool CQFramePlugin::isContainer() const
{
return true;
}
QString CQFramePlugin::domXml() const
{
return "<ui language=\"c++\">\n"
" <widget class=\"CQFrame\" name=\"Frame\">\n"
" <property name=\"geometry\">\n"
" <rect>\n"
" <x>0</x>\n"
" <y>0</y>\n"
" <width>120</width>\n"
" <height>80</height>\n"
" </rect>\n"
" </property>\n"
" </widget>\n"
"</ui>";
}
QString CQFramePlugin::includeFile() const
{
return "cqframe.h";
}
So I haven't worked with QT yet, but I'm going to gie it a try. First thing, I think, is that you should use the QStackedLayout to cover the widgets (source and QT manual). But you need to have a single stack.
So the stack should be a private member of CQFrame. E.g.
class CQFrame : public QFrame
{
[...]
private:
QWidget* _frame; // Frame to cover.
QFrame* _frameCover;
QStackedLayout* _stackedLayout;
[...]
And probably:
CQFrame::~CQFrame()
{
delete _stackedLayout;
delete _frameCover;
}
Then you could already initialize everything in the constructor
CQFrame::CQFrame(QWidget* parent = 0, QWidget* frame)
: QFrame(parent)
, _frame(frame)
, _frameCover(new QFrame(this))
, _stackedLayout(new QStackedLayout(this))
{
_frameCover->setGeometry(rect());
[...]
_stackedLayout->addWidget(frame);
_stackedLayout->addWidget(frameCover);
//default:_stackedLayout->setStackingMode(QStackedLayout::StackOne);
}
You could then switch between widgets in the stack using
void CQFrame::SetCover(bool state)
{
if (state) _stackedLayout->setCurrentWidget(_frameCover);
else _stackedLayout->setCurrentWidget(_frame);
}
Maybe this helps you.
edit2:
I removed this code, as it was incorrect, both in coding format, as in idea
So I checked the QT sources QStackedLayout.cpp and QLayout and it seems a QWidget can have only one layout. If you add another layout, you get an error.
Furthermore, a QStackedLayout is a QLayout, is QObject. That could indicate it is automatically removed?
I'm not sure of the cover is implemented in QT as it should. It seems like you have a QWidget you want to cover, on top of which you put a QStackedLayout that is not used as designed i.e. switching stack objects, on top of which you put a new QWidget that is made visible or not.
And maybe that bottom QWidget is already in a (stacked)layout, etc.
I'm coming from c++ and I'm having difficulties making my classes work the way I want them to. The program I am building is rather simple. The user creates an album or two and can fill that album with cards. Those albums are created dynamically on run time.
Here is my initial class diagram for the back-end c++ stuff:
class diagram
And here is how the UI looks like:
QML UI
The crux of the problem:
A set of changes to the classes are necessary to make them available to QML.
Inheritance: Classes need to inherit from QObject or QAbstractListModel depending on their role.
Containers: Dynamically instantiated objects within an object, through pointers or not, need to be placed in a QList or something similar.
Root context property: The model needs to be registered so that it can be made available in a Listview for example.
I have had some success setting an instance of an Album class, with some changes made to it, as a root context property. The model worked correctly and the cards in the album displayed correctly in a ListView.
The problem here is that I need to step out of the picture another level, I should set a class that contains the Albums as the root context property and let the user create, and add, Albums to it during run time. I will also need to also display the albums and the cards inside them through different views.
I am wondering if I should implement a table/tree data structure for the albums and the cards and somehow pass the columns and rows to the models or If there is a tidier approach that I am not aware of.
Here are my classes so far:
album.h
#ifndef ALBUM_H
#define ALBUM_H
#include <QObject>
#include <QString>
#include <QAbstractListModel>
#include <QQmlListProperty>
#include "card.h"
class Album : public QAbstractListModel
{
Q_OBJECT
public:
enum AlbumRoles {
CardIDRole = Qt::UserRole + 1,
NameRole,
ImageURLRole,
SubtypeRole,
SupertypeRole,
NumberRole,
ArtistRole,
RarityRole,
SeriesRole,
SetRole,
SetCodeRole,
ConditionRole,
StatusRole
};
Album(QObject *parent = 0);
QString name() const;
void addCard(const Card &card);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Card> m_cards;
QString m_name;
};
album.cpp
#include "album.h"
Album::Album(QObject *parent)
: QAbstractListModel(parent)
{
}
QString Album::name() const
{
return m_name;
}
void Album::addCard(const Card &card)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_cards << card;
endInsertRows();
}
int Album::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_cards.count();
}
QVariant Album::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_cards.count())
return QVariant();
const Card &card = m_cards[index.row()];
if (role == CardIDRole)
return card.cardID();
else if (role == NameRole)
return card.name();
else if (role == ImageURLRole)
return card.imageURL();
else if (role == SubtypeRole)
return card.subtype();
else if (role == SupertypeRole)
return card.supertype();
else if (role == NumberRole)
return card.number();
else if (role == ArtistRole)
return card.artist();
else if (role == RarityRole)
return card.rarity();
else if (role == SeriesRole)
return card.series();
else if (role == SetRole)
return card.set();
else if (role == SetCodeRole)
return card.setCode();
else if (role == ConditionRole)
return card.condition();
else if (role == StatusRole)
return card.status();
return QVariant();
}
QHash<int, QByteArray> Album::roleNames() const {
QHash<int, QByteArray> roles;
roles[CardIDRole] = "cardID";
roles[NameRole] = "name";
roles[ImageURLRole] = "imageURL";
roles[SubtypeRole] = "subtype";
roles[SupertypeRole] = "supertype";
roles[NumberRole] = "number";
roles[ArtistRole] = "artist";
roles[RarityRole] = "rarity";
roles[SeriesRole] = "series";
roles[SetRole] = "set";
roles[SetCodeRole] = "setCode";
roles[ConditionRole] = "condition";
roles[StatusRole] = "status";
return roles;
}
card.h
#ifndef CARD_H
#define CARD_H
#include <QString>
class Card
{
public:
// Standard Qt constructor with parent for memory management
Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
QString cardID() const;
QString name() const;
QString imageURL() const;
QString subtype() const;
QString supertype() const;
int number() const;
QString artist() const;
QString rarity() const;
QString series() const;
QString set() const;
QString setCode() const;
QString condition() const;
QString status() const;
private:
// private members
QString m_cardID;
QString m_name;
QString m_imageURL;
QString m_subtype;
QString m_supertype;
int m_number;
QString m_artist;
QString m_rarity;
QString m_series;
QString m_set;
QString m_setCode;
QString m_condition;
QString m_status;
};
#endif //CARD_H
card.cpp
#include "card.h"
// Standard Qt constructor with parent for memory management
Card::Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status): m_cardID(cardID), m_name(name), m_imageURL(imageURL), m_subtype(subtype), m_supertype(supertype), m_number(number), m_artist(artist), m_rarity(rarity), m_series(series), m_set(set), m_setCode(setCode), m_condition(condition), m_status(status)
{
}
QString Card::cardID() const
{
return m_cardID;
}
QString Card::name() const
{
return m_name;
}
QString Card::imageURL() const
{
return m_imageURL;
}
QString Card::subtype() const
{
return m_subtype;
}
QString Card::supertype() const
{
return m_supertype;
}
int Card::number() const
{
return m_number;
}
QString Card::artist() const
{
return m_artist;
}
QString Card::rarity() const
{
return m_rarity;
}
QString Card::series() const
{
return m_series;
}
QString Card::set() const
{
return m_set;
}
QString Card::setCode() const
{
return m_setCode;
}
QString Card::condition() const
{
return m_condition;
}
QString Card::status() const
{
return m_status;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "app.h"
#include "album.h"
#include "card.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Album>("Classes.PokemonApp", 1, 0, "Album");
qmlRegisterType<Card>("Classes.PokemonApp", 1, 0, "Card");
App pokeApp;
Album myAlbumModel;
Card cardOne("xy7-2","Gloom","http://s3.amazonaws.com/pokemontcg/xy7/2.png","Stage 1","Pokémon",2,"Masakazu Fukuda","Uncommon","XY","Ancient Origins","xy7","Mint","In my collection");
Card cardTwo("xy7-7","Sceptile-EX","https://s3.amazonaws.com/pokemontcg/xy7/7.png","EX","Pokémon",7,"Eske Yoshinob","Rare Holo EX","XY","Ancient Origins","xy7","Used","Duplicate");
myAlbumModel.addCard(cardOne);
myAlbumModel.addCard(cardTwo);
pokeApp.addAlbum(myAlbumModel);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("pokeApp", &pokeApp);
return app.exec();
}
CardsView.qml
import QtQuick 2.0
import Classes.PokemonApp 1.0
// Cards Deligate
ListView {
width: 200; height: 250
model: myAlbumModel
delegate: Text { text: "Card:"
+ "\n" + "ID: " + cardID
+ "\n" + "Name: " + name
+ "\n" + "Image URL: " + imageURL
+ "\n" + "Subtype: " + subtype
+ "\n" + "Supertype: " + supertype
+ "\n" + "Number: " + number
+ "\n" + "Artist: " + artist
+ "\n" + "Rarity: " + rarity
+ "\n" + "Series: " + series
+ "\n" + "Set: " + set
+ "\n" + "Set code: " + setCode
+ "\n" + "Condition: " + condition
+ "\n" + "Status: " + status }
}
So to be clear, this is what I need help with:
Give the user the ability to create albums during run time, let the user add cards to albums and have models display the albums and the cards in them in different views.
Assuming that your App class has a list of Album objects, accessible as a property, adding an album would simply be a slot or invokable method in that class.
Something like
public slots:
void addAlbum(const QString &name);
That would create an instance of Album, put it in the list and emit the notify signal for the property.
For adding a Card you could add a similar slot to the Album class, something like
pulic slots:
void addCard(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
which would simply create a Card add then call the addCard overload.
I am now finished with the project and I have accomplished all the goals I had and overcome the problems that I have asked for help here and a few other ones that showed up later on.
For those who might find this page while searching for solutions for similar problems they have, you can find all the source code for my complete project on my Github page here: https://github.com/Nizars/PokeApp
I would like to point out a few changes that I have made:
I have switched to an SQL method of storing the cards, the albums and the card images as well.
I have used different models for different views. A proxy model for relational queries used by the table view and two sql models for other functionalities.
I also use a custom made image provider for checking for images requested from the qml side in the database first before requesting the images from an API through https and storing it in the database if it wasn't found there at first.
I have also switched to a borderless window to get rid of the windows default window border and created my own close, minimize and maximize buttons. I have also used a mouse position provider to make dragging the window around possible and seamless.
I have also added a working RSS feed to the homepage and used a page swipe view for the different pages in the app, I used connections to signal messages between the different qml documents so that data can be sent between them.
Final note: You need to include your own OpenSSL library includes for the network requests to work and create the sql tables in your local server as well as connect the app to it in pokeapp.cpp.
Feel free to ask me any questions if you need any help.
I have lots of custom Qt widgets that have their own layouts and contain lists of even more widgets.
I ended up having a ManagerWidget that controls everything (including listening to all events) and propagating them to the widget that needs to update its content. It seems there are too many methods that just pass the information along.
Is there a better way of doing this? Connecting the classes through signal/slots doesn't really work in this case because there's a map of them and I wouldn't know which one was fired.
Would the Mediator pattern fit in this case?
class ManagerWidget : public QWidget {
public:
map<ID, MainWidgetContainer*> mainWidgetMap;
RecordsContainer *recentRecords; // This widget can contain any number of widgets, each showing a record
// events are caught here and propagated to the widgets
void AddNewEntry(ID id) {
MainWidgetContainer *w = new MainWidgetContainer(id);
mainWidgetMap.insert(id, w);
}
void UpdatePictureWidget(ID id) {
mainWidgetMap.value(id)->UpdatePictureWidget();
}
void AddXItem(ID id, Type1 k) {
if(id == 0)
recentRecords->AddXItem(k);
else
mainWidgetMap.value(id)->AddXItem(k);
}
void UpdateEntry(ID id, string newName) { }
};
class MainWidgetContainer : public QWidget {
public:
NameWidget *name;
PictureWidget *pic;
RecordsContainer *records; // This widget can contain any number of widgets, each showing a record
MainWidgetContainer(ID id) {
name = new NameWidget(id);
records = new RecordsContainer();
pic = new PictureWidget();
// setup layout and add items to records as they come in later
}
AddXItem(Type1 k) { records->AddXItem(k); }
UpdatePictureWidget() { pic->Update(); }
};
class RecordsContainer : public QWidget {
public:
map<Type1, Item*> itemMap;
AddXItem(Type1 k) {
Item *item = new Item(k);
itemMap.insert(k, item);
mainLayout->addWidget(item);
}
AddYItem(Type2 k);
// etc
};