# Qyoto 中的绘图 II

> 原文： [http://zetcode.com/gui/csharpqyoto/paintingII/](http://zetcode.com/gui/csharpqyoto/paintingII/)

在 Qyoto C# 编程教程的这一部分中，我们将继续绘图。 我们将提供一些更复杂的示例。

## 甜甜圈形状

第一个示例通过旋转一堆椭圆来创建复杂的形状。

```cs
using System;
using QtCore;
using QtGui;

/**
 * ZetCode Qyoto C# tutorial
 *
 * This program draws a donut
 * shape.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{    
    public QyotoApp() 
    {
        WindowTitle = "Donut";

        PaintEvent += OnPaintEvent;

        Resize(350, 280);
        Move(300, 300);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    {
        QPainter ptr = new QPainter(this);
        DrawDonut(ptr);

        ptr.End();
     }

    void DrawDonut(QPainter ptr) 
    {
        QColor col = new QColor();
        col.SetNamedColor("#333333");

        ptr.Pen = new QPen(col, 0.5);

        ptr.SetRenderHint(QPainter.RenderHint.Antialiasing);

        int h = Height;
        int w = Width;

        ptr.Translate(new QPoint(w/2, h/2));

         for (double rot=0; rot < 360.0; rot+=5.0 ) 
         {
             ptr.DrawEllipse(-125, -40, 250, 80);
             ptr.Rotate(5.0);
         }
    }

    [STAThread]
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

```

在此示例中，我们创建一个甜甜圈。 形状类似于曲奇，因此得名“甜甜圈”。

```cs
QColor color = new QColor();
color.SetNamedColor("#333333");

```

我们可以使用十六进制表示法来创建颜色对象。

```cs
int h = Height;
int w = Width;

```

在这里，我们确定窗口的宽度和高度。

```cs
ptr.Translate(new QPoint(w/2, h/2));

```

我们将坐标系移到窗口的中间。 这样，我们使绘图在数学上更容易。

```cs
for (double rot=0; rot < 360.0; rot+=5.0 ) 
{
    ptr.DrawEllipse(-125, -40, 250, 80);
    ptr.Rotate(5.0);
}

```

我们绘制一个椭圆对象 72 次。 每次，我们将椭圆旋转 5 度。 这将创建我们的甜甜圈形状。

![Donut](img/604f2043afaf21fc6aa4ba5747cec1d9.jpg)

图：多纳圈

## 灰度图像

在下面的示例中，我们将创建一个灰度图像。

```cs
using System;
using QtGui;
using QtCore;

/**
 * ZetCode Qyoto C# tutorial
 *
 * In this example, we create a 
 * grayscale image.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{    
    QImage sid;
    int w, h = 0;

    public QyotoApp() 
    {
        WindowTitle = "Gray scale";

        PaintEvent += OnPaintEvent;        

        LoadImage();
        Resize(320, 150);
        Move(300, 300);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    { 
        QPainter ptr = new QPainter(this);
        DrawImages(ptr);

        ptr.End();
    }

    void DrawImages(QPainter ptr) 
    {
        ptr.DrawImage(5, 15, sid);
        ptr.DrawImage(w + 10, 15, GrayScale(sid.Copy()));
    }

    void LoadImage()
    {
        sid = new QImage("smallsid.jpg");

        w = sid.Width();
        h = sid.Height();
    }

    QImage GrayScale(QImage img)
    {        
        for (int i=0; i < w; i++)
        {
            for (int j=0; j < h; j++)
            {
                uint c = img.Pixel(i, j);
                int gray = Global.qGray(c);
                int alpha = Global.qAlpha(c);
                img.SetPixel(i, j, Global.qRgba(gray, gray, 
                   gray, alpha));                
            }
        }  

        return img;
    }          

    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

```

我们有一个彩色 JPG 图像。 我们把它画在窗口上。 我们创建图像的副本，将其转换为灰度并在原始图像旁边的窗口上绘制。

```cs
void LoadImage()
{
    sid = new QImage("smallsid.jpg");

    w = sid.Width();
    h = sid.Height();
}

```

在`LoadImage()`方法中，我们加载图像并获取其宽度和高度。

```cs
QImage GrayScale(QImage img)
{        
    for (int i=0; i < w; i++)
    {
        for (int j=0; j < h; j++)
        {
            uint c = img.Pixel(i, j);
            int gray = Global.qGray(c);
            int alpha = Global.qAlpha(c);
            img.SetPixel(i, j, Global.qRgba(gray, gray, 
                gray, alpha));                
        }
    }  

    return img;
}    

```

`GrayScale()`方法将图像转换为灰度并返回。 我们遍历图像的所有像素。 `Pixel()`方法返回有问题的像素。 我们使用`Global.qGray()`方法来获取特定像素的灰度值。 同样，我们获得 alpha 值。 最后，我们使用`SetPixel()`方法修改像素。 我们将灰色值用于颜色的红色，绿色和蓝色部分。

![Grayscale image](img/ec1630b5023d124f5f705317694e6527.jpg)

图：灰度图像

## 反射

在下一个示例中，我们显示反射图像。 该效果使人产生幻觉，好像图像在水中被反射一样。

```cs
using System;
using QtGui;
using QtCore;

/**
 * ZetCode Qyoto C# tutorial
 *
 * In this example we create a reflected image.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{
    QImage img;
    QImage reflected_img;

    int iw, ih = 0;
    double initial_opacity = 0.7;
    double opacity = 0.7;
    double step = 0;
    const int GAP = 30;

    public QyotoApp() 
    {
        WindowTitle = "Reflection";

        PaintEvent += OnPaintEvent;

        InitExample();  

        Resize(300, 400);
        Move(150, 150);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    { 
        QPainter ptr = new QPainter(this);
        DrawImages(ptr);

        ptr.End();
    }

    void InitExample()
    {
        img = new QImage("slanec.png");

        if (img.IsNull()) 
        {
            Console.WriteLine("Error loading image");
        }

        iw = img.Width();
        ih = img.Height();

        step = opacity / ih;    

        reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);
        CreateReflectedImage();
    }       

    void CreateReflectedImage()
    {
        QPainter fptr = new QPainter(reflected_img);

        int i = 0;
        double opacity = 0.7;    

        while (i < ih)
        {        
            i++;
            opacity = opacity - step;

            fptr.Opacity = initial_opacity-opacity;
            fptr.DrawImage(0, i, img, 0, i, -1, 1);
        }          

        fptr.End();
    }

    void DrawImages(QPainter ptr) 
    {
        int w = Width;
        int h = Height;

        ptr.FillRect(0, 0, w, h, Qt.GlobalColor.black);
        ptr.SetRenderHint(QPainter.RenderHint.Antialiasing);

        QRect r = new QRect(25, 15, iw, ih);
        ptr.DrawImage(r, img);

        ptr.Translate(0, 2 * ih + GAP);
        ptr.Scale(1, -1);     

        ptr.DrawImage(25, 0, reflected_img);      
    }

    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

```

我们从当前工作目录加载图像。 我们创建另一个相同大小的空图像。 我们将原始图像逐行复制到新的空白图像，并逐渐增加透明度。

```cs
img = new QImage("slanec.png");

if (img.IsNull()) 
{
    Console.WriteLine("Error loading image");
}

```

我们加载一个 PNG 图片，并进行一些错误检查。

```cs
iw = img.Width();
ih = img.Height();

step = opacity / ih;     

```

我们得到图像的宽度和高度。 步进变量控制第二张图像淡出的强度。

```cs
reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);

```

创建一个新的空图像。 它具有原始图像的大小。

```cs
void CreateReflectedImage()
{
    QPainter fptr = new QPainter(reflected_img);
    ...

```

在`CreateReflectedImage()`方法中，我们绘制空白图像。

```cs
while (i < ih)
{        
    i++;
    opacity = opacity - step;

    fptr.Opacity = initial_opacity-opacity;
    fptr.DrawImage(0, i, img, 0, i, -1, 1);
}     

```

我们将原始图像复制到新图像。 逐行。 不透明度在每个循环中逐步降低。

```cs
QRect r = new QRect(25, 15, iw, ih);
ptr.DrawImage(r, img);

```

第一个图像绘制在窗口上。

```cs
ptr.Translate(0, 2 * ih + GAP);
ptr.Scale(1, -1);     

ptr.DrawImage(25, 0, reflected_img);   

```

在这里，我们将第二个图像向下移动，比原始图像低一些。 `Scale()`方法将图像上下翻转。 请注意，平移是图像高度的两倍。 这是必要的，因为缩放操作不仅会翻转图像，还会使图像向上移动。 要了解这一点，只需拍摄一张照片，将其放在桌子上并翻转即可。

![A reflected image](img/40cb860f6349666fa29c5586e5c46e58.jpg)

图：反射图像

## 等待效果

在此示例中，我们使用透明效果创建一个等待演示。 我们将绘制 8 条线，这些线将逐渐消失，从而产生一条线在移动的错觉。 此类效果通常用于通知用户幕后正在进行繁重的任务。 一个示例是通过互联网流式传输视频。

```cs
using System;
using QtGui;
using QtCore;

/**
 * ZetCode Qyoto C# tutorial
 *
 * This program draws basic shapes
 * available in Qyoto.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified November 2012
 */

public class QyotoApp : QMainWindow 
{
    int count = 0;

    double[,] trs = 
    {
        { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
        { 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
        { 0.9, 1.0,  0.0,  0.15, 0.3, 0.5, 0.65, 0.8 },
        { 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5, 0.65 },
        { 0.65, 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5 },
        { 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15, 0.3 },
        { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15 },
        { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0 }
    };

    public QyotoApp() 
    {
        WindowTitle = "Waiting";

        PaintEvent += OnPaintEvent;

        InitExample();

        Resize(300, 200);
        Move(300, 300);
        Show();
    }

    private void OnPaintEvent(object sender, QEventArgs<QPaintEvent> e)
    {
        QPainter ptr = new QPainter(this);
        DrawLines(ptr);

        ptr.End();
    }

    void InitExample()
    {    
        count = 0;
        StartTimer(105);
    }

    void DrawLines(QPainter ptr)
    {
        QPen pen = new QPen();
        pen.Width = 3;
        pen.CapStyle = PenCapStyle.RoundCap;

        int w = Width;
        int h = Height;

        ptr.Translate(w/2, h/2);
        ptr.Pen = pen;

        int len = trs.GetLength(0);

        for (int i=0; i < len; i++)
        {
            ptr.Opacity = trs[count%8, i];
            ptr.DrawLine(0, -10, 0, -40);
            ptr.Rotate(45);
        }
    }

    protected override void OnTimerEvent(QTimerEvent e)
    {
        count++;
        Repaint();
    }

    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();
        return QApplication.Exec();
    }
}

```

我们用八个不同的 alpha 值绘制八条线。

```cs
double[,] trs = 
{
    { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
    { 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
...

```

这是透明度值的数组。 有 8 行，每行一个位置。 8 行中的每行将连续使用这些值。

```cs
count = 0;
StartTimer(105);

```

在这里，我们启动计数值并启动一个计时器。

```cs
QPen pen = new QPen();
pen.Width = 3;
pen.CapStyle = PenCapStyle.RoundCap;

```

我们使线条更粗一些，以使它们更加可见。 我们用圆帽画线。 带圆帽的线条看起来更好。

```cs
for (int i=0; i < len; i++)
{
    ptr.Opacity = trs[count%8, i];
    ptr.DrawLine(0, -10, 0, -40);
    ptr.Rotate(45);
}

```

在此循环中，我们设置不透明度值。 我们画线并旋转它。 这产生了移动和渐隐线的错觉。

```cs
protected override void OnTimerEvent(QTimerEvent e)
{
    count++;
    Repaint();
}

```

每次调用计时器事件时，我们都会增加计数值并重新绘制窗口区域。

![Waiting effect](img/53955883b407efed369c1ea98f1ee0ea.jpg)

图：等待 effect

在 Qyoto C# 编程教程的这一部分中，我们结束了有关在 Qyoto 中绘图的讨论。