2012-07-12 32 views
7

Tôi có một QGraphicsTextItem được đặt vào một QGraphicsItem. Tôi muốn QGraphicsTextItem luôn nằm trực tiếp trên QGraphicsItem, nhưng tôi cũng muốn văn bản vẫn giữ nguyên kích thước khi hệ số tỷ lệ nhỏ hơn 1, tức là văn bản vẫn ở kích thước nó ở hệ số tỷ lệ 1 ngay cả khi đồ họa gốc mục được thu nhỏ lại. Tôi đã thấy rằng đặt cờ QGraphicsItem::ItemIgnoresTransformations thành true khi hệ số tỷ lệ dưới 1 sẽ thực hiện thủ thuật để giữ lại kích thước.Duy trì vị trí con tương đối sau khi áp dụng QGraphicsItem :: ItemIgnoresTransformations

Nhưng dường như tôi không tìm được cách để giữ vị trí của văn bản luôn ở trên QGraphicsItem. Có cách nào để làm việc này không? Tôi đã thử sử dụng chức năng deviceTransform(), nhưng văn bản vẫn chuyển động khỏi QGraphicsItem khi tôi cuộn ra. Điều tồi tệ hơn là một số mục văn bản bắt đầu “cười khúc khích”, tức là họ bắt đầu liên tục thay đổi vị trí của họ từ trước đến giờ, để trông giống như họ đang run rẩy. Nếu đây là chức năng tôi cần sử dụng, tôi đoán tôi không biết cách sử dụng nó đúng cách.

Trong constructor của QGraphicsItem của tôi, tôi đã thêm một QGraphicsTextItem:

fTextItem = new QGraphicsTextItem(getName(), this); 
fTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations); 

Dưới đây là đoạn mã từ chức năng sơn của QGraphicsItem

qreal lod = painter->worldTransform().m22(); 
if(lod <= 1.0) { 
    fTextItem-setFlag(QGraphicsItem::ItemIgnoresTransformations); 
    fTextItem->setPos(fTextItem->deviceTransform(view-viewportTransform()).inverted().map(view->mapFromScene(mapToScene(0,0)))); 
} else { 
    fTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations, false); 
    fTextItem->setPos(0, 0); 
} 
+0

không nên bạn thiết lập QGraphicsTextItem đối tượng cờ để bỏ qua sự biến đổi cha mẹ thay vì các QGraphicsItem? – ArmenB

Trả lời

4

Disclaimer: đây có thể là quá mức cần thiết cho những gì bạn đang cố gắng làm. Chúng tôi đã có một số hạn chế bổ sung trong dự án của chúng tôi khiến giải pháp này trở nên dễ dàng nhất đối với chúng tôi.

Chúng tôi phải làm điều gì đó tương tự trong một dự án và kết quả là dễ nhất để chúng tôi không sử dụng ItemIgnoresTransformations và thay vào đó sẽ tự biến đổi. Đây là hàm chính mà chúng tôi sử dụng để tạo một biến đổi chỉ dịch (không mở rộng) để vẽ một mục tại một vị trí cụ thể. Bạn có thể sửa đổi nó cho việc sử dụng của bạn.

static QTransform GenerateTranslationOnlyTransform(
    const QTransform &original_transform, 
    const QPointF &target_point) { 
    // To draw the unscaled icons, we desire a transform with scaling factors 
    // of 1 and shearing factors of 0 and the appropriate translation such that 
    // our icon center ends up at the same point. According to the 
    // documentation, QTransform transforms a point in the plane to another 
    // point using the following formulas: 
    // x' = m11*x + m21*y + dx 
    // y' = m22*y + m12*x + dy 
    // 
    // For our new transform, m11 and m22 (scaling) are 1, and m21 and m12 
    // (shearing) are 0. Since we want x' and y' to be the same, we have the 
    // following equations: 
    // m11*x + m21*y + dx = x + dx[new] 
    // m22*y + m12*x + dy = y + dy[new] 
    // 
    // Thus, 
    // dx[new] = m11*x - x + m21*y + dx 
    // dy[new] = m22*y - y + m12*x + dy 
    qreal dx = original_transform.m11() * target_point.x() 
      - target_point.x() 
      + original_transform.m21() * target_point.y() 
      + original_transform.m31(); 
    qreal dy = original_transform.m22() * target_point.y() 
      - target_point.y() 
      + original_transform.m12() * target_point.x() 
      + original_transform.m32(); 

    return QTransform::fromTranslate(dx, dy); 
} 

Để sử dụng, đi QPainter chuyển đổi được truyền cho phương thức sơn và làm điều gì đó như:

painter->save(); 
painter->setTransform(GenerateTranslationOnlyTransform(painter->transform(), 
                 some_point)); 
// Draw your item. 
painter->restore(); 
+0

Tại thời điểm này trông giống như overkill cho vấn đề của tôi :) Nhưng vững chắc mặt đất lên cách tiếp cận nếu không có cách dễ dàng. Cảm ơn rất nhiều! – KD07

+0

Tôi đã thử sử dụng phép chuyển đổi tùy chỉnh này trong ứng dụng của mình. Nó hoạt động như xa chuyển đổi là mối quan tâm nhưng vẫn vị trí mục của tôi là tắt. Tôi muốn mục con của tôi (có chuyển đổi tùy chỉnh) để ở tại (0,0) điểm của phụ huynh. Tôi vượt qua some_point như (0,0). – KD07

6

Đề nghị của tôi là để phân lớp QGraphicsSimpleTextItem theo cách này:

class TextItem 
    : public QGraphicsSimpleTextItem 
{ 
public: 
    TextItem(const QString &text) 
     : QGraphicsSimpleTextItem(text) 
    { 

    } 
    void paint(QPainter *painter, 
     const QStyleOptionGraphicsItem *option, QWidget *widget) 
    { 
     painter->translate(boundingRect().topLeft()); 
     QGraphicsSimpleTextItem::paint(painter, option, widget); 
     painter->translate(-boundingRect().topLeft()); 
    } 
    QRectF boundingRect() const 
    { 
     QRectF b = QGraphicsSimpleTextItem::boundingRect(); 
     return QRectF(b.x()-b.width()/2.0, b.y()-b.height()/2.0, 
      b.width(), b.height()); 
    } 
}; 
QGraphicsSimpleTextItem *mText = new TextItem("Item"); 
scene()->addItem(mText); 
mText->setFlag(QGraphicsItem::ItemIgnoresTransformations, true); 
mText->setPos(itemToFollow->pos()); 
+0

Lưu ý rằng KD07 đã hỏi về 'QGraphicsTextItem' (hỗ trợ định dạng HTML/văn bản đa dạng thức). Có thể tìm thấy một 'QGraphicsSimpleTextItem' khác trên [Qt Center] (http://www.qtcentre.org/threads/51168-QGraphicsTextItem-center-based-coordinates). – handle

0

Thêm vào câu trả lời của Dave Mateer, tôi nghĩ sẽ hữu ích khi thêm vào trong một số trường hợp, bạn cũng nên duy trì hình chữ nhật giới hạn thích hợp (cũng như hình dạng) của đối tượng. Đối với tôi, tôi cần phải sửa đổi boundingRect() một chút quá cho hành vi lựa chọn đối tượng thích hợp. Hãy nhớ rằng các recting bound của đối tượng sẽ được thu nhỏ và chuyển đổi như bình thường nếu chúng ta KHÔNG sử dụng cờ ItemIgnoresTransformations. Vì vậy, chúng ta cũng cần phải rescale boundingRect để duy trì hiệu ứng độc lập của khung nhìn.

Để duy trì hình chữ nhật giới hạn xem độc lập hóa ra khá dễ dàng: chỉ cần lấy hệ số tỷ lệ từ deviceTransform(m_view->viewportTransform()).inverted().m11() và nhân hằng số này với hình chữ nhật giới hạn tọa độ cục bộ của bạn. Ví dụ:

qreal m = this->deviceTransform(m_view->viewportTransform()).inverted().m11(); 
return QRectF(m*(m_shapeX), m*(m_shapeY), 
       m*(m_shapeR), m*(m_shapeR)); 
0

Câu trả lời hay của Dave Mateer! Tôi đã có vấn đề mà tôi muốn xác định một yếu tố quy mô khác nhau ở các mức thu phóng khác nhau.Đây là cách tôi đã làm nó:

void MyGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem* option, QWidget* widget) 
{ 
    //save painter for later operations 
    painter->save(); 
    QTransform originalTransform = painter->transform(); 
    QPointF originalCenter = rect().center(); 
    qreal dx = originalTransform.m11() * originalCenter.x() + originalTransform.m21() * originalCenter.y() + originalTransform.m31(); 
    qreal dy = originalTransform.m22() * originalCenter.y() + originalTransform.m12() * originalCenter.x() + originalTransform.m32(); 
    //normally our target scale factor is 1, meaning the item has keeps its size, regardless of zoom 
    //we adjust the scale factor though when the item is smaller than one pixel in comparison to the background image 
    qreal factor = 1.0; 
    //check if scale factor if bigger that the item size, and thus it occupies less that a pixel in comparision to the background image 
    if (rect().width() < originalTransform.m11()) { 
     //calculate adjusted scale factor 
     factor = originalTransform.m11()/rect().width(); 
    } 
    //adjust position according to scale factor 
    dx -= factor * originalCenter.x(); 
    dy -= factor * originalCenter.y(); 
    //set the new transform for painting 
    painter->setTransform(QTransform::fromScale(factor, factor) * QTransform::fromTranslate(dx, dy)); 
    //now paint... 
    QGraphicsXYZItem::paint(painter, option, widget); 
    //restore original painter 
    painter->restore(); 
} 

Bạn cần phải điều chỉnh hình chữ nhật bounding quá trong trường hợp đó:

QRectF MyGraphicsItem::boundingRect() const 
{ 
    QRectF rect = QGraphicsEllipseItem::boundingRect(); 
    //this is a bit hackish, let me know if you know another way... 
    if (scene() != NULL && scene()->views().at(0) != NULL) 
    { 
     //get viewport transform 
     QTransform itemTransform = scene()->views().at(0)->transform(); 
     QPointF originalCenter = rect.center(); 
     //calculate back-projected original size of item 
     qreal realSizeX = rect.width()/itemTransform.m11(); 
     qreal realSizeY = rect.height()/itemTransform.m11(); 
     //check if scale factor is bigger that the item size, and thus it occupies less that a pixel in comparison 
     //to the background image and adjust size back to equivalent of 1 pixel 
     realSizeX = realSizeX < 1.0 ? 1.0 : realSizeX; 
     realSizeY = realSizeY < 1.0 ? 1.0 : realSizeY; 
     //set adjusted position and size according to scale factor 
     rect = QRectF(rect.center().x() - realSizeX/2.0, rect.center().y() - realSizeY/2.0, realSizeX, realSizeY); 
    } 
    return rect; 
} 

Với giải pháp này, các hạng mục công trình rất tốt trong trường hợp của tôi.

0

đây là một giải pháp tôi đã nghĩ ra rất vừa phải phức tạp:

1) Lấy boundingRect() của phụ huynh và bản đồ nó để cảnh 2) lấy X tối thiểu và Y của danh sách này của điểm, này là nguồn gốc thực sự của sản phẩm của bạn, trong cảnh phối 3) thiết lập vị trí của đứa trẻ

trong Pyside:

br = parent.mapToScene(parent.boundingRect()) 
    realX = min([item.x() for item in br]) 
    realY = min([item.y() for item in br]) 
    child.setPos(parent.mapFromScene(realX, realY)) #modify according to need 
0

tôi đã tìm thấy một giải pháp, mà không liên quan đến rối tung với bất kỳ biến đổi hoặc bởi tay chia tỷ lệ/định vị. Có một gợi ý trong mô tả QGraphicsItem::ItemIgnoresTransformations cờ:

QGraphicsItem::ItemIgnoresTransformations

Mục bỏ qua biến đổi di truyền (ví dụ, vị thế của mình là vẫn neo vào mẹ, nhưng phụ huynh hoặc xoay chế độ xem, zoom hoặc biến đổi cắt được bỏ qua). [...]

Và đó chính là chìa khóa! Chúng tôi cần hai mục: phụ huynh sẽ giữ vị trí tương đối (không có bất kỳ cờ nào được đặt) và mục con sẽ vẽ bản vẽ tại điểm (0,0) của cha mẹ (với bộ cờ QGraphicsItem::ItemIgnoresTransformations). Đơn giản như thế!

Tôi đã gói gọn chức năng này thành một lớp duy nhất - đây là một số mã:

#include <QGraphicsItem> 
#include <QPainter> 

class SampleShape : public QGraphicsItem 
{ 
private: 
    /* This class implements shape drawing */ 
    class SampleShapeImpl : public QGraphicsItem 
    { 
    public: 
     SampleShapeImpl (qreal len, QGraphicsItem *parent = nullptr) 
      : QGraphicsItem(parent), m_len(len) 
     { 
      /* ignore transformations (!) */ 
      setFlag(QGraphicsItem::ItemIgnoresTransformations); 
     } 

     QRectF boundingRect (void) const override 
     { 
      /* sample bounding rectangle */ 
      return QRectF(-m_len, -m_len, m_len*2, m_len*2); 
     } 

     void paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override 
     { 
      /* draw a shape, (0,0) is an anchor */ 
      painter->drawLine(0, -m_len, 0, m_len); 
      painter->drawLine(-m_len, 0, m_len, 0); 
      // ... 
     } 

    private: 
     qreal m_len; // sample shape parameter 
    }; 

public: 
    /* This is actually almost an empty class, you only need to set 
    * a position and pass any parameters to a SampleShapeImpl class. 
    */ 
    SampleShape (qreal x, qreal y, qreal len, QGraphicsItem *parent = nullptr) 
     : QGraphicsItem(parent), m_impl(len, this) // <-- IMPORTANT!!! 
    { 
     /* set position at (x, y), view transformations will apply */ 
     setPos(x, y); 
    } 

    QRectF boundingRect (void) const override 
    { 
     return QRectF(); // it's just a point, no size 
    } 

    void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override 
    { 
     // empty, drawing is done in SampleShapeImpl 
    } 

private: 
    SampleShapeImpl m_impl; 
}; 
Các vấn đề liên quan