Segfault при нажатии на QFrame - PullRequest
0 голосов
/ 12 января 2020

Эта topi c может быть немного длинной, так как я должен объяснить предпосылку, прежде чем приступить к рассмотрению проблемы. Во-первых, моя главная цель - это приложение, в котором пользователь может перетаскивать команды из панель инструментов для формирования рабочих процессов, которые отправляются и выполняются на удаленном сервере. В настоящее время я работаю над клиентской частью в qt, и это сводит меня с ума.

Это мой код:

draglabel.h

  #ifndef DRAGLABEL_H
  #define DRAGLABEL_H

  #include <QLabel>

  class QDragEnterEvent;
  class QDragMoveEvent;
  class QFrame;

  class DragLabel : public QLabel
  {
  public:
      DragLabel(const QString &text, QWidget *parent);
      QString labelText() const;

  private:
      QString m_labelText;
  };

  #endif // DRAGLABEL_H
draglabel.c

 #include "draglabel.h"

 #include <QtWidgets>

 DragLabel::DragLabel(const QString &text, QWidget *parent)
     : QLabel(parent)
 {
     QFontMetrics metric(font());
     QSize size = metric.size(Qt::TextSingleLine, text);

     QImage image(size.width() + 12, size.height() + 12, QImage::Format_ARGB32_Premultiplied);
     image.fill(qRgba(0, 0, 0, 0));

     QFont font;
     font.setStyleStrategy(QFont::ForceOutline);

     QLinearGradient gradient(0, 0, 0, image.height()-1);
     gradient.setColorAt(0.0, Qt::white);
     gradient.setColorAt(0.2, QColor(200, 200, 255));
     gradient.setColorAt(0.8, QColor(200, 200, 255));
     gradient.setColorAt(1.0, QColor(127, 127, 200));

     QPainter painter;
     painter.begin(&image);
     painter.setRenderHint(QPainter::Antialiasing);
     painter.setBrush(gradient);
     painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
                             25, 25, Qt::RelativeSize);

     painter.setFont(font);
     painter.setBrush(Qt::black);
     painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, text);
     painter.end();

     setPixmap(QPixmap::fromImage(image));
     m_labelText = text;
 }

 QString DragLabel::labelText() const
 {
     return m_labelText;
 }

dragwidget.h
  #ifndef DRAGWIDGET_H
  #define DRAGWIDGET_H

  #include <QWidget>
  #include <QFrame>
  #include <vector>
  #include <set>
  #include "draglabel.h"
  using namespace std;

  class QDragEnterEvent;
  class QDropEvent;

  class DragWidget : public QFrame
  {
  public:
      DragWidget(QWidget *parent = nullptr);
      void setMode(int desiredMode);
      void changePairingMode();
      void showAvailableCommands();
      void initDrawingLayout();
      vector<tuple<QString,QString>> actCommands;
      vector<tuple<QString,QString>> execCommands;
      vector<pair<int,int>>waitingForPair;
      int pairingMode=0;
      QFrame*drawingCon;

  private:
    int widgetMode=1;

  protected:
      void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
      void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
      void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;
      void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
  };

  #endif // DRAGWIDGET_H

dragwidget.cpp
#include "draglabel.h"
#include "dragwidget.h"
#include "arrowhead.h"

#include <QtWidgets>
#include <QWidget>
#include <QFrame>
#include <QColor>
#include <tuple>
using namespace std;

static inline QString dndProcMimeType() { return QStringLiteral("application/x-fridgemagnet"); }

DragWidget::DragWidget(QWidget *parent)
    : QFrame(parent)
{
    drawingCon=new QFrame(this);
    QPalette newPalette = palette();
    newPalette.setColor(QPalette::Window, Qt::white);
    setPalette(newPalette);
    setWindowTitle(tr("Drag-and-Drop"));
    setMinimumSize(300,300);
    setAcceptDrops(true);
    setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
    drawingCon->setPalette(newPalette);
    drawingCon->setWindowTitle(tr("Drag-and-Drop"));
    drawingCon->setMinimumSize(350,350);
    drawingCon->setAcceptDrops(false);
    drawingCon->show();
}

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat(dndProcMimeType())) {
        if (children().contains(event->source())) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else if (event->mimeData()->hasText()) {
        event->acceptProposedAction();
    } else {
        event->ignore();
    }
}

void DragWidget::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat(dndProcMimeType())) {
        if (children().contains(event->source())) {
            if(widgetMode==1)
            {
                event->setDropAction(Qt::MoveAction);
                event->accept();
            }
            else {
                event->ignore();
            }
        } else {
            if(widgetMode==1)
            {
                event->acceptProposedAction();
            }
            else
            {
                if(widgetMode==1)
                {
                    event->accept();
                }
                else {
                    event->ignore();
                }
            }
        }
    } else if (event->mimeData()->hasText()) {
        event->acceptProposedAction();
    } else {
        event->ignore();
    }
}

void DragWidget::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasFormat(dndProcMimeType())) {
        const QMimeData *mime = event->mimeData();
        QByteArray itemData = mime->data(dndProcMimeType());
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
        QString text;
        QPoint offset;
        dataStream >> text >> offset;
        DragLabel *newLabel = new DragLabel(text, this);
        newLabel->move(event->pos() - offset);
        newLabel->show();
        newLabel->setAttribute(Qt::WA_DeleteOnClose);
        if (event->source() == this) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            tuple<QString,QString> addTest;
            addTest=make_tuple(text,"");
            actCommands.push_back(make_tuple(text,""));
            for(auto it:actCommands)
                qDebug()<<get<0>(it)<<" "<<get<1>(it);
            event->acceptProposedAction();
        }
    } else {if (event->mimeData()->hasText()) {
        if(widgetMode==1)
        {
            event->accept();
        }
        else {
            event->ignore();
        }

        event->acceptProposedAction();
        }
    }

}

void DragWidget::mousePressEvent(QMouseEvent *event)
{
    DragLabel *child = static_cast<DragLabel*>(childAt(event->pos()));
    if(!pairingMode){
    if (!child)
        return;
    QPoint hotSpot = event->pos() - child->pos();
    if(widgetMode==1)
        qDebug()<<child->labelText();
    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);
    dataStream << child->labelText() << QPoint(hotSpot);

    QMimeData *mimeData = new QMimeData;
    mimeData->setData(dndProcMimeType(), itemData);
    mimeData->setText(child->labelText());

    QDrag *drag = new QDrag(this);
    drag->setMimeData(mimeData);
    drag->setPixmap(*child->pixmap());
    drag->setHotSpot(hotSpot);

    child->hide();

    if (drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction) == Qt::MoveAction)
        child->close();
    else {
        child->show();
        }
    }
    else {
        if(widgetMode==1)
        {
            DragLabel *child = static_cast<DragLabel*>(childAt(event->pos()));
            if (!child)
                return;
            qDebug()<<"Facem pair cu:"<<child->labelText();
             waitingForPair.push_back(make_pair(child->x(),child->y()));
            if(waitingForPair.size()==2) {
                ArrowHead *line=new ArrowHead(waitingForPair.at(0).first,waitingForPair.at(0).second,waitingForPair.at(1).first,waitingForPair.at(1).second,drawingCon);
                line->show();
                waitingForPair.erase(waitingForPair.begin(),waitingForPair.begin()+1);
                qDebug()<<"Tragem linie";
            }
        }
}
}

void DragWidget::setMode(int desiredMode)
{
    widgetMode=desiredMode;
}
void DragWidget::showAvailableCommands()
{
    DragLabel*grep=new DragLabel("grep",this);
    grep->move(this->x(),this->y());
    grep->show();
    DragLabel*cat=new DragLabel("cat",this);
    grep->move(this->x()+40,this->y());
    cat->show();
    DragLabel*wc=new DragLabel("wc",this);
    wc->move(this->x()+90,this->y());
    wc->show();

}
void DragWidget::changePairingMode()
{
    if(pairingMode==1)
        pairingMode=0;
    else {
       pairingMode=1;
    }
}

mainWindow.h
#ifndef MAINWINDOW_H
 #define MAINWINDOW_H

 #include <QMainWindow>
 #include <QPushButton>
 #include <QTextEdit>
 #include "dragwidget.h"

 namespace Ui {
    class MainWindow;
 }

 class MainWindow : public QMainWindow
 {
    Q_OBJECT
 public:
    explicit MainWindow(QWidget *parent = nullptr);
 protected:
     virtual void closeEvent(QCloseEvent *event) override;
 private slots:
    void handleButton();
    void closeAppButton();
    void pairButton();
 private:
    QPushButton *executeCode;
    QPushButton *pairCommands;
    QPushButton *closeApp;
    QTextEdit *inputUser;
    QTextEdit *outputServer;
    DragWidget * commandLayout=new DragWidget();
    DragWidget * availableLayout=new DragWidget();
 };

#endif // MAINWINDOW_H


mainWindow.cpp
#include "mainwindow.h"
#include "draglabel.h"
#include "dragwidget.h"
#include <QCoreApplication>
#include <QApplication>
#include <QHBoxLayout>
#include <QPushButton>
#include <QCloseEvent>
#include <QTextEdit>
#include <QFrame>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
   : QMainWindow(parent)
{
    executeCode=new QPushButton("Execute");
    closeApp=new QPushButton("Close");
    pairCommands=new QPushButton("Pair");
    connect(closeApp, SIGNAL (released()), this, SLOT (closeAppButton()));
    connect(pairCommands, SIGNAL (released()), this, SLOT (pairButton()));
    void pairButton();
    QHBoxLayout * horizontalLayout=new QHBoxLayout();
    commandLayout->setMode(1);
    availableLayout->setMode(2);
    horizontalLayout->addWidget(commandLayout);
    horizontalLayout->addWidget(availableLayout);
    availableLayout->showAvailableCommands();
    QVBoxLayout*inputBoxes=new QVBoxLayout();
    inputUser=new QTextEdit();
    outputServer=new QTextEdit();
    inputBoxes->addWidget(inputUser);
    inputBoxes->addWidget(outputServer);
    horizontalLayout->addLayout(inputBoxes);
    QVBoxLayout*withButtons=new QVBoxLayout();
    withButtons->addLayout(horizontalLayout);
    withButtons->addWidget(pairCommands);
    withButtons->addWidget(executeCode);
    withButtons->addWidget(closeApp);
    withButtons->addWidget(new QFrame());
    setCentralWidget(new QWidget);
    centralWidget()->setLayout(withButtons);
}

void MainWindow::handleButton()
{

}
void MainWindow::closeEvent(QCloseEvent *event)
{
    event->accept();
}
void MainWindow::closeAppButton()
{
   exit(EXIT_SUCCESS);
}
void MainWindow::pairButton()
{
    commandLayout->changePairingMode();
    qDebug()<<commandLayout->pairingMode;
}

Примечание. Может показаться идиотским c, но у меня есть тот же класс для «панели инструментов», откуда вы должны перетаскивать команды и также для части, где вы должны перетаскивать команды и соединять их.

Это в основном модифицированный код примера холодильника-магнита на веб-сайте qt. Проблема, которая вызывает головную боль, заключается в рисовании линий между dragwidget, я пробовал рисовать все в одном и том же QFrame, но это оказалось катастрофическим, поскольку вся pixelMap экземпляра dragWidget перезаписывается при каждом рисовании. Решение, которое я придумал, состоит в том, чтобы наложите дополнительный QFrame на мой dragWidget, чтобы рисовать линии там, и все будут счастливы, но, как всегда, на каждом шагу случается беда. Когда я пытаюсь нажать на командном виджете, все в порядке, но нажатие на что-либо, кроме DragLabel, приводит к ошибка из-за нажатия на QFrame из-за childAt (), возвращающего адрес QFrame, наложенного на первый экземпляр dragWdiget ();

Мой главный вопрос: как мне преодолеть это препятствие

1 Ответ

0 голосов
/ 13 января 2020

Вы должны использовать qobject_cast вместо static_cast.

Добавьте макрос Q_OBJECT в каждое объявление класса:

class DragLabel : public QLabel
{
    Q_OBJECT

public:
  //... class declaration ...
}

class DragWidget  : public QFrame
{
    Q_OBJECT

public:
  //... class declaration ...
}

Затем используйте qobject_cast вместо static_cast для childAt(), например:

DragLabel *child = qobject_cast<DragLabel*>(childAt(event->pos()));

if(!child){
....
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...