OpenGL|ES 2.0 glClear command freeze until window state change - qt

My OpenGL|ES 2.0 glClear command freezes until the window state changes (eg. the window gets hidden or shown).
The target platform is ARM7 with a Mali 400 GPU.
All code is mostly copied from the Qt OpenGL ES Cube example.
What am I forgetting?
Leon
Source:
#include "streamplayer.h"
#include <QtOpenGL>
#include <QGLFunctions>
StreamPlayer::StreamPlayer(QWidget *parent) :
QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
program = new QGLShaderProgram();
}
StreamPlayer::~StreamPlayer()
{
}
void StreamPlayer::initializeGL()
{
qDebug() << "Initializing GL";
initShaders();
glClearColor(0.5f, 0.5f, 0.7f, 1.0f);
return;
}
void StreamPlayer::paintGL()
{
qDebug() << "Paint GL";
qDebug() << "Clearing buffers";
glClear(GL_COLOR_BUFFER_BIT);
qDebug() << "Never comes here until a window state change";
}
void StreamPlayer::resizeGL(int width, int height)
{
qDebug() << "Resizing GL to " << width << "x" << height;
glViewport(0, 0, width, height);
qDebug() << "Done resizing";
}
void StreamPlayer::initShaders()
{
qDebug() << "Initializing shaders";
setlocale(LC_NUMERIC, "C");
if(!program->addShaderFromSourceFile(QGLShader::Vertex, ":/shaders/vshader.glsl")) {
qDebug() << "Failed to create vertex shader";
}
if(!program->addShaderFromSourceFile(QGLShader::Fragment, ":/shaders/fshader.glsl")) {
qDebug() << "Failed to create fragment shader";
}
if(!program->link()) {
qDebug() << "Failed to link";
}
_gl_vertex = program->attributeLocation("vertex");
_gl_texCoord = program->attributeLocation("texCoord");
_gl_matrix = program->attributeLocation("matrix");
_gl_texture = program->attributeLocation("tex");
if(!program->bind()) {
qDebug() << "Failed to bind";
}
setlocale(LC_ALL, "");
qDebug() << "Shaders ready";
}

Have you kept the timer asking for frame updates ? It is this timer that is asking for openGL redraw by calling updateGL() on the glwidget, that ask for a (delayed) paintGL(). Otherwise paintGL will only be called when Qt estimates necessary (for example window shown).
QTimer *timer = new QTimer(this);
timer->setInterval(10);
QObject::connect(timer, SIGNAL(timeout()), glwidget, SLOT(updateGL()));
//And at the end of MainWindow initialization
timer->start();
See this SO thread for related question.

Related

Connect and read data automatically from BLE weight scale in Qt application

I'm developing a Qt app which need to read data from weight scale model and can't quite understand how exactly the Bluetooth Low Energy works and how to implement it in Qt.
I have a UC-352BLE weight scale which uses BLE to send data. What I want to achieve is this:
After initial pairing the scale with my Raspberry Pi on which my app is running, when the scale sends data (it does it automatically when you take a measurement), my app receive it. For example I have a blood pressure monitor which uses normal Bluetooth and here it's easy. In my app, I create a QBluetoothServer and call its listen() method. Then when the (already paired) device sends a measurement, it connects with my app automatically, then I create QBluetoothSocket and read the data. But with the scale and it's BLE it seems that you can't do it that way. I tried to follow the Qt documentation on this and right now I just have to manually press the button which connects to the scale when it's sending the data. And every time it connects to the device it discovers it's characteristics and services etc. Don't know if I can do it so the app automatically receives a connection and reads a data when the scale sends a measurement. And even when I try to connects like that sometimes it connects and sometimes don't (I get Unknown error form QLowEnergyController::Error when connecting). Here is what I already have:
Bletest::Bletest(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Bletest)
{
ui->setupUi(this);
if (localDevice.isValid()) {
localDevice.powerOn();
localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
connect(&localDevice, &QBluetoothLocalDevice::deviceConnected, this, &Bletest::deviceConnected);
connect(&localDevice, &QBluetoothLocalDevice::deviceDisconnected, this, &Bletest::deviceDisconnected);
connect(&localDevice, &QBluetoothLocalDevice::pairingFinished, this, &Bletest::pairingFinished);
}
discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &Bletest::addDevice);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &Bletest::scanFinished);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &Bletest::scanFinished);
discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
}
Bletest::~Bletest()
{
delete ui;
}
// Local device slots
void Bletest::deviceConnected(const QBluetoothAddress &address)
{
qDebug() << address.toString() << " connected";
}
void Bletest::deviceDisconnected(const QBluetoothAddress &address)
{
qDebug() << address.toString() << " disconnected";
}
void Bletest::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
{
}
// Agent slots
void Bletest::addDevice(const QBluetoothDeviceInfo &device)
{
if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
if (device.name().contains("352")) {
bleDevice = device;
qDebug() << "Found: " + device.name() + "\t" + device.address().toString();
}
}
}
void Bletest::scanFinished()
{
qDebug() << "Devices scan finished";
}
///////////
void Bletest::on_connectButton_clicked()
{
controller = QLowEnergyController::createCentral(bleDevice, this);
connect(controller, &QLowEnergyController::serviceDiscovered, this, &Bletest::serviceDiscovered);
connect(controller, &QLowEnergyController::discoveryFinished, this, &Bletest::serviceScanFinished);
connect(controller, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
this, [this](QLowEnergyController::Error error) {
qDebug() << "Cannot connect to device: " + QString::number(error);
});
connect(controller, &QLowEnergyController::connected, this, [this]() {
qDebug() << "Connected to device";
controller->discoverServices();
});
connect(controller, &QLowEnergyController::disconnected, this, [this]() {
qDebug() << "Disconnected";
});
controller->connectToDevice();
}
// Controller slots
void Bletest::serviceDiscovered(const QBluetoothUuid &gatt)
{
qDebug() << "Service discovered: " << gatt.toString();
if (gatt.toString().contains("0000181d-0000-1000-8000-00805f9b34fb")) {
service = controller->createServiceObject(QBluetoothUuid(QBluetoothUuid::WeightScale));
if (service) {
qDebug() << "Found weight scale service";
connect(service, &QLowEnergyService::stateChanged, this, &Bletest::serviceStateChanged);
connect(service, &QLowEnergyService::characteristicChanged, this, &Bletest::updateWeight);
connect(service, &QLowEnergyService::characteristicRead, this, &Bletest::updateWeight);
service->discoverDetails();
}
}
}
void Bletest::serviceScanFinished()
{
qDebug() << "Service scan finished";
}
////////////////////////////
// Service slots
void Bletest::serviceStateChanged(QLowEnergyService::ServiceState newState)
{
if (controller->state() == QLowEnergyController::UnconnectedState)
return;
if (newState == QLowEnergyService::DiscoveringServices) {
qDebug() << "Discovering services state";
} else if (QLowEnergyService::ServiceDiscovered) {
qDebug() << "Service discovered.";
const QLowEnergyCharacteristic weightChar = service->characteristic(QBluetoothUuid(QBluetoothUuid::WeightMeasurement));
if (!weightChar.isValid()) {
qDebug() << "Weight data not found";
return;
}
qDebug() << "Weight data found";
//service->readCharacteristic(weightChar);
desc = weightChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
if (desc.isValid()) {
qDebug() << "Writing descriptor";
service->writeDescriptor(desc, QByteArray::fromHex("0100"));
}
}
}
void Bletest::updateWeight(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
qDebug() << "Updating weight";
if (c.uuid() != QBluetoothUuid(QBluetoothUuid::WeightMeasurement))
return;
double weight = qFromLittleEndian<qint16>(value.mid(1, 2).data()) * RESOLUTION;
qDebug() << "New weight: " << value.mid(1, 2);
qDebug() << "New weight: " + QString::number(weight, 'f', 2);
}
///////////////////////////
Can someone point me in the right direction with this? Is is even possible with BLE to automatically connects to my app and receive data?
Thanks.

Reading from character device with Qt

I'm not very good at character devices, so I need your help. A have a char device(let's call it /dev/my_light) which is a light sensor. I have to read the data from this file and transform it to the brightness value and then pass it to the brightness manager that changes the brightness of my screen. The problem is that when I read the value for some period of time I get old values from the file.I assume there is a buffer(again not sure how character devices exactly work). Whereas when I use cat /dev/my_light I see new data! Is it possible to get rid off the buffer and read new values that were written to the file just right now. Here is my code in Qt:
void MySensor::updateMySensor()
{
Packet packet;
packet.startByte = 0;
packet.mantissa = 0;
packet.exp = 0;
d->device = ::open(d->path.toStdString().c_str(), O_RDONLY);
if (d->device == -1)
{
qDebug() << Q_FUNC_INFO << "can't open the sensor";
return;
}
ssize_t size = ::read(d->device, &packet, sizeof(packet));
close(d->device);
if (size == -1)
{
qDebug() << errno;
return;
}
packet.exp &= 0x0F;
float illumination = pow(2, packet.exp) * packet.mantissa * 0.045;
if(d->singleShot) emit lightSensorIsRunning(true);
emit illuminationRead(illumination);
}
The mySensor function is called every second. I tried to call it each 200 msec but it didn't help. The value of illumination stays old for about 7 seconds(!) whereas the value that I get from cat is new just immediately.
Thank you in advance!
I can't test with your specific device, however, I'm using the keyboard as a read only device.
The program attempts to connect to keyboard and read all keys pressed inside and outside the window. It's a broad solution you'll have to adapt to meet your demands.
Note that I'm opening the file with O_RDONLY | O_NONBLOCK which means open in read only mode and no wait for the event be triggered(some notifier needed to know when data is ready!) respectively.
You'll need super user privilege to run this example!
#include <QtCore>
#include <fcntl.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const char *device_name = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";
int descriptor = open(device_name, O_RDONLY | O_NONBLOCK);
if (descriptor < 0)
{
qDebug() << "Error" << strerror(errno);
return a.exec();
}
QFile device;
if (!device.open(descriptor, QFile::ReadOnly))
{
qDebug() << "Error" << qPrintable(device.errorString());
return a.exec();
}
QSocketNotifier notifier(device.handle(), QSocketNotifier::Read);
QObject::connect(&notifier, &QSocketNotifier::activated, &notifier, [&](int socket){
Q_UNUSED(socket)
struct input_event ev;
QByteArray data = device.readAll();
qDebug() << "Event caught:"
<< "\n\nDATA SIZE" << data.size()
<< "\nSTRUCT COUNT" << data.size() / int(sizeof(input_event))
<< "\nSTRUCT SIZE" << sizeof(input_event);
qDebug() << ""; //New line
while (data.size() >= int(sizeof(input_event)))
{
memcpy(&ev, data.data(), sizeof(input_event));
data.remove(0, int(sizeof(input_event)));
qDebug() << "TYPE" << ev.type << "CODE" << ev.code << "VALUE" << ev.value << "TIME" << ev.time.tv_sec;
}
qDebug() << ""; //New line
});
return a.exec();
}

QNetwork Reply from QNetworkAccessManager

The following code is from an example shows how to use QNetworkAccessManager to download things.
void Downloader::replyFinished (QNetworkReply *reply)
{
if(reply->error())
{
qDebug() << "ERROR!";
qDebug() << reply->errorString();
}
else
{
qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();
qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
QFile *file = new QFile("C:/Qt/Dummy/downloaded.txt");
if(file->open(QFile::Append))
{
file->write(reply->readAll());
file->flush();
file->close();
}
delete file;
}
reply->deleteLater();
}
My question is do we have to call reply->deleteLater(); here? If we don't call it, when we perform QNetworkAccessManager::get() call the second time, will the QNetworkReply* in the slot be the same QNetworkReply* ?
If you don't call deleteLater(), the QNetworkReply object will be leaked and its memory not freed. A second get() call will create a new QNetworkReply object.

QDialog show is does not happen immediately

I have a sever client application and in the beginning client loads data from server. I have a dialog showing status of getting data from server (has progress bar). But when I call the function the dialog appears with no contents in it with white background and suddenly changes to completed status.
void SystemScreen::loadServerData()
{
qDebug() << Q_FUNC_INFO << "Invoked";
if (NULL != mpDataManagerDlg)
{
qDebug() << Q_FUNC_INFO << "show progres screen";
mpDataManagerDlg->showScreen();
}
loadData();
qDebug() << Q_FUNC_INFO << "Exits";
}
void SystemScreen::loadData()
{
qDebug() << Q_FUNC_INFO << "Invoked";
if (NULL != mpDataManager)
{
mpDataManager->loadDataFromServer();
}
qDebug() << Q_FUNC_INFO << "Exits";
}
I feel that dialog is displayed only after loadData() function is completed. Is there any alternative to do this?
I used a timer to start
QTimer::singleShot(100, this, SLOT(loadData()));
But then I have some trouble in getting data. ie data is empty if I read suddenly.
EDIT:
void DataManagerDialog::setDefault()
{
qDebug() << Q_FUNC_INFO << "Invoked";
setProgressBar(0);
setProgressBarColor(false);
ui->deptFailButton->hide();
ui->deptOkButton->hide();
ui->deptLabel->setStyleSheet("color:gray");
ui->subGroupFailButton->hide();
ui->subGroupOkButton->hide();
ui->subGroupLabel->setStyleSheet("color:gray");
ui->itemFailButton->hide();
ui->itemOkButton->hide();
ui->itemLabel->setStyleSheet("color:gray");
ui->salesBtnFailButton->hide();
ui->salesBtnOkButton->hide();
ui->salesBtnLabel->setStyleSheet("color:gray");
qDebug() << Q_FUNC_INFO << "Exits";
}
void DataManagerDialog::alignCenter()
{
qDebug() << Q_FUNC_INFO << "Invoked";
QWidget *par = parentWidget();
if (par)
{
int x = width()/2;
int y = height()/2;
QPoint mid(mapToGlobal(QPoint(x, y)));
int px = par->width()/2;
int py = par->height()/2;
QPoint parMid(mapToGlobal(QPoint(px, py)));
move(parMid.x()-mid.x(), parMid.y()-mid.y());
}
qDebug() << Q_FUNC_INFO << "Exits";
}
void DataManagerDialog::showScreen()
{
setDefault();
alignCenter();
show();
}
You probably do not enter the event loop.
Try to call QCoreApplication::processEvents() from time to time in mpDataManager->loadDataFromServer() to update the GUI.
From the processEvents documentation :
You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
Edit added after getting feedback from the comments
A better approach would be to send signals in your loadDataFromServer() method with the status information and have a slot listen to the signal and update the GUI.
Here a prototype illustrating the idea :
void mpDataManagerDlg::loadDataFromServer() {
while(true) {
// do some work
int progress = // some value
emit updateDialogSignal(progress);
}
}
// in your dialog class
public slots:
void DataManagerDialog::updateDialog(int progress) {
// update gui
}
More about signals and slots can be found here.

Using OpenGL Shader on a QGraphicsView

I am trying to use an OpenGL Shader on a QGrapicsView.
My graphic card is a NVidia GeForce GT 425M. I made my experiments on a Ubuntu 12.04 with the NVidia driver 295.40.
I Create a QMainWindow containing two QGraphicsView, each graphics view are displaying the same scene. On One of the Graphics View, I setup an OpenGL View port.
The OpenGL View Port is a QGLWidget I had overloaded with the loading of the vertex and fragment shader programs.
Look at my QMainWindow constructor :
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_scene = new QGraphicsScene(this);
ui->gvShaded->setScene(m_scene);
ui->gvUnshaded->setScene(m_scene);
m_glWidget = new QGLScene();
ui->gvShaded->setViewport(m_glWidget);
m_glWidget->updateGL();
if(m_glWidget->context()->isValid())
{
qDebug()<<"GL Context is valid";
}else
{
qWarning()<<"GL Context is not valid";
}
qDebug() << "Opengl Version : " << m_glWidget->context()->format().openGLVersionFlags();
QBrush brush(QLinearGradient(0,0,20,0));
brush.setColor(Qt::blue);
QGraphicsRectItem* rect = m_scene->addRect(0,0,20,20,QPen(Qt::black), brush);
ui->gvShaded->ensureVisible(rect);
ui->gvUnshaded->ensureVisible(rect);
}
As you can see I instantiate the object QGLScene which is :
class QGLScene : public QGLWidget
{
public:
QGLScene(QWidget* _parent=0);
protected :
virtual void initializeGL();
};
My GLWidget Construction is :
QGLScene::QGLScene(QWidget *_parent)
:QGLWidget(QGLFormat(QGL::DepthBuffer), _parent)
{
}
And definition of initializeGL is :
void QGLScene::initializeGL()
{
qDebug() << __PRETTY_FUNCTION__;
QGLShader fragmentShader(QGLShader::Fragment, context(), this);
QGLShader vertexShader(QGLShader::Vertex, context(), this);
QGLShaderProgram pgm(context(), this);
if(vertexShader.compileSourceFile("./vertexShader.glsl"))
{
if(fragmentShader.compileSourceFile("./fragmentShader.glsl"))
{
pgm.addShader(&fragmentShader);
pgm.addShader(&vertexShader);
pgm.link();
pgm.bind();
qDebug() << "Shader bien chargé : "<< pgm.log();
}else
{
qDebug() << "Problem when loading fragment shader :";
qDebug() << fragmentShader.log();
qDebug() << QString(fragmentShader.sourceCode());
}
}else
{
qDebug() << "Problem when loading vertex shader :";
qDebug() << vertexShader.log();
qDebug() << QString(vertexShader.sourceCode());
}
}
My shaders are very useless shaders :
Vertex :
#version 150
uniform mat4 viewMatrix, projMatrix;
in vec4 position;
in vec3 color;
out vec3 Color;
void main()
{
Color = color;
gl_Position = projMatrix * viewMatrix * position ;
}
Fragment :
#version 150
in vec3 Color;
out vec4 outputF;
void main()
{
outputF = vec4(1.0, 0, 0, 1.);
}
So I attempt to have my Shaded Graphics view as a big red square because I think I enforced the pixels to be red (in my fragment shader).
I have no shader compilation error, but my two scene are the same.
Of course, the program pass into the initializeGL function (I can read the PRETTY_FUNCTION).
Does some one have an explanation ?

Resources