Skip to content
  • 0 Votes
    15 Posts
    298 Views
    KenAppleby 0K

    @StudentScripter

    Below is code that I can share. It's a complete example which perhaps does what you need. The void HandlesItem::paint() function is where the graphics transformations are done. It's rather long-winded but it at least shows what's required. It draws a painter path around the model items and deals with scaling and rotation of the model items as well as scaling of the graphics view.

    I'm sorry there is so much code: it's mostly the test framework. It uses mouse wheel events + modifiers to set the scale and rotation of items and the view.

    // main.cpp #include <QGraphicsRectItem> #include <QGraphicsRectItem> #include <QRandomGenerator> #include <QPainter> // A graphics path item that paints a selection graphic rectangle around a model item. class HandlesItem : public QGraphicsPathItem { public: HandlesItem(QGraphicsItem * modelItem, QGraphicsItem * parent =nullptr); void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget =nullptr) override; void setViewScale(double scale); protected: QGraphicsItem * mModelItem{ nullptr }; }; // A contentless graphics item that acts as a proxy for a model item and manages a HandlesItem for it. // The model item position must be set by setting the Proxy item position. // The model item scale and rotation must be set by the setItemScale() and setItemRotation() functions. // Model items and HandleItems are assumed to transform about their center points. class Proxy : public QGraphicsRectItem { public: Proxy(QGraphicsItem * modelItem, QGraphicsItem * parent =nullptr); void setSelected(bool selected); bool isSelected() const; void setItemRotation(double angle); void setItemScale(double scale); void setViewScale(double scale); // call to set the HandleItem's pen width according to the view scale. protected: QGraphicsItem * mModelItem{ nullptr }; HandlesItem * mHandlesItem{ nullptr }; bool mSelected{ false }; }; // -------------------------------------- HandlesItem::HandlesItem(QGraphicsItem * modelItem, QGraphicsItem * parent) : QGraphicsPathItem{ parent }, mModelItem{ modelItem } { setViewScale(1.0); setTransformOriginPoint(mModelItem->boundingRect().center()); } void HandlesItem::setViewScale(double scale) { QPen pen{ Qt::yellow }; pen.setWidthF(4.0/scale); setPen(pen); } void HandlesItem::paint(QPainter * painter, [[ maybe_unused ]] const QStyleOptionGraphicsItem * option, [[ maybe_unused ]] QWidget * widget) { const double scale{ mModelItem->scale() }; const double margin{ 8.0 / scale }; QRectF r{ mModelItem->boundingRect().adjusted(-margin, -margin, margin, margin) }; const QPointF tl{ r.topLeft() }, tr{ r.topRight() }, br{ r.bottomRight() }, bl{ r.bottomLeft() }; QTransform transform; transform.translate(r.center().x(), r.center().y()); transform.scale(mModelItem->scale(), mModelItem->scale()); transform.rotate(mModelItem->rotation()); transform.translate(-r.center().x(), -r.center().y()); const QPointF tlt{ transform.map(tl) }, trt{ transform.map(tr) }, brt{ transform.map(br) }, blt{ transform.map(bl) }; QPainterPath path; path.moveTo(tlt); path.lineTo(trt); path.lineTo(brt); path.lineTo(blt); path.lineTo(tlt); setPath(path); QGraphicsPathItem::paint(painter, option, widget); } Proxy::Proxy(QGraphicsItem * modelItem, QGraphicsItem * parent) : QGraphicsRectItem{ parent }, mModelItem{ modelItem } { mModelItem->setPos(0, 0); // the model item is positioned by the Proxy. mModelItem->setParentItem(this); setFlag(QGraphicsItem::ItemHasNoContents); mHandlesItem = new HandlesItem{ modelItem, this }; setSelected(false); } void Proxy::setSelected(bool selected) { mSelected = selected; mHandlesItem->setVisible(mSelected); update(); } bool Proxy::isSelected() const { return mSelected; } void Proxy::setItemRotation(double angle) { if (mModelItem) { mModelItem->setTransformOriginPoint(mModelItem->boundingRect().center()); mModelItem->setRotation(mModelItem->rotation() + angle); } } void Proxy::setItemScale(double scale) { if (mModelItem) { mModelItem->setTransformOriginPoint(mModelItem->boundingRect().center()); mModelItem->setScale(mModelItem->scale() * scale); } } void Proxy::setViewScale(double scale) { mHandlesItem->setViewScale(scale); } // The remaining code is a QGraphicsView and QGraphicsScene test framework for the above. #include <QGraphicsView> class GraphicsView : public QGraphicsView { public: GraphicsView(QWidget * parent); void initialise(QGraphicsScene& scene); protected: void mousePressEvent(QMouseEvent * event) override; void mouseMoveEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent *) override; void wheelEvent(QWheelEvent *) override; void setSelection(Proxy * item); void initialiseScene(); void setViewTransform(); QTransform makeTransform() const; QList<Proxy *> mItems; Proxy * mSelectedItem{ nullptr }; Proxy * mDragging{ nullptr }; QPointF mMoveScenePoint; double mScale{ 1.0 }; double mRotation{ 0.0 }; }; #include <QMainWindow> #include <QGraphicsScene> #include <QBoxLayout> class HandlesMainWindow : public QMainWindow { public: HandlesMainWindow(QWidget * parent =nullptr); protected: QGraphicsScene mScene; }; HandlesMainWindow::HandlesMainWindow(QWidget * parent) : QMainWindow(parent) { QWidget * centralwidget; QVBoxLayout * verticalLayout; GraphicsView * graphicsView; resize(532, 377); centralwidget = new QWidget(this); centralwidget->setObjectName("centralwidget"); verticalLayout = new QVBoxLayout(centralwidget); verticalLayout->setObjectName("verticalLayout"); graphicsView = new GraphicsView(centralwidget); graphicsView->setObjectName("graphicsView"); verticalLayout->addWidget(graphicsView); setCentralWidget(centralwidget); graphicsView->initialise(mScene); } #include <QMouseEvent> #include <QWheelEvent> namespace { QRandomGenerator& rng() { static QRandomGenerator sRng; return sRng; } QColor randomColor() { return QColor{ rng().bounded(64, 255), rng().bounded(64, 255), rng().bounded(64, 255) }; } QPointF randomPoint() { return QPointF{ 1.0*rng().bounded(20, 400), 1.0*rng().bounded(20, 400) }; } QRectF randomRect() { return QRectF{ 0.0, 0.0, 1.0*rng().bounded(20, 100), 1.0*rng().bounded(20, 100) }; } } GraphicsView::GraphicsView(QWidget * parent) : QGraphicsView{ parent } { setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setRenderHint(QPainter::Antialiasing); setTransformationAnchor(ViewportAnchor::NoAnchor); } void GraphicsView::initialise(QGraphicsScene& scene) { setScene(&scene); initialiseScene(); } void GraphicsView::mousePressEvent(QMouseEvent * event) { const QPoint p{ event->pos() }; QGraphicsItem * item{ itemAt(p) }; if (item) { mDragging = qgraphicsitem_cast<Proxy *>(item->parentItem()); mMoveScenePoint = mapToScene(p); } else { mDragging = nullptr; } } void GraphicsView::mouseMoveEvent(QMouseEvent * event) { const QPoint p{ event->pos() }; if (mDragging) { const QPointF sp{ mapToScene(p) }; mDragging->moveBy(sp.x() - mMoveScenePoint.x(), sp.y() - mMoveScenePoint.y()); mMoveScenePoint = sp; } } void GraphicsView::mouseReleaseEvent(QMouseEvent * event) { mDragging = nullptr; const QPoint p{ event->pos() }; QGraphicsItem * item{ itemAt(p) }; if (item) { setSelection(qgraphicsitem_cast<Proxy *>(item->parentItem())); } else { setSelection(nullptr); } } void GraphicsView::wheelEvent(QWheelEvent * event) { if (event->modifiers() == Qt::NoModifier) { double deltaScale{ 1.0 }; QPoint p = event->angleDelta(); if (p.y() > 0) { deltaScale = 1.1; } else if (p.y() < 0) { deltaScale = 1.0/1.1; } event->accept(); if (mSelectedItem) { mSelectedItem->setItemScale(deltaScale); } else { mScale = mScale * deltaScale; setViewTransform(); } } else if (event->modifiers() == Qt::ControlModifier){ QPoint p = event->angleDelta(); double rotation{ 0.0 }; if (p.y() > 0) { rotation = 2.0; } else if (p.y() < 0) { rotation = -2.0; } event->accept(); if (mSelectedItem) { mSelectedItem->setItemRotation(rotation); } else { mRotation += rotation; setViewTransform(); } } } void GraphicsView::setSelection(Proxy * item) { mSelectedItem = item; if (item) { item->setSelected(true); } for (auto i : mItems) { if (not (i == item)) { i->setSelected(false); } } } void GraphicsView::setViewTransform() { setTransform(makeTransform()); for (auto i : mItems) { i->setViewScale(mScale); } } QTransform GraphicsView::makeTransform() const { QTransform result; const QPointF center{ rect().center() }; result.translate(center.x(), center.y()); result.scale(mScale, mScale); result.rotate(mRotation); result.translate(-center.x(), -center.y()); return result; } void GraphicsView::initialiseScene() { scene()->setBackgroundBrush(QBrush{ Qt::cyan }); for (int i = 0; i < 20; i++) { QGraphicsRectItem * item{ new QGraphicsRectItem(::randomRect()) }; item->setBrush(QBrush{ ::randomColor() }); Proxy * proxy{ new Proxy{ item } }; proxy->setPos(::randomPoint()); scene()->addItem(proxy); mItems << proxy; } } #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); HandlesMainWindow w; w.show(); return a.exec(); }
  • 0 Votes
    32 Posts
    1k Views
    A

    Okay, thanks to everyone who helped me, to resolve the varius bugs and expand my knowlage.
    here the recap of how to create and customize the view:

    step 1)

    Create a normal c++ Class containing the Data you want to display (not inheriting from QWidget)

    step 2)

    on the Custom Class implement a method "paint(QPainter *painter, const QRect &rect, const QPalette &palette)" that will be used inside the QStyledItemDelegate::paint() method of the custom QStyledItemList for delegating the drawing part to the normal c++ class

    step 3)

    create a widgetEditor (derived from QWidget) that implement some default Widgets (like QComboBox, QLineEdit, QSpinBox ecc..) to let the user change the data.

    When the user have finished to edit, you can provide a "button" to emit a custom signal like "commitAndCloseEditor()" so you can trigger the QStyledItemDelegate::commitData() and closeEditor() signal needed for destroy the Editor once finished the editing.
    1e39a40b-6bec-4087-9e5a-371075bfb1e2-image.png

    step 4)

    for the custom QStyledItemDelegate to work, you must re-implement the following methods:

    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const void setEditorData( QWidget* editor, const QModelIndex &index ) const void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) step 5)

    use the method from the QIndexModel to detect if the model have the correct data that can be converted correctly to your vanilla c++ class from the point (1)

    NOTE: see the StarDelegate Example provided by Qt. If the underling model have contains a int, using QModelIndex::.data().canConvert<StarRating>() checks if passing a int to the constructor of StarRating will be enough to initialize it.
    remember to declare the vanilla c++ class with the Qt's macro like so: Q_DECLARE_METATYPE(StarRating)

    you can found the complete code (with notes and comments) on github at: https://github.com/aVenturelli-qt/CommentsListView/tree/main

    I still need to fix the data subscription to the model, but this is a separate topic that I will ask on another question if needed. You can find the problems highlighted with a comment like:
    /* WARNING HERE */

  • 0 Votes
    12 Posts
    456 Views
    Pl45m4P

    @StudentScripter said in QGraphicsRectItem item translation instead of movement?:

    Well the real problem seems to be that rect().setTopLeft() and all other direct changes to the parentitems rect result in a changed Origin Point of the object in the scene, while with setScale() the OriginPoint stays intact.

    AFAIK the "topLeft" of an item rotated by 90° clockwise is still the same point as when not being transformed... the original item stays the same, because you can revert the transformation at any time to get your initial item back. The QTranform only "maps" the initial item matrix to your transformed one.
    So I think calling setTopLeft manually will mess up your item and the current transformation completely.

  • 0 Votes
    5 Posts
    182 Views
    Pl45m4P

    @Axel-Spoerl said in QWidget Drag And Drop inside Layout. Is this a good way?:

    I have troubles to imagine what you mean by that. Colors aside, a widget is typically a rectangle. If I want to indicate a potential landing spot, I'd rather use a rectangle, than a line.

    I think @StudentScripter means the same behavior you see when adding widgets to a layout in QtDesigner.

    A "line" can be a thin rectangle :)

    You also see a rectangle when dragging QDockWidgets around showing the valid DockAreas.

    Maybe check the source code of QtCreator's Designer or QMainWindows dockarea.

  • 0 Votes
    3 Posts
    171 Views
    C

    I forgot to mentioned this ,I am Using TextInputUnit as component ,
    here is the TextInputUnit.qml , i have added the Font.AllUppercase
    TextInput {
    id: textInput
    text: value
    focus: true
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.bottom: parent.bottom
    anchors.leftMargin: 10
    anchors.right: unitsRect.left
    verticalAlignment: Text.AlignVCenter
    color: '#EBF4FF'
    font.family: 'Lato'
    font.pixelSize: 16
    font.capitalization: Font.AllUppercase

    selectByMouse: true

    // validator: validatorIn
    onEditingFinished: {
    root.textChanged()
    }
    readOnly: readonly
    }

    but still its not working . What will be the issue

  • 0 Votes
    14 Posts
    425 Views
    JonBJ

    @jeremy_k said in Console App how to populate a 10.000*10.000 Grid efficiently?:

    A easy optimization for setting a quadrant or an entire 2d space as a single color is to remove all subtrees of that tree,

    Absolutely. If that's the sort of thing you want to do. Doesn't seem to relate to toggling the state of 100,000 adjacent bits in 5 million.

    it stores all squares that shall be changed in a vector.
    The squares changed can be anywhere in this big map,
    eventually reaching many millions of squares needed to be changed at onces.

    If that is what you need to do, again I'm not sure I see any use of these quadtrees. But only you need the exact situation.

  • 0 Votes
    5 Posts
    254 Views
    mrdebugM

    Hi, if you have to straming audio and video files you could use live555 on server side and, client side, only QMediaPlayer using rtsp.

  • 0 Votes
    4 Posts
    225 Views
    S

    bumping this up. Anyone can hint me how to mass paint controls?

  • 0 Votes
    3 Posts
    208 Views
    S

    I tried to debug further but some things i get are just strange. I can't figure out why this happens:

    ORIG TOP LEFT 0 ORIG BOTTOM RIGHT QPointF(450,292) ORIG TOP LEFT 0 ORIG BOTTOM RIGHT QPointF(450,292) ORIG TOP LEFT 0 ORIG BOTTOM RIGHT QPointF(450,250) ORIG TOP LEFT nan ORIG BOTTOM RIGHT QPointF(450,250) ORIG TOP LEFT nan ORIG BOTTOM RIGHT QPointF(450,250) ORIG TOP LEFT nan ORIG BOTTOM RIGHT QPointF(450,250) ORIG TOP LEFT nan ORIG BOTTOM RIGHT QPointF(450,250) ORIG TOP LEFT nan ORIG BOTTOM RIGHT QPointF(450,250)

    Here is the code i get the debbug from:

    #include "handleitem.h" #include "resizablehandlerect.h" #include <QGraphicsSceneMouseEvent> #include <QDebug> #include "resizablepixmapitem.h" HandleItem::HandleItem(Position position) : handlePosition(position) { setFlag(QGraphicsItem::ItemIsMovable); } void HandleItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { ResizableHandleRect *rectItem = dynamic_cast<ResizableHandleRect *>(parentItem()); if (!rectItem) { return; // Wenn das Parent-Item kein ResizableHandleRect ist, beende die Funktion } QRectF boundingFrameRect = rectItem->selectorFrameBounds(); OriginTopLeft = mapToScene(boundingFrameRect.topLeft()); OriginBottomRight = mapToScene(boundingFrameRect.bottomRight()); QRectF SceneBoundingFrameRect(OriginTopLeft, OriginBottomRight); qDebug() << "ORIG TOP LEFT" << OriginLeftCenter << "ORIG BOTTOM RIGHT" << OriginBottomRight; OriginBottomCenter = SceneBoundingFrameRect.bottom(); OriginTopCenter = SceneBoundingFrameRect.top(); BottomCenterWasDragged = false; TopCenterWasDragged = false; } void HandleItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { ResizableHandleRect *rectItem = dynamic_cast<ResizableHandleRect *>(parentItem()); if (!rectItem) { return; // Wenn das Parent-Item kein ResizableHandleRect ist, beende die Funktion } QRectF boundingFrameRect = rectItem->selectorFrameBounds(); QPointF topLeft = mapToScene(boundingFrameRect.topLeft()); QPointF bottomRight = mapToScene(boundingFrameRect.bottomRight()); QRectF SceneBoundingFrameRect(topLeft, bottomRight); //qDebug() << "HandlePosition: " << handlePosition; switch (handlePosition) { case TopLeft: break; case TopCenter: SceneBoundingFrameRect.setTop(event->scenePos().y()); if(SceneBoundingFrameRect.bottom() < SceneBoundingFrameRect.top() && BottomCenterWasDragged == false){ qDebug() << "-- (2)SWITCH Top to Bottom"; handlePosition = BottomCenter; TopCenterWasDragged = true; } break; case TopRight: break; case RightCenter: break; case BottomRight: break; case BottomCenter: SceneBoundingFrameRect.setBottom(event->scenePos().y()); if(SceneBoundingFrameRect.bottom() < SceneBoundingFrameRect.top() && TopCenterWasDragged == false){ qDebug() << "(1)SWITCH Bottom to Top--"; handlePosition = TopCenter; BottomCenterWasDragged = true; } break; case BottomLeft: break; case LeftCenter: break; default: break; } if(SceneBoundingFrameRect.topLeft() != OriginTopLeft && BottomCenterWasDragged == true){ SceneBoundingFrameRect.setTopLeft(OriginTopLeft); BottomCenterWasDragged = false; qDebug() << "(1*)Correct Top, Bottom was Dragged!" << SceneBoundingFrameRect.topLeft(); } if(SceneBoundingFrameRect.bottomRight() != OriginBottomRight && TopCenterWasDragged == true){ SceneBoundingFrameRect.setBottomRight(OriginBottomRight); TopCenterWasDragged = false; qDebug() << "(2*)Correct Bottom, Top was Dragged!" << SceneBoundingFrameRect.bottomRight(); } QPointF topLeft2 = mapFromScene(SceneBoundingFrameRect.topLeft()); QPointF bottomRight2 = mapFromScene(SceneBoundingFrameRect.bottomRight()); QRectF NEWBoundingFrameRect2(topLeft2, bottomRight2); rectItem->setSelectorFrameBounds(NEWBoundingFrameRect2);
  • 0 Votes
    22 Posts
    2k Views
    AdithyaA

    Thanks guys it worked. python3 and readyReadStandardOutput(showed the log ,was expecting it to work like qDebig() and cout .My bad) . Sorry for bothering with wrong misconception.

  • 0 Votes
    5 Posts
    421 Views
    S

    @Pl45m4 Well thank you very much that does the job! Is there any way to make these menu items background heigher without changing the font size.

    EDIT:
    QMenuBar{
    min-height: 20px;
    }

  • 0 Votes
    5 Posts
    251 Views
    Pl45m4P

    @StudentScripter

    This example shows how a use case could look like

    https://doc.qt.io/qt-6/qtwidgets-graphicsview-basicgraphicslayouts-example.html

    But as you can see, the custom items inherit from QGraphicsLayoutItem and QGraphicsItem.
    So to properly use a QGraphicsItem in a layout, you have to inherit QGraphicsLayoutItem or use QGraphicsWidget directly... however I don't know how it fits in your existing logic/structure. It might break something... using layouts and "moving free / free resizable by user" is kinda counterproductive.

  • 0 Votes
    8 Posts
    360 Views
    S

    @Pl45m4 @SGaist Ok well i got is removed by setting the titlebar widget to fixed height of 1 and background transparent. This way it retains its dockability.

  • 0 Votes
    3 Posts
    270 Views
    JonBJ

    @StudentScripter
    You can put pointers to widgets into arrays/lists/maps as you please and use those for iterating through them etc.

    You can't get Designer to do that for you, so you have to manually populate the arrays/lists from the ui->... instance variables yourself (after setupUi()).

    You can also visit all widgets of a given type, without setting anything up, via topWidget->findChildren<QLineEdit *>().

    You can "group" widgets at design-time by e.g. creating a "holding" container QWidget or QFrame or QGroupBox if that helps. For buttons there is also QButtonGroup.

  • 0 Votes
    18 Posts
    658 Views
    S

    @StudentScripter said in Save Programm Settings internally + how to organize with folders?:

    QString filePath = "/path/to/your/settings.ini";

    If you want to use an ini file, please use QStandardPath with ConfigLocation and don't invent your own place where to put the ini file.

  • 0 Votes
    8 Posts
    519 Views
    S

    @JonB @SGaist @DerReisende Thank you all very much. Thats kinda tedious to do but guess thats how it is. Appreciate all your help and indeed mapFrom and mapToSource are what fixes my issue. :D Y'all have a nice day.