I am promoting the QDoubleSpinBox class as i want to catch the mouseDoubleClick Event.
This is the Promoted class.
class SumDoubleBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SumDoubleBox(QWidget* parent = nullptr);
void setSingleStep(double val);
double singleStep() const;
void stepBy(int steps) override;
protected:
virtual void focusInEvent(QFocusEvent *e) override;
public slots:
void setZero();
void setOne();
signals:
int signalUndoRedo();
private:
double m_defaultStep = 1.0;
double m_CurrentStep;
bool m_stepUp;
};
SumDoubleBox::SumDoubleBox(QWidget* parent) : QDoubleSpinBox(parent)
{
SumLineEdit* lineEdit = new SumLineEdit(this);
setLineEdit(lineEdit);
setMinimum(0.0);
setMaximum(99999.0);
}
Since i am creating a pointer in the Constructor of the SumDoubleBox Class.
SumLineEdit* lineEdit = new SumLineEdit(this);
Do i need to Explicitly delete this in the Destructor ?
/////////////////////////////////////////////////////////////////
The Defination of the SumLineEdit class.
class SumLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit SumLineEdit(QWidget* parent = nullptr) { };
protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
};
void SumLineEdit::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
selectAll();
event->accept();
return;
}
QLineEdit::mouseDoubleClickEvent(event);
}
Since you've parented the new lineedit to the SumDoubleBox, it should get deleted with its parent.
There is, however, a more Qt-centric way to handle this case that I would strongly recommend you use or at least look into: Installing an event filter on the built-in line edit and handling the events for it. I've made many spinbox variants, and this approach usually works out best:
SumDoubleBox::SumDoubleBox( QWidget *parent ) :
QDoubleSpinBox( parent )
{
lineEdit()->installEventFilter( this );
}
bool SumDoubleBox::eventFilter( QObject *object, QEvent *event )
{
if( object == lineEdit() && event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
if( mouseEvent->button() == Qt::LeftButton )
{
selectAll();
event->accept();
return true; //swallow the event
}
}
return false; //let the event through to the lineedit
}
This makes the custom class entirely redundant. It's nice because inheritance is a big hammer that easily gets out of control as a codebase scales up, and if there are other simpler ways to achieve your goal, it's often worth considering.
Related
I want to make a screen caster application that captures a window or a screen directly to QQuickImage. To do so I made this header which has a thread object which frequently updates screen shots when signal received (sorry file was written in a poor language)
class OtherThread : public QThread
{
Q_OBJECT
QString oldWritingFile = "filename.png";
bool loaded = true;
public:
int winId = 0;
OtherThread(QObject *parent = nullptr):QThread(parent){};
~OtherThread(){
if(isRunning()){
requestInterruption();
wait();
}
};
void setParams(int id){
winId = id;
}
void knowLoaded(){
loaded =true;
}
signals:
void fileCacheChanged(QString);
protected:
void run() override{
while (!isInterruptionRequested()) {
if(winId != 0 && loaded){
bool success;
loaded = false;
if(oldWritingFile == QString::number(winId)+".png"){
if(qApp->primaryScreen()->grabWindow(winId).save(QString::number(winId)+"file.png")){
oldWritingFile = QString::number(winId)+"file.png";
success = true;
}else{success=false;}
}else{
if(qApp->primaryScreen()->grabWindow(winId).save(QString::number(winId)+".png")){
oldWritingFile = QString::number(winId)+".png";
success = true;
}else{success = false;}
}
emit fileCacheChanged(oldWritingFile);
}
}
};
};
class Controller : public QObject
{
Q_OBJECT
QString oldWritingFile;
public:
Controller(QObject *parent = nullptr):QObject(parent) {}
virtual ~Controller() {}
void changeFile(QString message){
oldWritingFile = message;
emit newFile(message);
};
public slots:
void startThread(){
OtherThread *thread =new OtherThread;
connect(thread, &OtherThread::fileCacheChanged, this, &Controller::changeFile);
connect(this, &Controller::stop, thread, &OtherThread::requestInterruption);
connect(this, &Controller::changePrint, thread, &OtherThread::setParams);
connect(this, &Controller::loaded, thread, &OtherThread::knowLoaded);
thread->start();
}
QString getLoadedFile(){
if(oldWritingFile != NULL){
return "file:./"+oldWritingFile;
}else{
return "file:./index.png";
}
}
signals:
void stop();
void changePrint(int id);
void newFile(QString);
void loaded();
};
and with my qml image i did this
Image {
id: image
anchors.fill: parent
source: "images/kalaripayattu.svg"
fillMode: Image.PreserveAspectFit
cache:false
Component.onCompleted: {
justThread.newFile.connect(updateFunction)
}
function updateFunction(filename){
source = "file:./"+filename;
justThread.loaded()
}
}
JustThread{
signal stopThread
id:justThread
onStopThread: {
stop()
}
Component.onCompleted: {
startThread()
changePrint(id)
}
}
Component.onCompleted:{
applicationWindow.closing.connect(justThread.stopThread)
}
but it has a really horrible fps.
can anything be done to increase fps?
Is there any easy way to do this?
Image is not really made for displaying frequently changing images. What you want is VideoOutput.
Here's what I would do :
You need to create a QObject derived class instance and set it as the source of the VideoOutput : http://doc.qt.io/qt-5/qml-qtmultimedia-videooutput.html#source-prop
Your class needs to have a Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface NOTIFY videoSurfaceChanged)
In your setVideoSurface method, you need to call the start method of QAbstractVideoSurface with a correct format (your size and the pixelformat, pixelformat should be QVideoFrame::Format_ARGB32 for desktop I guess).
And then when you want to update the VideoOutput (via a QTimer for example), you call the present method of QAbstractVideoSurface with a QVideoFrame you constructed from the QPixmap you got in QScreen::grabWindow.
For that, you could convert the QPixmap to QImage with toImage and then convert it to QVideoFrame with theQVideoFrame::QVideoFrame(const QImage &image) constructor.
I want to resize the app window proportionally 1:1. I tried to change it inside the ResizeEvent, but then I got the window flickering. Now my code looks like this, but it doesn't work.
filterobject.h:
class FilterObject:public QObject{
public:
QWidget *target = nullptr;//it holds a pointer to target object
int goalHeight=0;
FilterObject(QObject *parent=nullptr):QObject(parent){}//uses QObject constructor
bool eventFilter(QObject *watched, QEvent *event) override;//and overrides eventFilter function
};
widget.h:
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
virtual int heightForWidth ( int w ) const { return w*9/16;}
//virtual void resizeEvent(QResizeEvent *event) override;
~Widget();
private:
Ui::Widget *ui;
};
widget.cpp:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void QWidget::resizeEvent(QResizeEvent *event){
FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();
filter->target=targetWidget;
targetWidget->installEventFilter(filter);
}
bool FilterObject::eventFilter(QObject *watched, QEvent *event) {
if(watched != target){//checks for correct target object.
return false;
}
if(event->type() != QEvent::Resize){//and correct event
return false;
}
QResizeEvent *resEvent = static_cast<QResizeEvent*>(event);
goalHeight = 9*resEvent->size().width()/16;
if(target->height()!=goalHeight){
target->setFixedHeight(goalHeight);
}
return true;
};
Perhaps this code will work, but my condition if(event->type() != QEvent::Resize) does not work .. Any ideas?
You have some problems in your code. First of all you should install event filter once e.g in your constructor. You create an object of event filter and install it every time resizeEvent is triggered which is wrong. Also you are installing event filter on the wrong object (a new QWidget). So remove the resizeEvent function and insert in the constructor of Widget:
FilterObject *filter = new FilterObject();
filter->target=this;
installEventFilter(filter);
I have a table and move around inside with left, right, up, down buttons. Now I need to create a SIGNAL when I stay in a certain cell and press SPACE button. This SIGNAL should bring also the coordinate of that cell. I tried with standard signals of QTableWidget but it does not work. How can I solve this?
Create a separate header file i.e. "customtable.h" and then in the Designer you can Promote the existing QTableWidget to this class.
class customTable:public QTableWidget
{
Q_OBJECT
public:
customTable(QWidget* parent=0):QTableWidget(parent){}
protected:
void keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_Space)
{
emit spacePressed(this->currentRow(),this->currentColumn());
}
else { QTableWidget::keyPressEvent(e); }
}
signals:
spacePressed(int r, int c);
};
You can use an event filter to do this:
class TableSpaceWatcher : public QObject {
Q_OBJECT
bool eventFilter(QObject * receiver, QEvent * event) override {
auto table = qobject_cast<QTableWidget*>(receiver);
if (table && event->type() == QEvent::KeyPress) {
auto keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Space)
emit spacePressed(table->currentRow(), table->currentColumn());
}
return false;
}
public:
using QObject::QObject;
Q_SIGNAL void spacePressed(int row, int column);
void installOn(QTableWidget * widget) {
widget->installEventFilter(this);
}
};
QTableWidget table;
TableSpaceWatcher watcher;
watcher.installOn(&table);
OK, this should be easy.
I tried to handle drop event onto a QGraphicsView widget. Incoming data dragged from a QTreeView widget. For that, I re-implemented these methods:
void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
this.scene()->addPixmap(pixmap);
}
This works fine; but how can I change another graphicsview scene within this widget's drop event? That is:
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
// I cannot access ui; and cannot access my widgets...:
ui->anotherview->scene()->addPixmap(pixmap);
}
What about making a custom signal in your QGraphicsView like void showPixmap(QPixmap p) and connecting it to a slot in your main gui class where you can access ui elements. You can then call emit showPixamp(pixmap) in the dropEvent.
Subclassing QGraphicsView
//header file
class CustomView : public QGraphicsView
{
public:
CustomView(QGraphicsScene*, QWidget*=NULL);
~CustomView();
signals:
void showPixmap(QPixmap p);
protected:
virtual void dropEvent(QDropEvent *event);
};
//cpp file
CustomView::CustomView(QGraphicsScene *scene, QWidget* parent)
:QGraphicsView(scene, parent)
{
//if you need to initialize variables, etc.
}
void CustomView::dropEvent(QDropEvent *event)
{
//handle the drop event
QPixmap mPixmap;
emit showPixmap(mPixmap);
}
Using event filters in your main GUI class
void GUI::GUI()
{
ui->mGraphicsView->installEventFilter(this);
}
bool GUI::eventFilter(QObject *object, QEvent *event)
{
if (object == ui->mGraphicsView && event->type() == QEvent::DropEnter) {
QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
//handle the drop event
return true;
}
else
return false;
}
Sorry if I'm missing something obvious, but I can't seem to find an answer to my question. Any help would be appreciated.
I am trying to use a QSlider to manipulate data in a class I created.
In the main window constructor I have the following:
connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(setValue(int)));
With a slot defined in the same class:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
AFrame *frame;
Ui::MainWindow *ui;
public slots:
void setValue(int val)
{
frame->setMphValue(val);
}
};
My frame class is a promoted widget to allow for drawing over the image I have set and is defined as follows:
class AFrame : public QLabel
{
Q_OBJECT
public:
AFrame( QWidget *parent );
void setMphValue(int val) { m_mph = val; }
protected:
void paintEvent( QPaintEvent *event );
private:
int m_mph;
};
The problem is that when I try assigning the m_mph value in the paintEvent function of the AFrame class, the integer value is lost.
Is there something obvious that I'm missing? Is there a better way to approach this problem?
And my paintEvent code:
void AFrame::paintEvent(QPaintEvent *event)
{
QLabel::paintEvent(event);
QPainter painter(this);
QPen pen("#FF0099");
pen.setWidth(8);
painter.setPen(pen);
//rpm
painter.drawLine(250,275,165,165);
//oil
painter.drawLine(450,100,400,75);
//fuel
painter.drawLine(650,95,600,65);
//mph
QRect rec(0,0,125,3);
int velocity = m_mph;
int rpmStartVal = -225;
float mph = velocity * 1.68;
painter.translate(870,275);
painter.rotate(rpmStartVal + mph);
painter.drawRect(rec);
}
The integer value is not being lost. The widget has no magical insight into the fact that it should repaint when the mph value is updated. Your setMphValue should look like below. That's all there's to it.
void setMphValue(int val) {
m_mph = val;
update();
}
To add to Kuba's reply:
You should also check whether val is the same value as previous, in order to avoid avoid repainting when it's not actually necessary - and side-effect infinite loops should anything called by update() later touch setMphValue().
In full:
void setMphValue(int val) {
if (val == m_mph) return;
m_mph = val;
update();
}