根据比例计算QGraphicsTextItem字体大小

发布于 2024-09-11 00:08:09 字数 1657 浏览 0 评论 0原文

我在 QGraphicsScene 上有 QGraphicsTextItem 对象。用户可以通过拖动角来缩放QGraphicsTextItem对象。 (我使用自定义的“转换编辑器”来执行此操作。)用户还可以通过从属性面板更改字体大小来更改 QGraphicsTextItem 的大小。我想做的是统一这些,以便当用户通过用鼠标拖动角来缩放对象时,它实际上在幕后计算“使生成的对象适合目标大小并保持目标大小所需的字体大小”比例因子为 1.0?”

我现在要做的是使用 QGraphicsItem::mouseMoveEvent 让对象正常缩放,然后触发 QGraphicsItem::mouseReleaseEvent 中的 FinalizeMapScale 方法一次鼠标比例完成。然后,此方法应将字体更改为适当的大小并将比例设置回 1.0。

我有一个似乎有效的解决方案,但我并不热衷于它。我对 Qt 和 C++ 都比较陌生,所以希望有任何评论或更正。

  • 有没有更好的方法来构建这整个事情?
  • 是否有 Qt 方法已经可以做到这一点?
  • 我的方法是否正确,但有一些 Qt 或 C++ 错误?

请随意在下面评论我的答案,提交您自己的首选解决方案。谢谢!

[编辑] 根据评论中的要求,这里是缩放代码的基础知识。实际上,我们走了不同的方向,因此不再使用此代码(以及下面的代码)。此代码位于 mouseMoveEvent 方法中,如果在右下角“热点”中单击鼠标,则之前已在 mousePressEvent 中将“scaling_”标志设置为 true。请注意,此代码位于装饰器 QGraphicsItem 中,该装饰器保存指向正在缩放的​​目标的指针。这种抽象对于我们的项目来说是必要的,但对于大多数用途来说可能有点过分了。

void TransformDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
  ...
  if (scaling_) {
    QGraphicsItem *target_item = target_->AsQGraphicsItem();
    target_item->setTransformOriginPoint(0.0, 0.0);
    QPointF origin_scene = mapToScene(target_item->transformOriginPoint());
    QPointF scale_position_scene = mapToScene(event->pos());
    qreal unscaled_width = target_item->boundingRect().width();
    qreal scale_x = (scale_position_scene.x() - origin_scene.x()) / unscaled_width;
    if (scale_x * unscaled_width < kMinimumSize) {
      scale_x = kMinimumSize / unscaled_width;
    }
    target_item->setScale(scale_x);
  } else {
    QGraphicsObject::mouseMoveEvent(event);
  }
}

I have QGraphicsTextItem objects on a QGraphicsScene. The user can scale the QGraphicsTextItem objects by dragging the corners. (I am using a custom "transformation editor" to do this.) The user can also change the size of the QGraphicsTextItem by changing the font size from a property panel. What I would like to do is unify these so that when the user scales the object by dragging the corner with the mouse, behind the scenes it actually is calculating "What size font is necessary to make the resulting object fit the target size and keep the scale factor at 1.0?"

What I am doing now is letting the object scale as normal using QGraphicsItem::mouseMoveEvent and then triggering a FinalizeMapScale method in QGraphicsItem::mouseReleaseEvent once the mouse scale is complete. This method should then change the font to the appropriate size and set the scale back to 1.0.

I have a solution that appears to be working, but I'm not crazy about it. I'm relatively new to both Qt and C++, so would appreciate any comments or corrections.

  • Is there a better way to architect this whole thing?
  • Are there Qt methods that already do this?
  • Is my method on the right track but has some Qt or C++ errors?

Feel free to comment on my answer below on submit your own preferred solution. Thanks!

[EDIT] As requested in comment, here is the basics of the scaling code. We actually went a different direction with this, so this code (and the code below) is no longer being used. This code is in the mouseMoveEvent method, having previously set a "scaling_" flag to true in mousePressEvent if the mouse was clicked in the bottom-right "hot spot". Note that this code is in a decorator QGraphicsItem that holds a pointer to the target it is scaling. This abstraction was necessary for our project, but is probably overkill for most uses.

void TransformDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
  ...
  if (scaling_) {
    QGraphicsItem *target_item = target_->AsQGraphicsItem();
    target_item->setTransformOriginPoint(0.0, 0.0);
    QPointF origin_scene = mapToScene(target_item->transformOriginPoint());
    QPointF scale_position_scene = mapToScene(event->pos());
    qreal unscaled_width = target_item->boundingRect().width();
    qreal scale_x = (scale_position_scene.x() - origin_scene.x()) / unscaled_width;
    if (scale_x * unscaled_width < kMinimumSize) {
      scale_x = kMinimumSize / unscaled_width;
    }
    target_item->setScale(scale_x);
  } else {
    QGraphicsObject::mouseMoveEvent(event);
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

君勿笑 2024-09-18 00:08:09

请不要对循环与退出结构进行圣战。我们对此感到满意。

void MapTextElement::FinalizeMapScale() {

  // scene_document_width is the width of the text document as it appears in
  // the scene after scaling. After we are finished with this method, we want
  // the document to be as close as possible to this width with a scale of 1.0.
  qreal scene_document_width = document()->size().width() * scale();

  QString text = toPlainText();

  // Once the difference between scene_document_width and the calculated width
  // is below this value, we accept the new font size.
  const qreal acceptable_delta = 1.0;

  // If the difference between scene_document_width and the calculated width is
  // more than this value, we guess at the new font size by calculating a new
  // scale factor. Once it is beneath this value, we creep up (or down) by tiny
  // increments. Without this, we would sometimes incur long "back and forth"
  // loops when using the scale factor.
  const qreal creep_delta = 8.0;
  const qreal creep_increment = 0.1;

  QScopedPointer<QTextDocument> test_document(document()->clone());
  QFont new_font = this->font();
  qreal delta = 0.0;

  // To prevent infinite loops, we store the font size values that we try.
  // Because of the unpredictable (at least to me) relationship between font
  // point size and rendering size, this was the only way I could get it to
  // work reliably.
  QList<qreal> attempted_font_sizes;

  while (true) {

    test_document->setDefaultFont(new_font);
    delta = scene_document_width - test_document->size().width();

    if (std::abs(delta) <= acceptable_delta ||
        attempted_font_sizes.contains(new_font.pointSizeF())) {
      break;
    }

    attempted_font_sizes.append(new_font.pointSizeF());

    qreal new_font_size = 0.0;
    if (std::abs(delta) <= creep_delta) {
      new_font_size = delta > 0.0 ? new_font.pointSizeF() + creep_increment
                                  : new_font.pointSizeF() - creep_increment;
    } else {
      new_font_size = new_font.pointSizeF()
                      * scene_document_width
                      / test_document->size().width();
    }
    new_font.setPointSizeF(new_font_size);
  }

  this->setFont(new_font);
  this->setScale(1.0);
}

Please no holy wars about the loop-with-exit construct. We're comfortable with it.

void MapTextElement::FinalizeMapScale() {

  // scene_document_width is the width of the text document as it appears in
  // the scene after scaling. After we are finished with this method, we want
  // the document to be as close as possible to this width with a scale of 1.0.
  qreal scene_document_width = document()->size().width() * scale();

  QString text = toPlainText();

  // Once the difference between scene_document_width and the calculated width
  // is below this value, we accept the new font size.
  const qreal acceptable_delta = 1.0;

  // If the difference between scene_document_width and the calculated width is
  // more than this value, we guess at the new font size by calculating a new
  // scale factor. Once it is beneath this value, we creep up (or down) by tiny
  // increments. Without this, we would sometimes incur long "back and forth"
  // loops when using the scale factor.
  const qreal creep_delta = 8.0;
  const qreal creep_increment = 0.1;

  QScopedPointer<QTextDocument> test_document(document()->clone());
  QFont new_font = this->font();
  qreal delta = 0.0;

  // To prevent infinite loops, we store the font size values that we try.
  // Because of the unpredictable (at least to me) relationship between font
  // point size and rendering size, this was the only way I could get it to
  // work reliably.
  QList<qreal> attempted_font_sizes;

  while (true) {

    test_document->setDefaultFont(new_font);
    delta = scene_document_width - test_document->size().width();

    if (std::abs(delta) <= acceptable_delta ||
        attempted_font_sizes.contains(new_font.pointSizeF())) {
      break;
    }

    attempted_font_sizes.append(new_font.pointSizeF());

    qreal new_font_size = 0.0;
    if (std::abs(delta) <= creep_delta) {
      new_font_size = delta > 0.0 ? new_font.pointSizeF() + creep_increment
                                  : new_font.pointSizeF() - creep_increment;
    } else {
      new_font_size = new_font.pointSizeF()
                      * scene_document_width
                      / test_document->size().width();
    }
    new_font.setPointSizeF(new_font_size);
  }

  this->setFont(new_font);
  this->setScale(1.0);
}
计㈡愣 2024-09-18 00:08:09

看待问题的另一种方法是:Qt 已经缩放了字体,我需要向用户显示的有效字体大小是多少(用户看到的,而不是文本项中设置的字体大小)作为他们的选择新字体大小?这只是一个替代方案,您仍然需要类似于您的计算。

我有类似的问题。我有一个文本项目,我希望它像其他单位图形项目一样具有单位大小(一个像素大小)(然后用户可以缩放它们。)需要设置什么字体(setPointSize)? (还有什么setTextWidth和什么setDocumentMargin?)这种设计的优点是您不需要将文本项的缩放与任何其他形状的图形项的缩放不同地对待。 (但我还没有让它工作。)

另外,用户界面问题:如果用户更改字体大小,项目的大小是否会更改?或者它是否保持相同的大小,并且文本换行方式不同,在文本末尾留下或多或少的空白?当用户追加新文本时,字体大小是否会发生变化以使所有文本适合形状的大小,或者形状大小是否会增加以容纳更多文本?换句话说,它更像是流程图应用程序(其中形状大小固定且字体缩小),还是像文字处理应用程序(其中字体大小恒定且形状(页数)增长)?

Another way to look at the problem is: Qt has scaled the font, what is the effective font size (as it appears to the user, not the font size set in the text item) that I need to display to the user as their choice of new font size? This is just an alternative, you still need a calculation similar to yours.

I have a similar problem. I have a text item that I want to be unit size (one pixel size) like my other unit graphic items (and then the user can scale them.) What font (setPointSize) needs to be set? (Also what setTextWidth and what setDocumentMargin?) The advantage of this design is that you don't need to treat the scaling of text items different than the scaling of any other shape of graphics item. (But I don't have it working yet.)

Also, a user interface issue: if the user changes the font size, does the item change size? Or does it stay the same size and the text wrap differently, leaving more or less blank space at the end of the text? When the user appends new text, does the font size change so all the text fits in the size of the shape, or does the shape size grow to accommodate more text? In other words, is it more like a flowchart app (where the shape size is fixed and the font shrinks), or like a word processor app (where the font size is constant and the shape (number of pages) grows?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文