SDL2 Rendering into QWidget - c++

I'm looking to port an emulator I wrote in GTK2 and SDL1.2 into using QT5 and SDL2.0.3. So far thats been fine as far as the emulator all works in SDL2 independantly and the GUI works fine in QT, with them both interacting. My issue is with trying to get SDL2 to appear inside a QWidget. This is something that worked fine in SDL1.2 using the window hack method.
From everything I've read online, this can be down with the SDL_CreateWindowFrom function getting the winId() from a QT Widget and passing it into it. However this seems to be for when you are using openGL.
What I want to know is how to do this when im only using SDL2's standard drawing functions to a texture, no OpenGL. All my attempts so far have failed and SDL just didn't appear in the widget at all.
Alternatively, I know you can convert an SDL_Suface to a QImage. How would I then go about getting the QT Widget to update everytime the QImage is updated (60fps). The complication with this is QT and SDL are running in seperate threads.
If anyone has some idea of a direction to point me or some useful example codes that may lead to an answer I would be very greatful. I havn't provided code because I'm not yet sure how to do it, to provide any code.
EDIT
So after some trialing with SDL and QT. I have managed to get SDL2 to draw into a QWidget using the its Standard Drawing tools. However this only works when setting the renderer to SDL_RENDERER_SOFTWARE. If i try and use SDL_RENDERER_ACCELERATED like I would perfer too. It results in the QT window locking up completely and failing to draw anything (including all other widgets in the window), and finally being terminated as not responding by the operating system (Kubuntu in my testing case).
Does any one have any leads as to why this may be. I can use SDL_RENDERER_ACCELERATED fine when SDL2 is drawing into its own window created with SDL_CreateWindow and SDL_CreateRenderer. Its only a problem when drawing to a QWidget.
Here is the code I have so far:
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <SDL2/SDL.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindowFrom((void*)w.ptr_gfx->winId());
SDL_Renderer* render = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
SDL_SetRenderDrawColor(render, 255, 0, 0, 255);
SDL_RenderFillRect(render, NULL);
SDL_RenderPresent(render);
return a.exec();
SDL_DestroyWindow(window);
SDL_DestroyRenderer(render);
SDL_Quit();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ptr_gfx = ui->gfx;
ui->gfx->setUpdatesEnabled(false);
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
Ui::MainWindow *ui;
QWidget* ptr_gfx;
};
#endif // MAINWINDOW_H
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QWidget" name="gfx" native="true">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>191</width>
<height>131</height>
</rect>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

When QImage is updated with new data, simply call the widget's repaint() method to make it paint itself on the screen.

Related

Qt QPushbutton not added by using .ui

I'm trying to add a new QPushbutton in my mainwindow.ui.
I added Qpushbutton by dragging in .ui, and on the right side I can see it added properly.
(I added btnDownload button, and btnQuit is added long time ago. it works fine)
I clicked go to slot on btnDownload and it created this code:
void MainWindow::on_btnDownload_clicked()
{
QMessageBox::information(this,"notify","button is clicked "); //this part is what I added
}
however, when I build my project I don't see this button added in the place I located it.
So to workaround, I tried to use setGeometry function in mainwindow.cpp file to locate this button.
this is a code I tried to write:
ui->btnDownload->setGeometry(x,y,wide,height);
but I get a warning message saying:
no memeber named 'btnDownload' in Mainwindow::ui
I did add QPushbutton in .ui, and created the connected function, but it cannot find this button.. how should I solve this?
I think I fixed it.
I compared it with btnQuit button (which works fine)
and the difference was that the new button was not clarified in header file. I'm going to fix it and I think it will works. If it doesn't, I'll add it.
Look into the .ui file. It's XML. You should see something like this
<widget class="QWidget" name="centralwidget">
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>130</x>
<y>150</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>PushButton</string>
</property>
</widget>
</widget>
On the build directory typically at <workspace>\build-<project>-Desktop_Qt_6_2_0_MSVC2019_64bit-Debug\<project>_autogen\include you should find a file ui_mainwindow.h with the contents
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralwidget;
QPushButton *pushButton; //<<======= this
QMenuBar *menubar;
QStatusBar *statusbar;
void setupUi(QMainWindow *MainWindow)
{
Right below you should find the instantiation of your button
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(800, 600);
centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
pushButton = new QPushButton(centralwidget); // <<<============= this
pushButton->setObjectName(QString::fromUtf8("pushButton"));
pushButton->setGeometry(QRect(130, 150, 75, 23));
MainWindow->setCentralWidget(centralwidget);

Cannot initialise a parameter of type QMainWindow: ui->setupUi(this) error

I am new to Qt. I have from the link
https://www.bogotobogo.com/Qt/Qt5_QNetworkRequest_Http_File_Download.php
the code because I want to learn about QNewtorkRequest class.
But when I execute the code I get the error below:
here is a part of the source code:
#include "httpdownload.h"
#include "ui_httpdownload.h"
HttpDownload::HttpDownload(QWidget *parent) :
QDialog(parent),
ui(new Ui::HttpDownload)
{
ui->setupUi(this);
ui->urlEdit->setText("http://qt.com");
ui->statusLabel->setWordWrap(true);
ui->downloadButton->setDefault(true);
ui->quitButton->setAutoDefault(false);
progressDialog = new QProgressDialog(this);
connect(ui->urlEdit, SIGNAL(textChanged(QString)),
this, SLOT(enableDownloadButton()));
connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelDownload()));
}
Here is the ui file:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HttpDownload</class>
<widget class="QMainWindow" name="HttpDownload">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>580</width>
<height>204</height>
</rect>
</property>
<property name="windowTitle">
Can you please help me?
There's your problem. The ui file expects a QMainWindow but your C++ code defines a QDialog. Choose one and fix the other.

How to dynamically swap QWidgets

I have a QMainWindow which contains, on the left, a QTreeWidget and, on the right, another QMainWindow with windowFlags set to Qt::Widget.
This is because the QMainWindow is the only way to provide docking functionality to a QWidget. (I want the docked components to be popped out of the "real" window entirely if needed.
My problem is that I want users to be able to keep popped out dock widgets even if their item is not selected on the left.
For example, here is the global layout:
Let's say I select Item 1. On the right I will have some dockable widgets which I am able to reorder as I wish. If I pop one out to keep an eye on it, I don't want it to disappear if I select Item 2.
To go even further, I could want to show ALL Items' dockable widgets at once if I want.
My original idea to achieve this was to make each Item have its dedicated QMainWindow stored inside its data and I would just switch the right one to reflect the currently active one.
Maybe what I want is a bad idea and maybe it's not even feasible.
Could someone with some Qt knowledge tell me if I'm doing/wanting something wrong please?
Edit:
It would be perfectly fine to me if there were a way to trigger the "inner QMainWindow" pop out manually. Like, for example, a button "pop out" in the top right corner which would completely pop it and make it an entirely new window (still linked to the other one, though)
Edit2:
I's like to point out that I haven't tried anything regarding this question yet. I am essentially wondering if it fits with Qt's way of doing things.
This question made me happy about being able to provide docking capabilities to only a part of the program but I still aren't sure about what I want. Can I really achieve this?
Edit7, MVCE:
Hopefully I didn't forget anything since this is done by modifying my files.
mainwindow2.cpp
#include "mainwindow2.hh"
#include "ui_mainwindow2.h"
#include <QTreeWidgetItem>
MainWindow2::MainWindow2(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow2)
{
ui->setupUi(this);
ui->mainPanel->setWindowFlags(Qt::Widget);
QTreeWidgetItem* item = new QTreeWidgetItem;
item->setData(0, 0, QVariant::fromValue(QString("Item 1")));
ui->accountsTreeWidget->addTopLevelItem(item);
QTreeWidgetItem* item2 = new QTreeWidgetItem;
item2->setData(0, 0, QVariant::fromValue(QString("Item 2")));
ui->accountsTreeWidget->addTopLevelItem(item2);
}
MainWindow2::~MainWindow2()
{
delete ui;
}
mainwindow2.hh
#ifndef MAINWINDOW2_HH
#define MAINWINDOW2_HH
#include <QMainWindow>
namespace Ui {
class MainWindow2;
}
class MainWindow2 : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow2(QWidget *parent = 0);
~MainWindow2();
private:
Ui::MainWindow2 *ui;
};
#endif // MAINWINDOW2_HH
mainwindow2.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow2</class>
<widget class="QMainWindow" name="MainWindow2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>700</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1200</width>
<height>700</height>
</size>
</property>
<property name="windowTitle">
<string>Main Window</string>
</property>
<widget class="QWidget" name="centralWidget">
<property name="minimumSize">
<size>
<width>1200</width>
<height>658</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1" rowspan="2" colspan="2">
<widget class="QMainWindow" name="mainPanel"/>
</item>
<item row="0" column="0">
<widget class="QTreeWidget" name="accountsTreeWidget">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<connections/>
</ui>
main.cpp
#include "mainwindow2.hh"
#include <QApplication>
#include <QStyleFactory>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setStyle(QStyleFactory::create("Fusion"));
MainWindow2 w;
w.show();
return app.exec();
}
project.pro
#-------------------------------------------------
#
# Project created by QtCreator 2017-11-12T23:07:49
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = project
TEMPLATE = app
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
INCLUDEPATH += $$PWD
SOURCES += \
main.cpp \
mainwindow2.cpp
HEADERS += \
mainwindow2.hh
FORMS += \
mainwindow2.ui
In the right side, you could add a parent widget, with a QBoxLayout, attach all the items to it, and just hide, show and sort them as you wish.
Make them a custom class, with a public int variable to let you identify them later on. Store their pointers in a QList, and then just loop the list and hide or show items by your criteria ( Using the int variable ).
Example:
Items form category A -> int value 0
Items form category B -> int value 1
etc...

undefined reference to vtable when compiling a Qt hello World

I was following this tutorial where I designed a simple window with a single button and try to trigger it. When I compile it I get an error and I don't really understand where it comes from. When I compile a simple program that works, but now when I try to create my own gui using QDesginer it doesn't work anymore. Usually the vtable error means that some virtual function is not implemented, but I don't see wheer this should result from. I have looked at similar questions here like QT C++ error: undefined reference to `vtable for appprinter' or Qt with codeblocks - undefined reference to vtable but this doesn't really help.
main.cpp
#include <QtWidgets/qapplication.h>
#include <QtWidgets/qpushbutton.h>
#include "main_frame.h"
class TestMainFrame : public QFrame
{
Q_OBJECT
public:
explicit TestMainFrame(QWidget *parent = 0);
~TestMainFrame();
private slots:
void onTest();
private:
Ui::MainFrameGUI *ui;
};
TestMainFrame::TestMainFrame(QWidget *parent) :
QFrame(parent),
ui(new Ui::MainFrameGUI)
{
ui->setupUi(this);
}
TestMainFrame::~TestMainFrame()
{
delete ui;
}
void TestMainFrame::onTest()
{
printf("Test\n");
}
int main( int argc, char **argv )
{
QApplication a(argc, argv);
TestMainFrame w;
w.show();
return a.exec();
}
main_frame.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainFrameGUI</class>
<widget class="QFrame" name="MainFrameGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>295</width>
<height>77</height>
</rect>
</property>
<property name="windowTitle">
<string>Testbutton</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="mButton">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>mButton</sender>
<signal>clicked()</signal>
<receiver>MainFrameGUI</receiver>
<slot>onTest()</slot>
<hints>
<hint type="sourcelabel">
<x>303</x>
<y>38</y>
</hint>
<hint type="destinationlabel">
<x>303</x>
<y>38</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>onTest()</slot>
</slots>
</ui>
build.log
d:\opt\qt-5.2.1\bin\uic.exe -g cpp -o d:\src\c\QTFrameQML\main_frame.h d:\src\c\QTFrameQML\main_frame.ui
g++.exe -std=c++11 -Wall -g -ID:\opt\qt-5.2.1\include -c d:\src\c\QTFrameQML\main.cpp -o Debug\obj\main.o
g++.exe -LD:\opt\qt-5.2.1\lib -o Debug\QTFrameQML.exe Debug\obj\main.o -lgdi32 -luser32 -lkernel32 -lcomctl32 -lQt5Core -lQt5Gui -lGLESv2d -lQt5Widgetsd -mwindows
Debug\obj\main.o: In function `ZN13TestMainFrameC2EP7QWidget':
d:/src/c/QTFrameQML/main.cpp:23: undefined reference to `vtable for TestMainFrame'
d:/src/c/QTFrameQML/main.cpp:23: undefined reference to `vtable for TestMainFrame'
Debug\obj\main.o: In function `ZN13TestMainFrameD2Ev':
d:/src/c/QTFrameQML/main.cpp:28: undefined reference to `vtable for TestMainFrame'
d:/src/c/QTFrameQML/main.cpp:28: undefined reference to `vtable for TestMainFrame'
collect2.exe: error: ld returned 1 exit status
Do NOT use Q_OBJECT macros in cpp files except you want to manually run the moc... Just move your class definition to main.h, include this in SOURCES and rerun qmake -> works
this is often an dependency issue where the moc-compiler where not invoked due to an already existing object file. try to distclean your environment and start qmake again. hope that helps

Qt - Using QUiLoader to load a widget with no parent

I have just started learning Qt and I am trying to create a simple widget using this QUiLoader. But I am getting this error : "Designer: An error has occurred while reading the UI file at line 1, column 0: Premature end of document."
#include <QtGui/QApplication>
#include <QtUiTools/QUiLoader>
#include <QFile>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QUiLoader loader;
QFile file(":/aks.ui");
file.open(QFile::ReadOnly);
QWidget *myWidget = loader.load(&file);
if(myWidget){
myWidget->show();
}
return a.exec();
}
I constructed the ui file using QtCreator 2.4.1 and I am on Qt 4.7.4. Check out the ui file too.
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>131</width>
<height>129</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>A</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_2">
<property name="text">
<string>B</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_3">
<property name="text">
<string>C</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_4">
<property name="text">
<string>D</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_5">
<property name="text">
<string>E</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
My project file:
#-------------------------------------------------
#
# Project created by QtCreator 2012-05-21T19:48:31
#
#-------------------------------------------------
QT += core gui
TARGET = Example
TEMPLATE = app
SOURCES += main.cpp \
sortdialog.cpp
HEADERS += \
sortdialog.h
FORMS += \
sortdialog.ui \
aks.ui
CONFIG += uitools
You need to add your .ui file to the resources of your project. Resources are files which get "compiled inside" of your app and are then available to Qt classes by file paths starting with ":/".
In order to add resources to your project, you need to create a resource file listing the files you want to register as resources. This file will be another XML file and can be created and edited by QtCreator. In the project manager, add another file and select the type Qt -> Qt resource file from within the dialog.
In your .pro file then appears a section:
RESOURCES += \
resources.qrc
In the resource file you need to add a prefix; just name it / (or leave it empty). Within this prefix you can add the file aks.ui so it will be named :/aks.ui.
Note that this type of UI creation takes place in runtime. That means, it is more flexible (maybe the ui file gets created only at runtime), but a little bit slower (since parsing and some more runtime processing takes place).
If you're new to Qt, you probably don't know that you can also let Qt create a class file for your ui file in the build process. This is already done when you list your ui file in the pro file in the FORMS += section.
To use the automatically built class, you should also have created a designer form class, which is another class where you put your own code inside. This class will load the automatically built class to setup your ui.
So there are two classes:
* The automatically generated class for your ui file, called Ui::Aks (in a namespace Ui), found in the file ui_aks.h in the build folder.
* Your own wrapper class; the acutal widget class, which uses the ui class.
If you want to create the second class manually, you can write (QtCreator actually does exactly this step when you add a designer form class instead of only a designer form):
aks.h:
#ifndef AKS_H
#define AKS_H
#include <QWidget>
namespace Ui {
class Aks; // <-- this is the automatically created ui class which we need
}
class aks : public QWidget // <-- your own widget class using the designer UI
{
Q_OBJECT
public:
explicit Aks(QWidget *parent = 0);
~Aks();
private:
Ui::Aks *ui; // <-- instance of the automatically created ui class
};
#endif // AKS_H
aks.cpp:
#include "aks.h"
#include "ui_aks.h" // <-- include the automatically generated ui class
Aks::Aks(QWidget *parent) :
QWidget(parent),
ui(new Ui::Aks)
{
ui->setupUi(this); // <-- here, the widgets from the ui file get created
}
Aks::~Aks()
{
delete ui;
}