1. 程式人生 > >QGraphicsScene 管理 QGraphicsItem(單擊/選擇/移動/縮放/刪除)

QGraphicsScene 管理 QGraphicsItem(單擊/選擇/移動/縮放/刪除)

簡述

在圖形檢視框架中,QGraphicsScene 提供一個快速的介面,用於管理大量 item,QGraphicsItem 是場景中 item 的基類。

圖形檢視提供了一些典型形狀的標準 item,當然,我們也可以自定義 item。除此之外,QGraphicsItem 還支援以下特性:

  • 滑鼠按下、移動、釋放和雙擊事件,以及滑鼠懸浮事件、滾輪事件和上下文選單事件
  • 鍵盤輸入焦點和鍵盤事件
  • 拖放
  • 分組:通過父子關係,或 QGraphicsItemGroup
  • 碰撞檢測

下面,一起來看看 QGraphicsScene 對 QGraphicsItem 的管理,主要包括:單擊、選擇、移動、縮放、刪除等。

|

操作細節

為了實現以上功能,我們主要實現了 QGraphicsScene 和 QGraphicsItem 對應的事件,通過滑鼠和鍵盤來操作。

操作細節主要包括:

  • 選擇:點選左鍵、按 Shift 鍵可以單選,按下 Ctrl 可進行多選。
  • 新增:點選左鍵
  • 刪除:點選右鍵,刪除滑鼠下的 item;當按下 Ctrl 選擇多個 items 時,按下 Backspace 鍵,將選中的全部刪除。
  • 移動:點選左鍵,選擇 item,然後移動滑鼠;當按下 Ctrl 選擇多個 items 時,可以移動選中的 items。
  • 縮放:按 Alt 鍵,然後滑鼠拖拽 item 的邊界。

在對應操作的事件中,我們輸出了一些除錯資訊,以便跟蹤。

示例

效果

這裡寫圖片描述

原始碼

custom_item.h:

#ifndef CUSTOM_ITEM_H
#define CUSTOM_ITEM_H

#include <QGraphicsRectItem>
#include <QGraphicsScene>

//QGraphicsScene管理QGraphicsItem(單擊/選擇/移動/縮放/刪除)
// 自定義 Item
class CustomItem : public QGraphicsRectItem
{
public:
    explicit CustomItem(QGraphicsItem *parent = 0
); protected: // Shift+左鍵:進行選擇 Alt:準備縮放 void mousePressEvent(QGraphicsSceneMouseEvent *event); // Alt+拖拽:進行縮放 移動 void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); // 使item可使用qgraphicsitem_cast int type() const; private: QPointF m_centerPointF; bool m_bResizing; }; // 自定義 Scene class CustomScene : public QGraphicsScene { protected: // 左鍵:新增item 右鍵:移除item void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); // Backspace鍵移除item void keyPressEvent(QKeyEvent *event); }; #endif // CUSTOM_ITEM_H

custom_item.cpp:

#include <QKeyEvent>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include "custom_item.h"

// 自定義 Item
CustomItem::CustomItem(QGraphicsItem *parent)
    : QGraphicsRectItem(parent)
{
    // 畫筆 - 邊框色
    QPen p = pen();
    p.setWidth(2);
    p.setColor(QColor(0, 160, 230));

    setPen(p);
    // 畫刷 - 背景色
    setBrush(QColor(247, 160, 57));

    // 可選擇、可移動
    setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
}

void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        if (event->modifiers() == Qt::ShiftModifier) {
            qDebug() << "Custom item left clicked with shift key.";
            // 選中 item
            setSelected(true);
        } else if (event->modifiers() == Qt::AltModifier) {
            qDebug() << "Custom item left clicked with alt key.";
            // 重置 item 大小
            double radius = boundingRect().width() / 2.0;
            QPointF topLeft = boundingRect().topLeft();
            m_centerPointF = QPointF(topLeft.x() + pos().x() + radius, topLeft.y() + pos().y() + radius);
            QPointF pos = event->scenePos();
            qDebug() << boundingRect() << radius << this->pos() << pos << event->pos();
            double dist = sqrt(pow(m_centerPointF.x()-pos.x(), 2) + pow(m_centerPointF.y()-pos.y(), 2));
            if (dist / radius > 0.8) {
                qDebug() << dist << radius << dist / radius;
                m_bResizing = true;
            } else {
                m_bResizing = false;
            }
        } else {
            qDebug() << "Custom item left clicked.";
            QGraphicsItem::mousePressEvent(event);
            event->accept();
        }
    } else if (event->button() == Qt::RightButton) {
        qDebug() << "Custom item right clicked.";
        event->ignore();
    }
}

void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {
        QPointF pos = event->scenePos();
        double dist = sqrt(pow(m_centerPointF.x()-pos.x(), 2) + pow(m_centerPointF.y()-pos.y(), 2));
        setRect(m_centerPointF.x()-this->pos().x()-dist, m_centerPointF.y()-this->pos().y()-dist, dist*2, dist*2);
    } else if(event->modifiers() != Qt::AltModifier) {
        qDebug() << "Custom item moved.";
        QGraphicsItem::mouseMoveEvent(event);
        qDebug() << "moved" << pos();
    }
}

void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {
        m_bResizing = false;
    } else {
        QGraphicsItem::mouseReleaseEvent(event);
    }
}

int CustomItem::type() const
{
    return UserType + 1;
}

// 自定義 Scene
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "Custom scene clicked.";
    QGraphicsScene::mousePressEvent(event);
    if (!event->isAccepted()) {
        if (event->button() == Qt::LeftButton) {
            // 在 Scene 上新增一個自定義 item
            QPointF point = event->scenePos();
            CustomItem *item = new CustomItem();
            item->setRect(point.x()-25, point.y()-25, 60, 60);
            addItem(item);
        } else if (event->button() == Qt::RightButton) {
            // 檢測游標下是否有 item
            QGraphicsItem *itemToRemove = NULL;
            foreach (QGraphicsItem *item, items(event->scenePos())) {
                if (item->type() == QGraphicsItem::UserType+1) {
                    itemToRemove = item;
                    break;
                }
            }
            // 從 Scene 上移除 item
            if (itemToRemove != NULL)
                removeItem(itemToRemove);
        }
    }
}

void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "Custom scene moved.";
    QGraphicsScene::mouseMoveEvent(event);
}

void CustomScene::keyPressEvent(QKeyEvent *event) {
    if (event->key() == Qt::Key_Backspace) {
        // 移除所有選中的 items
        qDebug() << "selected items " << selectedItems().size();
        while (!selectedItems().isEmpty()) {
            removeItem(selectedItems().front());
        }
    } else {
        QGraphicsScene::keyPressEvent(event);
    }
}

使用很簡單,將 item 新增至 scene 中,通過 view 顯示即可。

#include <QApplication>
#include <QGraphicsView>
#include "custom_item.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 建立 item
    CustomItem *pItem = new CustomItem();
    pItem->setRect(20, 20, 60, 60);

    // 將 item 新增至場景中
    CustomScene scene;
    scene.setSceneRect(0, 0, 400, 300);
    scene.addItem(pItem);

    // 為檢視設定場景
    QGraphicsView view;
    view.setScene(&scene);
    view.show();

    return a.exec();
}

相關推薦

QGraphicsScene 管理 QGraphicsItem/選擇/移動//刪除

簡述 在圖形檢視框架中,QGraphicsScene 提供一個快速的介面,用於管理大量 item,QGraphicsItem 是場景中 item 的基類。 圖形檢視提供了一些典型形狀的標準 item,當然,我們也可以自定義 item。除此之外,QGraphi

swift--觸摸UITouch事件移動,擡起

send nss spa bject rst sse sta location 耗時 觸摸事件: UITouch:一個手機第一次點擊屏幕,會形成一個UITouch對象,知道離開銷毀。表示觸碰。UITouch對象能表明當前手指觸碰的屏幕位置、狀態,狀態分為開始觸碰、移動、離開

vue搭建後臺管理頁面左側導航,切換右側內容

htm right 後臺 opd imp page con com ng- home.vue頁面 <template> <div style="background-color: #EBEBEB;min-height:900px">

簡單java例模式多次,如何讓視窗只顯示一次

1.將實現功能的建構函式設為private 2.在寫一個public的構造方法: 如下: private static AddPerson addPerson = null;  public static  synchronized AddPerson GetInstance

統一認證管理系統點登入系統sso 淺談

        我所在的公司比較大,內部的各種管理系統和業務系統比較多,然而所有的系統都可以用公司的OA的員工工號和密碼直接進行登入 (當然登入介面都是一個就是內部OA門戶)。從進入公司以來我就一直有個問題,這是怎麼做到的?畢竟假如每個系統一套資料庫,那麼所有的系統都得同步O

做可雙執行的Jar包右鍵選擇用java(TM)開啟

例如d盤目錄下有一個資料夾包(package)名為MyPackage,包資料夾下是shutDown.java檔案。 步驟1:在d盤新建一個資料夾隨便取名為abc並將MyPackage資料夾包拖放到abc資料夾中。 步驟2:在dos中進入該abc目錄,編譯該.java檔案,命

Android 手勢識別 擡起 短按 長按 滾動 滑動

對於觸控式螢幕,其原生的訊息無非按下、擡起、移動這幾種,我們只需要簡單過載onTouch或者設定觸控偵聽器setOnTouchListener即可進行處理。不過,為了提高我們的APP的使用者體驗,有時候我們需要識別使用者的手勢,Android給我們提供的手勢識別工具Ge

表格變色行,把當行的選按鈕(radio)設為選中狀態,並應用當前樣式

需求:單擊行,自動選中當前行中的單選框按鈕。html頁面: <table id="tbList" class="table footable" data-sort="false">

Linux centos 跳過管理員密碼進行登錄用戶模式、救援模式

remount 無法 linu 單用戶 blog int 成功 程序 bios   這裏列舉了兩種更改或者取消管理員密碼登錄Linux系統的方法,其實兩種方法類似,都是想方設法跳過用戶認定,直接更改用戶文件、更改密碼的過程。   為了跳過系統正常啟動過程中的某些步驟,必須知

數據庫:MySQL表的表記錄的操作

基礎上 des 別名 order by data database values 生成 結果 一、表記錄的增刪改查 1、增加表記錄 <1>插入一條記錄:   insert [into] tab_name (field1,filed2,.......) val

排序算法冒泡,選擇,插入,快速查找算法二分,快速

元素 快速查找 冒泡排序 比較 簡單 目標 記錄 rec 向下取整                         四種排序算法 1.冒泡排序   思路分析:從前往後相鄰的兩個數一次進行比較,大的往下沈,小的網上 冒。當相鄰的兩個數的比較後發現他們的排序與排序要求相反,就互

js遞歸渲染子節點父節點展示子節點

ner container com 循環 cnblogs each ber 獲取 r+ 需求概況如下:點擊某個文件夾,顯示該文件夾下的子文件夾。文件夾的層級和數量不固定。 在這裏,我簡單準備了一下數據結構,來模擬這個效果:var nodes=[ {

設計模式:對象生成例、工廠、抽象工廠

添加 對象實例 log return ray 靜態 學習 線程 tco 對象的創建有時會成為面向對象設計的一個薄弱環節。我們可以使用多種面向對象設計方案來增加對象的創建的靈活性。 單例模式:生成一個且只生成一個對象實例的特殊類 工廠方法模式:構建創建者類的繼承層級

Android系統下用js自定義gesture事件仿ios實現移動端事件一致

initial path acc mtab uil 查看 sans fault default 一、手勢事件 下面二維碼是一個實例dome,可掃碼直接查看: 在ios系統中,系統自帶了gesture事件,兩個手指操作的時候,就會產生一下三種手勢

python文件封裝成*.exe文件文件和多文件

-- 黑板 workday 程序包 代碼 拷貝 4.5 hole nic 環境:win10 64位 python3.7 單*.py文件打包Python GUI:程序打包為exe 一、安裝Pyinstaller,命令pip install Pyinstaller,(大

kuangbin專題七 HDU1754 I Hate It 點修改維護最大值

cond color include 專題 一次 span 修改 信息 \n 很多學校流行一種比較的習慣。老師們很喜歡詢問,從某某到某某當中,分數最高的是多少。 這讓很多學生很反感。 不管你喜不喜歡,現在需要你做的是,就是按照老師的要求,寫一個程序,模擬老師的

訊息佇列:Ubuntu16.04安裝和Web頁面管理RabbitMQ樓主親測、真實有效

RabbitMQ 總來來說,RabbitMQ的安裝還是有一些難度的。不同的方式,安裝的方法也是完全不一樣,還要解決蠻多依賴。加上現在有些網站,極其不負責,很多博文都沒有經過測試檢驗就直接發出來的。樓主來親測一下,希望能對大家有好的幫助。 一、安裝前的準備 要確保,你有Erla

學生消費記錄管理系統C語言 結構體, 連結串列

自己在寒假練手的小專案  本系統要實現的功能: 1.     消費記錄存在檔案fee.txt中, 每一條記錄包括一個消費的交易日期、入賬日期、交易額、交易後餘額 2.     (1)使用者能夠查詢自己

hdu1754I Hate It解題報告---線段樹點替換 & 區間最值

                                        I Hate I

自定義ImageView: 實現自由 ,自由移動後的圖片 .雙放大與縮小圖片 相容ViewPager

直接擼程式碼, 複製就能用 package com.zhf.baselibrary.view; import android.annotation.SuppressLint; import android.content.Context; import android.graphi