make a QGraphicsItemGroup with two QGraphicsSimpleTextItem-s? - linux

Assume I want to display in some QGraphicsScene a simple genealogy tree. Each person there has exactly a first name and a last name (and nothing else displayed).
For each person there, I want to build a QGraphicsItemGroup made of two QGraphicsSimpleTextItem-s vertically aligned and centered. One for first names of persons, one for their family names. Each having its own font & color. I don't want to use a heavy QGraphicsTextItem because it is too heavy (a person name with first and last names only don't deserve a full-fledged QTextDocument).
So I was thinking of
struct Person {
QGraphicsItemGroup _group;
QGraphicsLinearLayout _lay;
QGraphicsSimpleTextItem _firstname;
QGraphicsSimpleTextItem _lastname;
public:
Person(QGraphicsScene*scene,
const std::string& first, const std::string& last)
: group(), _lay(Qt::Vertical),
_firstname(first.c_str()), _lastname(last.c_str()) {
_lay.addItem(&_firstname);
_lay.setSpacing(0, 5);
_lay.addItem(&_lastname);
_group.addToGroup(&_firstname);
_group.addToGroup(&_lastname);
scene->addItem(&_group);
};
};
but this don't work because _lay.addItem(&_firstname); don't compile, since a QGraphicsSimpleTextItem is not a QGraphicsLayoutItem
Any hints? Or is my entire approach wrong?
Should I define a class inheriting both from QGraphicsSimpleTextItem and from QGraphicsLayoutItem ?
NB: the actual code (GPlv3 licensed) is in my basixmo project on github, file guiqt.cc, commit 99fd6d7c1ff261. It is not a genealogy project, but an abstract syntax tree editor like thing for an interpreter with persistence. The concerned class is BxoCommentedObjrefShow; instead of a first name, I am showing an id like _7rH2hUnmW63o78UGC, instead of the last name, I am showing a short comment like body of test1_ptrb_get. The basixmo project itself is a tentative rewrite of melt-monitor-2015 in C++11 & Qt5

It seem you supposed a purpose to the QGraphicsLinearLayout which is not its real purspose:
From Official QT 5.7 documentation
The QGraphicsLinearLayout class provides a horizontal or vertical layout for managing widgets in Graphics View
It is not supposed to layout normal drawing items in you scene, but QWidgets. Unfortunately, the documentation and examples about it seem not to work.
How to implement vertical layout
Fortunately, the QGraphicsItem is not difficult to extends, so you may implement your pair of text in few lines:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsSimpleTextItem>
#include <QGraphicsItemGroup>
class QGraphicsPerson: public QGraphicsItemGroup
{
public:
QGraphicsPerson( const QString& firstStr,
const QString& secondStr,
QGraphicsItem *parent=nullptr)
{
first.setText(firstStr);
second.setText(secondStr);
addToGroup(&first);
addToGroup(&second);
second.setPos(first.pos().x(), first.pos().y()+first.boundingRect().height());
}
protected:
QGraphicsSimpleTextItem first, second;
};
int main(int n, char **args)
{
QApplication app(n, args);
QGraphicsScene scene;
QGraphicsView window(&scene);
QGraphicsPerson person( "Adrian", "Maire");
scene.addItem(&person);
person.setPos(30,30);
QGraphicsPerson person2( "Another", "Name");
scene.addItem(&person2);
window.show();
return app.exec();
}
Note: Scene space extends to the infinite in both direction, so space management is very simplified (no minimum/maximum sizes, stretching, etc.)

Related

Signals in Linux

I am studying signals from oreilly book. I came across this.
#include <signal.h>
typedef void (*sighandler_t)(int);----> func ptr returns void. uses typedef
sighandler_t signal (int signo, sighandler_t handler);
Later on in code. He just uses
void sigint_handler (int signo)----> normal function returning void
{
}
can typedef be applied on functions
I want to know how it works
can typedef be applied on functions
Yes.....
I want to know how it works
As the example you have read - the syntax is rather obscure (after 25 years of C I still have to think about it), but it is quite straight forward. Passing and storing pointers to functions is greatly simplified if you use typedefs.
I suggest either take a detour and learn about pointers to functions and typedefs of them, or take it as read for now and return to pointers to function later, as you cannot be a C programmer and avoid them.
A signal is just like a interrupt, when it is generated by user level, a call is made to the kernel of the OS and it will action accordingly. To create a signal, here I just show you an example
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
void sig_handler1(int num)
{
printf("You are here becoz of signal:%d\n",num);
signal(SIGQUIT,SIG_DFL);
}
void sig_handler(int num)
{
printf("\nHi! You are here becz of signal:%d\n",num);
}
int main()
{
signal(SIGINT,sig_handler1);
signal(SIGQUIT,sig_handler);
while(1)
{
printf("Hello\n");
sleep(2);
}
}
after running this code if you will press Ctrl+C then a message will show - "You are here becoz of signal:2" instead of quiting a process as we have changed a signal according to our action. As, Ctrl+C is a maskable signal.
To know more anbout signals and types of signals with examples please follow the link :
http://www.firmcodes.com/signals-in-linux/

QAudioInput record sounds failed

For my purpose, I want to use Qt5.1 to record sounds in WAV format, 16000Hz, 16bit and 1 channel, but the sounds are all 32bit by default. So I must find a class that can set "Bit Size" and the class is QAudioFormat for there's a function setBitSize() in the class. So I can no longer use QAudioRecorder class for it can not take QAudioFormat as parameter but QAudioInput do. And I use QAudioInput to record sounds with the code below:
#include<QAudioFormat>
#include<QAudioInput>
#include<QString>
#include<QFile>
#include<QDebug>
int main()
{
QFile output;
output.setFileName("record.raw");
output.open(QIODevice::WriteOnly);
QAudioFormat settings;
settings.setCodec("audio/PCM");
settings.setSampleRate(16000);
settings.setSampleSize(16);
settings.setChannelCount(1);
settings.setByteOrder(QAudioFormat::LittleEndian);
settings.setSampleType(QAudioFormat::UnSignedInt);
QAudioInput *audio=new QAudioInput(settings);
audio->start(&output);
sleep(3);
audio->stop();
output.close();
delete audio;
return 0;
}
Well, after the program ran, the record.wav was still empty. I have successfully recorded the sounds using QAudioRecorder, and the only different is the QAudioRecorder class has setAudioInput() function (ie. "audio->setAudioInput("alsa:default");). So I think maybe it's the point of the problem, but QAudioInput has no function like this. That's my problem, maybe you can give my some advice and Thanks a lot:-)
I'm glad to have found someone with the same issue as mine. I've been trying to record from a microphone with QAudioRecorder but with a different sample size for a few days already. Thanks to your example I've succeeded by getting rid of QAudioRecorder. So it's my turn to help you.
I think while the program is in the sleep function it's not recording anymore. You need to use the concept of signal and slots provided by Qt to to record while the timer is running.
#include "AudioInput.h"
void AudioInput::setup(){
output.setFileName("record.raw");
output.open(QIODevice::WriteOnly);
QAudioFormat settings;
settings.setCodec("audio/PCM");
settings.setSampleRate(16000);
settings.setSampleSize(16);
settings.setChannelCount(1);
settings.setByteOrder(QAudioFormat::LittleEndian);
settings.setSampleType(QAudioFormat::UnSignedInt);
audio=new QAudioInput(settings);
audio->start(&output);
QTimer::singleShot(3000, this, SLOT(terminateRecording()));
}
void AudioInput::terminateRecording(){
audio->stop();
output.close();
delete audio;
}
I put your code in one class called AudioInput and the only difference is that I replaced sleep(3000) by QTimer::singleShot(3000, this, SLOT(terminateRecording()));. Contrary to sleep this function won't freeze the program during 3s but will just send a signal to terminateRecording() at the end of the time.
Here is the rest of the code:
int main(int argc, char** argv){
QCoreApplication app(argc,argv);
AudioInput t;
t.setup();
app.exec();
return 0;
}
and the header:
class AudioInput : public QObject{
Q_OBJECT
public Q_SLOTS:
void terminateRecording();
public:
void setup();
private:
QAudioInput *audio;
QFile output;
};
so basically the problem you seem to have is that the backend does not support the settings that you try to push into the QAudioInput. Luckily Qt has a way of getting the nearest usable format and here's hot to set it:
void AudioInput::setup(){
output.setFileName("record.raw");
output.open(QIODevice::WriteOnly);
QAudioFormat settings;
settings.setCodec("audio/PCM");
settings.setSampleRate(16000);
settings.setSampleSize(16);
settings.setChannelCount(1);
settings.setByteOrder(QAudioFormat::LittleEndian);
settings.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(settings)) {
settings = info.nearestFormat(settings); // This is the magic line
settings.setSampleRate(16000);
qDebug() << "Raw audio format not supported by backend. Trying the nearest format.";
}
audio=new QAudioInput(settings);
audio->start(&output);
QTimer::singleShot(3000, this, SLOT(terminateRecording()));
}

QScrollArea does not scroll to maximum after widgets have been added

My setup looks like this:
Main Window
|- QHBoxLayout
|- Some Widgets
|- QScrollArea
| |- QHBoxLayout
| |- QGridLayout
| |- Widgets dynamically loaded in at runtime
| |- QVBoxLayout
|- Some Widgets
After I added in Widgets I want the Scroll Area to completely scroll down.
I did this by:
this->ui->gridLayout_table->addWidget(/*Widget*/); // this works perfectly nice
this->ui->gridLayout_table->addWidget(/*Widget*/); // this also works perfectly nice
this->ui->scrollArea->verticalScrollBar()->setValue(this->ui->scrollArea->verticalScrollBar()->maximum());
At the last command it scrolls to maximum minus the height of the newly added widgets. Is there any way to flush the changes before scrolling?
Thanks in advance
Lukas
Python version for the rangeChanged method:
scroll_area = QtGui.QScrollArea()
scroll_bar = scroll_area.verticalScrollBar()
scroll_bar.rangeChanged.connect(lambda: scroll_bar.setValue(scroll_bar.maximum()))
By connecting the rangeChanged method to the scrolldown, it ensures that each time there is a change in range the scrollbar will scroll down to the max.
Source
I've had the same problem. I found a work-around, which might be useful for you as well.
Using scrollBar->setValue(scrollBar->maximum()); does not work when being used right after having added widgets to the scrolled area. The maximum value of the scroll bar simply is not updated yet.
However it does work if you have a button that the user has to click just to adjust the scroll bar's position to the maximum. It seems that the scroll area notifies its scroll bar asynchronously (through signal) only. So I simply added a hidden button pb_AdjustScrollBar to my widget and simulated a click, using the animateClick(int) method after having added widgets to the scrolled area. Once the signal clicked(bool) is received for the AdjustScrollBar button, I then trigger the scrollBar to use the maximum position with:
scrollBarPtr->triggerAction(QAbstractSlider::SliderToMaximum);
Notes: the animateClick timeout must be set to something like 100ms (default). Putting the timeout much shorter has caused the scrollBar not to update to its real maximum. However I guess 100ms is fast enough for user interactions anyhow.
Still I would be curious to know, how this issue can be solved in a smarter way.
I found out the way. After add a widget put:
scroll->widget()->resize(scroll->widget()->sizeHint());
qApp->processEvents();
One workaround is to use the rangeChanged(int,int) signal of the vertical scroll bar.
#include <QWidget>
#include <QBoxLayout>
#include <QScrollArea>
#include <QScrollBar>
class myClass
{
Q_OBJECT
public:
myClass();
private:
QScrollArea *scrollArea;
int scrollBarMax = 0;
private slots:
void scrollToBottom(int min, int max);
};
myClass::myClass()
{
QVBoxLayout *myLayout = new QVBoxLayout;
QWidget *myWidget = new QWidget;
myWidget->setLayout(myLayout);
scrollArea = new QScrollArea;
scrollArea->setWidget(myWidget);
scrollArea->setWidgetResizeable(true);
QObject::connect(scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(scrollToBottom(int,int)));
}
void myClass::scrollToBottom(int min, int max)
{
(void) min;
if (max > scrollBarMax) scrollArea->verticalScrollBar()->setValue(max);
scrollBarMax = max;
}
Hm, I don't have an answer and the same problem. I however can supply a small reproducing example:
#include <QtGui>
class TestWidget : public QWidget
{
Q_OBJECT
public:
TestWidget(QWidget * parent = 0);
private slots:
void addButton();
private:
QScrollArea * scrollArea;
QWidget * contents;
QVBoxLayout * contentsLayout;
};
TestWidget::TestWidget(QWidget * parent)
: QWidget(parent)
{
QVBoxLayout * layout = new QVBoxLayout(this);
scrollArea = new QScrollArea(this);
layout->addWidget(scrollArea);
contents = new QWidget(this);
scrollArea->setWidget(contents);
contents->resize(300,400);
scrollArea->setWidgetResizable(true);
contentsLayout = new QVBoxLayout(contents);
for (int i = 0; i < 10; ++i)
{
QPushButton * button = new QPushButton(QString("button %1").arg(i), this);
connect(button, SIGNAL(clicked()), this, SLOT(addButton()));
contentsLayout->addWidget(button);
}
}
void TestWidget::addButton()
{
QPushButton * button = new QPushButton("button", this);
connect(button, SIGNAL(clicked()), this, SLOT(addButton()));
contentsLayout->addWidget(button);
QScrollBar * scrollBar = scrollArea->verticalScrollBar();
scrollBar->setValue(scrollBar->maximum());
}
int main(int argc, char * argv[])
{
QApplication app(argc, argv);
TestWidget widget;
widget.show();
app.setQuitOnLastWindowClosed(true);
return app.exec();
}
#include "main.moc"
The problem occurs both with Qt 4.8 and 5.2 (needs minor modifications of the example).
Had the same problem, here is what does the manual say:
"In such cases, setting the layout's size constraint property to one which provides
constraints on the minimum and/or maximum size of the layout (e.g.,
QLayout::SetMinAndMaxSize) will cause the size of the scroll area to be updated
whenever the contents of the layout changes."
In my case QScrollArea's contents size was changed by setMinimumSize method of a QScrollArea's widget, so i simply added ui->scroll_area->widget()->layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); and it started to work fine.
Important: after resizing the QScrollArea's widget, you should change QScrollBar's value inside of a custom event handler. Simple example:
bool MyCustomClassWithScrollArea::event(QEvent * event)
{
switch (event->type())
{
case MyCustomScrollToEndEvent::ScrollToEndEventType:
{
ui->scroll_area->verticalScrollBar()->setValue( ui->scroll_area->verticalScrollBar()->maximum() ); //scrolls to the end
return true;
}
}
return QWidget::event(event);
}

where do i include the string library in my C++ header file

My simple class won't compile in Visual Studio. It worked before I added the string company member and the getter method getCo() to it. I think I need to put #include the string standard library somewhere but I am not sure where. Any idea where? In my header file, I have:
#pragma once
#ifndef ENGINEER_H_
#define ENGINEER_H_
class engineer {
int years;
string company;
public:
engineer(int years);
~engineer(void);
int getYears();
string getCo();
};
#endif ENGINEER_H_
And in my CPP file for the definition of the class, I have:
#include "StdAfx.h"
#include "engineer.h"
engineer::engineer(int y, string c){
years = y;
company = c;
}
engineer::~engineer(void) {
}
int engineer::getYears() {
return years;
}
string engineer::getCo() {
return company;
}
Put it in the header file, and prefix your usage of string with the namespace std.
Header:
#include <string>
class engineer
{
std::string company;
};
In the implementation file (.cpp) you can prefix the names or have a using directive.
Implementation:
using namespace std; // using directive, no longer need to use std::
Avoid putting the using directive in a header file, as that pollutes the global namespace and can cause problems with naming collisions in other libraries you may wish to use.
Put it in the header file, after the include guards:
#include <string>
using std::string;
This way, it will also be available for your cpp file, and you don't have to include it again.
BTW, the #pragma once and #ifndef ENGINEER_H_ serve the same purpose. You can have only one of them. Code generated by VC use the #pragma, which is shorter and doesn't add a definition, so that's what I'd use (no harm if you leave both, though).

Looking for an optimum multithread message queue

I want to run several threads inside a process. I'm looking for the most efficient way of being able to pass messages between the threads.
Each thread would have a shared memory input message buffer. Other threads would write the appropriate buffer.
Messages would have priority. I want to manage this process myself.
Without getting into expensive locking or synchronizing, what's the best way to do this? Or is there already a well proven library available for this? (Delphi, C, or C# is fine).
This is hard to get right without repeating a lot of mistakes other people already made for you :)
Take a look at Intel Threading Building Blocks - the library has several well-designed queue templates (and other collections) that you can test and see which suits your purpose best.
If you are going to work with multiple threads, it is hard to avoid synchronisation. Fortunately it is not very hard.
For a single process, a Critical Section is frequently the best choice. It is fast and easy to use. For simplicity, I normally wrap it in a class to handle initialisation and cleanup.
#include <Windows.h>
class CTkCritSec
{
public:
CTkCritSec(void)
{
::InitializeCriticalSection(&m_critSec);
}
~CTkCritSec(void)
{
::DeleteCriticalSection(&m_critSec);
}
void Lock()
{
::EnterCriticalSection(&m_critSec);
}
void Unlock()
{
::LeaveCriticalSection(&m_critSec);
}
private:
CRITICAL_SECTION m_critSec;
};
You can make it even simpler using an "autolock" class you lock/unlock it.
class CTkAutoLock
{
public:
CTkAutoLock(CTkCritSec &lock)
: m_lock(lock)
{
m_lock.Lock();
}
virtual ~CTkAutoLock()
{
m_lock.Unlock();
}
private:
CTkCritSec &m_lock;
};
Anywhere you want to lock something, instantiate an autolock. When the function finishes, it will unlock. Also, if there is an exception, it will automatically unlock (giving exception safety).
Now you can make a simple message queue out of an std priority queue
#include <queue>
#include <deque>
#include <functional>
#include <string>
struct CMsg
{
CMsg(const std::string &s, int n=1)
: sText(s), nPriority(n)
{
}
int nPriority;
std::string sText;
struct Compare : public std::binary_function<bool, const CMsg *, const CMsg *>
{
bool operator () (const CMsg *p0, const CMsg *p1)
{
return p0->nPriority < p1->nPriority;
}
};
};
class CMsgQueue :
private std::priority_queue<CMsg *, std::deque<CMsg *>, CMsg::Compare >
{
public:
void Push(CMsg *pJob)
{
CTkAutoLock lk(m_critSec);
push(pJob);
}
CMsg *Pop()
{
CTkAutoLock lk(m_critSec);
CMsg *pJob(NULL);
if (!Empty())
{
pJob = top();
pop();
}
return pJob;
}
bool Empty()
{
CTkAutoLock lk(m_critSec);
return empty();
}
private:
CTkCritSec m_critSec;
};
The content of CMsg can be anything you like. Note that the CMsgQue inherits privately from std::priority_queue. That prevents raw access to the queue without going through our (synchronised) methods.
Assign a queue like this to each thread and you are on your way.
Disclaimer The code here was slapped together quickly to illustrate a point. It probably has errors and needs review and testing before being used in production.

Resources