第八章 坐标系统变换

2023-11-06

一、使用QPainter绘图

http://blog.csdn.net/hellozex/article/details/78361311


二、坐标系统变换

    在QPainter的默认坐标系中,点(0,0)位于坐标绘制设备的左上角,x坐标向右,y坐标向下,类似图像处理中的图像像素排列,默认每个像素占用1*1大小的区域。
    理论上,像素的中心取决于版像素坐标哦。例如,窗口部件的左上角像素覆盖了点(0,0)到点(1,1)的区域,它的中心在(0.5,0.5)位置。如果告诉QPainter绘制一个像素,例如(100,100),它会相应地在两个方向做+0.5的偏移,使得像素点的中心位置在(100.5,100.5)。这一差别初看起来理论性很强但是在实践中却是非常重要。首先,只有当反走样无效时才偏移+0.5;如果反走样有效,并且我们试图在(100,100)绘制一个黑色像素,实际上QPainter会在(99.5,99.5)、(99.5,100.5)、(100.5,99.5)、(100.5,100.5)四个像素着浅灰色,给人的印象就是正好位于四个点的交点处,如果不需要这种效果,可以通过指定半像素坐标或者通过便宜QPainter(+0.5,+0.5)来避免这种效果的出现。
    这种窗口-视口机制对于编写独立于绘制设备大小和分辨率的绘制代码很有用的。例如,如果想让逻辑坐标从(-50,-50)到(+50,+50),并且(0,0)在中间,可以这样设置窗口:
painter.setWindow(-50, -50, 100, 100);
前面参数指定原点,后面是宽和高;这就意味着逻辑坐标(-50,-50)对应物理坐标(0,0),而逻辑坐标(+50,+50)对应物理坐标(320,200),如图:
    世界变换实在窗口-视口转换之外使用的变换矩阵。它允许使用移动、缩放、旋转或者拉伸绘制的项。例如,想要45°角绘制文本,他可以使用这段代码:
QTransform tramsform;
transform.rotate(+45.0);
painter.setWorldTransform(transform);
painter.drawText(pos, tr("Sales"));
    坐标变换的更为简单的方式是使用QPainter的translate()\scale()\rotate()和shear()这些简便函数。
下面来做一个微波炉旋钮:
头文件:
#ifndef OVENTIMER_H
#define OVENTIMER_H

#include <QDateTime>
#include <QWidget>
#include <QTimer>

class OvenTimer : public QWidget
{
    Q_OBJECT

public:
    OvenTimer(QWidget *parent = 0);

    void setDuration(int secs);
    int duration() const;
    void draw(QPainter *painter);

signals:
    void timeout();

protected:
    void paintEvent(QPaintEvent *event);
    void mousePressEvent(QMouseEvent *event);

private:
    QDateTime finishTime;
    QTimer *updateTimer;
    QTimer *finishTimer;
};

#endif
重新实现QWidget的
protected:
    void paintEvent(QPaintEvent *event);
    void mousePressEvent(QMouseEvent *event);
并且设置几个参数,控制烤箱定时器外观:
const double DegreesPerMinute = 7.0;
const double DegreesPerSecond = DegreesPerMinute / 60;
const int MaxMinutes = 45;
const int MaxSeconds = MaxMinutes * 60;
const int UpdateInterval = 5;

oventimer.cpp
#include <QtGui>
#include <cmath>

#ifndef M_PI
#define M_PI 3.14159265359
#endif

#include "oventimer.h"

const double DegreesPerMinute = 7.0;
const double DegreesPerSecond = DegreesPerMinute / 60;
const int MaxMinutes = 45;
const int MaxSeconds = MaxMinutes * 60;
const int UpdateInterval = 5;

OvenTimer::OvenTimer(QWidget *parent)
    : QWidget(parent)
{
    finishTime = QDateTime::currentDateTime();

    updateTimer = new QTimer(this);
    connect(updateTimer, SIGNAL(timeout()), this, SLOT(update()));

    finishTimer = new QTimer(this);
    finishTimer->setSingleShot(true);
    connect(finishTimer, SIGNAL(timeout()), this, SIGNAL(timeout()));
    connect(finishTimer, SIGNAL(timeout()), updateTimer, SLOT(stop()));

    QFont font;
    font.setPointSize(8);
    setFont(font);
}

void OvenTimer::setDuration(int secs)
{
    secs = qBound(0, secs, MaxSeconds);

    finishTime = QDateTime::currentDateTime().addSecs(secs);

    if (secs > 0) {
        updateTimer->start(UpdateInterval * 1000);
        finishTimer->start(secs * 1000);
    } else {
        updateTimer->stop();
        finishTimer->stop();
    }
    update();
}

int OvenTimer::duration() const
{
    int secs = QDateTime::currentDateTime().secsTo(finishTime);
    if (secs < 0)
        secs = 0;
    return secs;
}

void OvenTimer::mousePressEvent(QMouseEvent *event)
{
    QPointF point = event->pos() - rect().center();
    double theta = std::atan2(-point.x(), -point.y()) * 180.0 / M_PI;
    setDuration(duration() + int(theta / DegreesPerSecond));
    update();
}

void OvenTimer::paintEvent(QPaintEvent * /* event */)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);

    int side = qMin(width(), height());

    painter.setViewport((width() - side) / 2, (height() - side) / 2,
                        side, side);
    painter.setWindow(-50, -50, 100, 100);

    draw(&painter);
}

void OvenTimer::draw(QPainter *painter)
{
    static const int triangle[3][2] = {
        { -2, -49 }, { +2, -49 }, { 0, -47 }
    };
    QPen thickPen(palette().foreground(), 1.5);
    QPen thinPen(palette().foreground(), 0.5);
    QColor niceBlue(150, 150, 200);

    painter->setPen(thinPen);
    painter->setBrush(palette().foreground());
    painter->drawPolygon(QPolygon(3, &triangle[0][0]));

    QConicalGradient coneGradient(0, 0, -90.0);
    coneGradient.setColorAt(0.0, Qt::darkGray);
    coneGradient.setColorAt(0.2, niceBlue);
    coneGradient.setColorAt(0.5, Qt::white);
    coneGradient.setColorAt(1.0, Qt::darkGray);

    painter->setBrush(coneGradient);
    painter->drawEllipse(-46, -46, 92, 92);

    QRadialGradient haloGradient(0, 0, 20, 0, 0);
    haloGradient.setColorAt(0.0, Qt::lightGray);
    haloGradient.setColorAt(0.8, Qt::darkGray);
    haloGradient.setColorAt(0.9, Qt::white);
    haloGradient.setColorAt(1.0, Qt::black);

    painter->setPen(Qt::NoPen);
    painter->setBrush(haloGradient);
    painter->drawEllipse(-20, -20, 40, 40);

    QLinearGradient knobGradient(-7, -25, 7, -25);
    knobGradient.setColorAt(0.0, Qt::black);
    knobGradient.setColorAt(0.2, niceBlue);
    knobGradient.setColorAt(0.3, Qt::lightGray);
    knobGradient.setColorAt(0.8, Qt::white);
    knobGradient.setColorAt(1.0, Qt::black);

    painter->rotate(duration() * DegreesPerSecond);
    painter->setBrush(knobGradient);
    painter->setPen(thinPen);
    painter->drawRoundRect(-7, -25, 14, 50, 99, 49);

    for (int i = 0; i <= MaxMinutes; ++i) {
        if (i % 5 == 0) {
            painter->setPen(thickPen);
            painter->drawLine(0, -41, 0, -44);
            painter->drawText(-15, -41, 30, 30,
                              Qt::AlignHCenter | Qt::AlignTop,
                              QString::number(i));
        } else {
            painter->setPen(thinPen);
            painter->drawLine(0, -42, 0, -44);
        }
        painter->rotate(-DegreesPerMinute);
    }
}
在构造函数中,创建两个QTime对象:updateTimer用来每隔5秒刷新窗口部件的外观,finishTimer在定时器达到0发射timeout()信号。因为只执行一次所以调用setSingleShot(true)。默认情况下,定时器会重复触发直到停止或者销毁。最后一个connect()调用是一个优化,目的是当定时器停止运行时,停止更新窗口部件。
最后也有设置字体大小,以保证显示大小字体一致。
void OvenTimer::setDuration(int secs)
设置定时器持续的时间为给定的秒数。
int OvenTimer::duration() const
返回定时器完成前剩余的秒数,如果定时器未激活,返回0.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第八章 坐标系统变换 的相关文章

  • 如何使 Windows 窗体的关闭按钮不关闭窗体但使其不可见?

    该表单有一个 NotifyIcon 对象 当用户单击 关闭 按钮时 我希望表单不关闭而是变得不可见 然后 如果用户想再次查看该表单 可以双击系统托盘中的图标 如果用户想关闭表单 可以右键单击该图标并选择 关闭 有人可以告诉我如何使关闭按钮不
  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • C# 和 Javascript SHA256 哈希的代码示例

    我有一个在服务器端运行的 C 算法 它对 Base64 编码的字符串进行哈希处理 byte salt Convert FromBase64String serverSalt Step 1 SHA256Managed sha256 new S
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • C# 中的递归自定义配置

    我正在尝试创建一个遵循以下递归结构的自定义配置部分
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • x86 上未对齐的指针

    有人可以提供一个示例 将指针从一种类型转换为另一种类型由于未对齐而失败吗 在评论中这个答案 https stackoverflow com questions 544928 reading integer size bytes from a
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框

随机推荐

  • switch怎么一个账号绑定各种服务器,任天堂switch主副机器介绍,ns数字版游戏共享操作详解...

    原标题 任天堂switch主副机器介绍 ns数字版游戏共享操作详解 任天堂在日前推送了switch主机的6 0系统 其网络会员服务Switch Online也正式上线 值得一提的是此次更新引入了主副机器的概念 玩家可以绑定任天堂账号在swi
  • 为什么写了value属性 jq赋值value值不显示_[Go基础]理解 Go 标准库中的 atomic.Value 类型

    转载声明 文章作者 喵叔 上次更新 2019 03 15 许可协议 CC BY NC ND 4 0 转载请注明出处 原文链接 https blog betacat io post golang atomic value exploratio
  • 计算机安全中心无法启动,windows安全中心,教您无法启动windows安全中心的解决方法...

    最近小编在逛帖子的时候 有看到一些小伙伴说自己家的电脑不知道怎么了 突然间无法启动windows安全中心 在寻求帮助 看到这个之后 小编决定帮小伙伴们解决这个问题 那么接下来小编就告诉你们怎么解决windows安全中心无法启动的问题 win
  • VM虚拟机提示“vmware tools 的安装无法手动启动,自动安装正在进行中”

    VM虚拟机提示 vmware tools 的安装无法手动启动 自动安装正在进行中 装了个VMWARE虚拟机 但vmware tools总也装不上 提示 vmware tools 的安装无法手动启动 自动安装正在进行中 网上搜索下 解决方法如
  • cannot connect to 192.168. 由于目标计算机积极拒绝,无法连接或者AndroidStudio通过WIFI连接手机调试

    由于在公司只有一根数据线 不能同时两个手机用 一个自己的 一个测试机 那有人说 不会再买个吗 穷啊 买不起 AS可以通过WIFI 不需要数据线就可以连接到手机 这个很符合现在的我 嘻嘻 本以为很简单粗暴 但中间遇到了一些坑 并解决了 在此记
  • 学习笔记(一)数据挖掘概念与技术

    1数据仓库与数据库 数据仓库是一种用于长期存储数据的仓库 这些数据来自多个数据源 是经过组织的 以便支持管理决策 这些数据在一种统一的模式下存放 并且通常是汇总的 数据仓库提供一些数据分析能力 称作联机分析处理 数据库 传统关系型数据库的主
  • 从数据爬取到构建基于知识图谱的问答系统(前端展示)

    项目介绍 项目地址 整理了很久 一定要给个star呀 博主目前南京大学在读研究生 有问题欢迎咨询 bravezhangw 163 com
  • 数据中台盛行,DataOps兴起,数据架构才是未来

    导读 在数字化转型的浪潮下 数据架构获得了越来越多的关注 作为企业架构中的关键纽带 数据架构解决了业务与数据间的映射 规范了应用架构中的数据集成关系 指导了技术架构的技术选型 在企业中发挥着不可或缺的作用 伴随DataOps等场景的出现 数
  • 【Leetcode】P5612 从仓库到码头运输箱子

    Leetcode P5612 从仓库到码头运输箱子 你有一辆货运卡车 你需要用这一辆车把一些箱子从仓库运送到码头 这辆卡车每次运输有 箱子数目的限制 和 总重量的限制 给你一个箱子数组 boxes 和三个整数 portsCount maxB
  • es 局部更新 DSL 语句

    curl XPOST 192 168 1 47 9200 sub refresh 更新 根据条件 curl XPOST 192 168 1 4 9200 sub update by query pretty H Content Type a
  • 02-07GRE真题及答案解析整理

    02 07年GRE真题及答案解析整理 2002年11月23日GRE笔考题 VERBAL部分 Section 1 填空 1 Although she gives badly titles to her musical compositions
  • std::match_result

    英文文档 https cplusplus com reference regex match results 以下是我对此英文文档的翻译 经过一定的加工 改动和取舍 并添加了我自己的理解 可能翻译的比较差 请轻喷 有空我会更新的 match
  • 数据库的添加与查询

    创建数据库 使用数据库 创表 添加数据 1 查询所有学生的信息 2 查询姓名是李白的学生信息 3 查询1班是上海的学生 4 查询家乡是北京或者上海的学生 5 查询小乔的年龄 6 查询家乡不在北京的学生 7 查询年龄小于20的女同学 8 查询
  • Obsidian 从本地到云端

    原文 https www ftls xyz posts obcsapi fc simple 作者 恐咖兵糖 Obsidian 从本地到云端 Obsidian 作为本地笔记软件 在使用了多端同步插件 remotely save插件情况下 我选
  • Flask类视图的使用

    1 add url rule函数 之前我们接触的视图都是函数 所以一般简称视图函数 其实视图也可以基于类来实现 类视图的好处是支持继承 但是类视图不能跟函数视图一样 写完类视图还需要通过app add url rule 函数来进行注册 ap
  • Python如何自动操作电脑桌面应用程序

    前言 本文是该专栏的第2篇 后面会持续分享python的各种黑科技知识 值得关注 熟悉python的朋友 都知道python可以做自动化 比如说selenium pyppeteer airtest等等 但你是否听说过python可以来自动操
  • Office 365 官方原版镜像下载

    中文说明 专业增强版 简体中文版 文件名称 O365ProPlusRetail img 下载地址 https officecdn microsoft com db 492350F6 3A01 4F97 B9C0 C7C6DDF67D60 m
  • “M OP N“ 数值问题

    M OP N 数值问题 问题描述 获得用户输入的一个字符串 格式为 M OP N 其中 M和N是任何数字 OP代表一种操作 表示为如下四种 加减乘除 根据OP 输出M OP N的运算结果 统一保存小数点后2位 注意 M和OP OP和N之间可
  • 腾讯AI Lab开源大规模高质量中文词向量数据,800万中文词随你用

    感谢阅读腾讯AI Lab第45篇文章 本文将介绍大规模高质量的中文词向量数据的开源情况 今日 腾讯AI Lab 宣布开源大规模 高质量的中文词向量数据 该数据包含800多万中文词汇 相比现有的公开数据 在覆盖率 新鲜度及准确性上大幅提高 为
  • 第八章 坐标系统变换

    一 使用QPainter绘图 http blog csdn net hellozex article details 78361311 二 坐标系统变换 在QPainter的默认坐标系中 点 0 0 位于坐标绘制设备的左上角 x坐标向右 y