PDA

View Full Version : Problems with QPainter



franco.amato
21st November 2009, 10:42
Hi to all, I'm writing an audio editor. I have to draw the waveform.
The paint event of my wavewidget is so done:


/************************************************** *************/
/* Paint event */
/************************************************** *************/
void WaveWidget::paintEvent( QPaintEvent* pe )
{
// I call updateWave only if necessary if not I draw what is in cache
if( m_waveCachePixmap.isNull() )
{
updateWave();
}

QPainter p( this );
p.setRenderHint( QPainter::Antialiasing );
p.drawPixmap( 0, 0, m_waveCachePixmap );
}

where the updateWave() is so done:


/************************************************** **********************/
/* updateWave */
/* call this only when the wave is changed, */
/************************************************** **********************/
void WaveWidget::updateWave()
{
int h = height();
int w = width();

// I want another width and height here.
// Also, you might to clear the cache at resize events
QPixmap temp = QPixmap( w, h );

QPainter p( &temp );
p.fillRect( temp.rect(), Qt::white );
p.setRenderHint( QPainter::Antialiasing );


QPen pen( Qt::blue, 1 ); // blue solid line, 1 pixels wide
p.setPen( pen );

// Draw the waveform or no data thingie
if( m_wave )
{
unsigned int numSamples = m_wave->getNumSamples();
int channels = m_wave->getNumChannels();
FMOD_SOUND_TYPE soundType = m_wave->getSoundType();
FMOD_SOUND_FORMAT soundFormat = m_wave->getSoundFormat();

int sampleSize = channels * ( soundFormat == PCM8 ) ? 1 : 2;

//char* stream = sound->stream;
void* soundStream = m_wave->getSoundStream();
int maxHeight = (h / 2) / channels;
int increment = (h / 2);

switch( soundFormat )
{
case PCM8: //8 bits mono
{
qDebug("PCM8 sound file");
char* audio = reinterpret_cast<char*>(soundStream);
for( unsigned int i = 0; i < numSamples ; i++ )
{
char* current = (audio) + (i * sampleSize);
char audio = *current;
float reproc = audio / (float)(1 << 8);
//DrawLine( i, 0, i, height * reproc );
//QLine line( x, startY, x, startY + Y);
//p.drawLine(line);
//startY += increment;
}
break;
}
case PCM16: //16 bits stereo (LRLRLRLR...LRLR)
{
qDebug("PCM16 sound file");
short* audio = reinterpret_cast<short*>(soundStream);
for( unsigned int i = 0; i < numSamples; i++ )
{
int startY = maxHeight;
for( int k = 0; k < channels; k++ )
{
//short* value1 = reinterpret_cast<short*>(stream + k * 2);
//short* value1 = audio + k * 2;
short* value1 = audio + k;
int x = i / ( numSamples / w);//?
int y = *value1 * maxHeight / 0x0000FFFF * 2;
QLine line( x, startY, x, startY + y );
//std::cout << "Drawing at x: " << x << "startY: " << startY << "y: " << y << "\n";
p.drawLine(line);
startY += increment;
}
//std::cout << audio[i] << "\n";
audio += sampleSize;
}
break;
}

default:
{
qDebug("Undefined sound type");
break;
}
}
}
else
{
// Inform the user that no file has been loaded - perhaps using a
// no-data icon or something
qDebug("No data");
}

m_waveCachePixmap = temp;
}

I have some problems:
1) It doesn't draw nothing
2) I get the follow strange errors that with my poor experience in Qt I can not solve:

QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::setRenderHint: painter must be active to set rendering hints
QPainter::setPen: Painter not active
3) The step of drawing ( that doesn't work ) blok the application.

I hope to get help.
Best Regards

wysota
21st November 2009, 12:55
Please provide a minimal compilable example reproducing the problem.

franco.amato
21st November 2009, 16:27
How can I give you a compilable project?
I send to your private mail? Give me your mail please.

Franco

wysota
21st November 2009, 16:35
Attach it to your post. Make sure it is a minimal and compilable example. I don't want your whole project.

franco.amato
21st November 2009, 17:17
Hi,at the moment my project is at a minimal level :-)
I'll try to attach the minimal of the minimal



// the main
int main(int argc, char* argv[])
{
QApplication app( argc, argv );
MainWindow mainWin;
mainWin.showMaximized();
// init audio system
QTimer::singleShot( 1, AudioDevice::getInstance(), SLOT(init() ) );
return app.exec();
}

I create a QMainWindow containing a centralWidget


#ifndef __MAINWINDOW_H__
#define __MAINWINDOW_H__

/* system include */
#include <iostream>
#include <vector>
/* qt include */
#include <QMainWindow>
/* interfaces include */
#include "CentralWidget.h"
#include "WaveWidget.h"

QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow( QWidget *parent = 0, Qt::WindowFlags flags = 0 );

protected:
virtual void closeEvent(QCloseEvent *event);
virtual void paintEvent(QPaintEvent *event);

private slots:
void openAudioFile();
void about();

private:
void createActions();
void createMenus();
void createStatusBar();

/* Menus */
QMenu* fileMenu;
QMenu* helpMenu;
/* Actions */
QAction* openAct;
QAction* exitAct;
QAction* aboutAct;
QAction* aboutQtAct;

CentralWidget* mp_cW;
};

#endif //__MAINWINDOW_H__

I post only some relevant methods of MainWindow and of the rest of the classes:

MainWindow::MainWindow( QWidget *parent, Qt::WindowFlags flags )
: QMainWindow(parent, flags)
{
setObjectName("MainWindow");
setWindowTitle("ECP Studio");

createActions();
createMenus();
createStatusBar();

mp_cW = new CentralWidget( this );
setCentralWidget( mp_cW );
}

// I pass the audio file to the centralWidget
void MainWindow::openAudioFile()
{
QString fileName = QFileDialog::getOpenFileName( this );
if( !fileName.isEmpty() )
{
mp_cW->setSoundFile( fileName );
}
}

The centralwidget:

#include "WaveWidget.h"

class CentralWidget : public QWidget
{
public:
// ctor
CentralWidget( QWidget* parent = 0 );
// set the path of the sound file
void setSoundFile( QString soundName_ );

protected:
// override paintEvent
virtual void paintEvent( QPaintEvent * );

private:
QVBoxLayout* mp_vBox;
WaveWidget* mp_wave;
QString m_soundName;
};

#endif //__CENTRALWIDGET_H__

Again only some methods:


CentralWidget::CentralWidget( QWidget* parent /* = 0 */ )
: QWidget(parent),
mp_vBox( 0 ),
mp_wave( 0 )
{
/*vertical layout */
QVBoxLayout *layout = new QVBoxLayout;
/* waveform visualizer */
mp_wave = new WaveWidget( this );
layout->addWidget(mp_wave);
setLayout(layout);
}

void CentralWidget::setSoundFile( QString soundName_ )
{
m_soundName = soundName_;
mp_wave->setSoundFile( m_soundName );
}

Now the waveWidget, where I would display the waveform. Its a child of the centralwidget


#include <QWidget>
#include <QMouseEvent>

#include "SoundData.h"

class WaveWidget : public QWidget
{
Q_OBJECT

public:
WaveWidget( QWidget* parent = 0 );
void setSoundFile( QString soundName_ );
void setWave( SoundData* );

protected:
virtual void paintEvent( QPaintEvent* event );
virtual void resizeEvent ( QResizeEvent* event );
virtual void mousePressEvent( QMouseEvent *event );
virtual void mouseMoveEvent( QMouseEvent *event );
virtual void mouseReleaseEvent( QMouseEvent *event );

private:
void updateWave();

QPixmap m_waveCachePixmap;
SoundData* m_wave;
};

#endif //__WAVEWIDGET_H__

It contain SoundData that's a wrapper around the sound data.
Again only some relevant methods:


/************************************************** **********************/
/* Constructor */
/************************************************** **********************/
WaveWidget::WaveWidget( QWidget* parent /* = 0 */ )
: QWidget( parent ),
m_wave( 0 )
{
}

/************************************************** **********************/
/* setSoundFile */
/************************************************** **********************/
void WaveWidget::setSoundFile( QString soundName_ )
{
m_wave = new SoundData();
m_wave->loadSoundData( soundName_ );
setWave( m_wave );
}

/************************************************** **********************/
/* setWave */
/************************************************** **********************/
void WaveWidget::setWave( SoundData* w )
{
m_wave = w;
m_waveCachePixmap = QPixmap(); // Empty the cache as the wave data has changed

int samples = m_wave->getNumSamples();
resize( samples, 500 ); // NEW NEW NEW

update(); // This triggers a paint event later on, lets Qt decide
// how and when (if your widget is hidden, etc)
}

/************************************************** **********************/
/* updateWave */
/************************************************** **********************/
void WaveWidget::updateWave()
{
int h = height();
int w = width();

// I want another width and height here.
// Also, you might to clear the cache at resize events
QPixmap temp = QPixmap( w, h );

QPainter p( &temp );
p.fillRect( temp.rect(), Qt::white );
p.setRenderHint( QPainter::Antialiasing );


QPen pen( Qt::blue, 1 ); // blue solid line, 1 pixels wide
p.setPen( pen );

// Draw the waveform or no data thingie
if( m_wave )
{
unsigned int numSamples = m_wave->getNumSamples();
int channels = m_wave->getNumChannels();
FMOD_SOUND_TYPE soundType = m_wave->getSoundType();
FMOD_SOUND_FORMAT soundFormat = m_wave->getSoundFormat();

int sampleSize = channels * ( soundFormat == PCM8 ) ? 1 : 2;

//char* stream = sound->stream;
void* soundStream = m_wave->getSoundStream();
int maxHeight = (h / 2) / channels;
int increment = (h / 2);

switch( soundFormat )
{
case PCM8: //8 bits mono
{
qDebug("PCM8 sound file");
char* audio = reinterpret_cast<char*>(soundStream);
for( unsigned int i = 0; i < numSamples ; i++ )
{
char* current = (audio) + (i * sampleSize);
char audio = *current;
float reproc = audio / (float)(1 << 8);
//DrawLine( i, 0, i, height * reproc );
//QLine line( x, startY, x, startY + Y);
//p.drawLine(line);
//startY += increment;
}
break;
}
case PCM16: //16 bits stereo (LRLRLRLR...LRLR)
{
qDebug("PCM16 sound file");
short* audio = reinterpret_cast<short*>(soundStream);
for( unsigned int i = 0; i < numSamples; i++ )
{
int startY = maxHeight;
for( int k = 0; k < channels; k++ )
{
//short* value1 = reinterpret_cast<short*>(stream + k * 2);
short* value1 = audio + k * 2;
//short* value1 = audio + k;
int x = i / ( numSamples / w);//?
int y = *value1 * maxHeight / 0x0000FFFF * 2;
QLine line( x, startY, x, startY + y );

p.drawLine(line);
startY += increment;
}
//std::cout << audio[i] << "\n";
audio += sampleSize;
}
break;
}

default:
{
qDebug("Undefined sound type");
break;
}
}
}
else
{
// Inform the user that no file has been loaded - perhaps using a
// no-data icon or something
qDebug("No data");
}

m_waveCachePixmap = temp;
}

/************************************************** **********************/
/* Paint event */
/************************************************** **********************/
void WaveWidget::paintEvent( QPaintEvent* pe )
{
// chiamo la updateWave solo se e' necessario se no disegno quella in cache
if( m_waveCachePixmap.isNull() )
{
updateWave();
}

QPainter p( this );
p.setRenderHint( QPainter::Antialiasing );
p.drawPixmap( 0, 0, m_waveCachePixmap );
}

/************************************************** **********************/
/* Resize event */
/************************************************** **********************/
void WaveWidget::resizeEvent ( QResizeEvent * event )
{
m_waveCachePixmap = QPixmap(); // I invalidate the cache
update();
event->accept();
}

I hope is not too much code. But with less code I don't think how you can help me.
Let me know if you would also have the soundData class, is not a problem.

I really hope you can help me.
Best Regards,
Franco

wysota
21st November 2009, 22:45
I'm sorry but in my view, "minimal" can't be longer than 30 lines. If you send me your code, you won't find the problem, because the point of asking for a minimal compilable example is for you to sit down, take a look at your code, cut out the parts which you think are relevant to the problem and build a separate application out of this. In many cases it occurs that the new app is working correctly which suggests the error is in some other part of your app. Get rid of all the actions and events, leave only the crucial part related to the paint event.

franco.amato
22nd November 2009, 18:01
I'm sorry but in my view, "minimal" can't be longer than 30 lines. If you send me your code, you won't find the problem, because the point of asking for a minimal compilable example is for you to sit down, take a look at your code, cut out the parts which you think are relevant to the problem and build a separate application out of this. In many cases it occurs that the new app is working correctly which suggests the error is in some other part of your app. Get rid of all the actions and events, leave only the crucial part related to the paint event.

Dear wysota thank you for your help but I still don't understand.
I only have 2 actions more ( Close the app, and show about ) that I don't sent.
Did you try to load an audio file? And it work? So I definitively don't understand. What can I do to solve my problem? I really hope you can give to me some suggestions.

Best Regards,
Franco

wysota
22nd November 2009, 18:34
I didn't build your app. It's too tedious for me to copy & paste so many different files. It would really be better if you got rid of everything apart the relevant code. I.e. I don't see a point of the SoundData class being part of this example.

franco.amato
22nd November 2009, 22:24
I didn't build your app. It's too tedious for me to copy & paste so many different files. It would really be better if you got rid of everything apart the relevant code. I.e. I don't see a point of the SoundData class being part of this example.

Hi wysota,
I'll create only the wavewidhet ( without centralWidget nor QMainWindow ) and I'll try to draw some random values instead of sound pcm samples and past the code here.

Best Regards,
Franco

wysota
22nd November 2009, 23:45
Great. Try to fit it into those 30 lines. You don't even have to paint anything as you are getting errors before any painting is done - just initialize the painter.

franco.amato
23rd November 2009, 21:43
Great. Try to fit it into those 30 lines. You don't even have to paint anything as you are getting errors before any painting is done - just initialize the painter.

Dear wysota. I solved the problem that gave the error. Is the line:

resize(wave->getSamples(), 200);
This did that the waveWidget was expanded ( in the x direction ) more that the mainWindow because the number of audio samples I have is ~10E6.
So I tried to attach the waveWidget to a QScrollArea without success so:


CentralWidget::CentralWidget( QWidget* parent /* = 0 */ )
: QWidget(parent),
mp_vBox( 0 ),
mp_wave( 0 )
{
/* vertical layout */
mp_vBox = new QVBoxLayout( this );
/* scroll area */
scrollArea = new QScrollArea();
/* waveform visualizer */
mp_wave = new WaveWidget( this );
scrollArea->setWidget( mp_wave );
mp_vBox->addWidget( scrollArea );
setLayout( mp_vBox );
}

This did that now I can't see the waveWidget.
Where my code is wrong?

Best Regards

wysota
23rd November 2009, 21:54
Call QScrollArea::setWidgetResizable() with true as the parameter and see if it helps. In general you should reimplement sizeHint() for your wave widget.