在 Qyoto C# 编程教程的这一部分中,我们将继续绘图。 我们将提供一些更复杂的示例。
甜甜圈形状
第一个示例通过旋转一堆椭圆来创建复杂的形状。
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();}}
在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。
QColor color = new QColor();color.SetNamedColor("#333333");
我们可以使用十六进制表示法来创建颜色对象。
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);}
我们绘制一个椭圆对象 72 次。 每次,我们将椭圆旋转 5 度。 这将创建我们的甜甜圈形状。

图:多纳圈
灰度图像
在下面的示例中,我们将创建一个灰度图像。
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 图像。 我们把它画在窗口上。 我们创建图像的副本,将其转换为灰度并在原始图像旁边的窗口上绘制。
void LoadImage(){sid = new QImage("smallsid.jpg");w = sid.Width();h = sid.Height();}
在LoadImage()方法中,我们加载图像并获取其宽度和高度。
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()方法修改像素。 我们将灰色值用于颜色的红色,绿色和蓝色部分。

图:灰度图像
反射
在下一个示例中,我们显示反射图像。 该效果使人产生幻觉,好像图像在水中被反射一样。
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();}}
我们从当前工作目录加载图像。 我们创建另一个相同大小的空图像。 我们将原始图像逐行复制到新的空白图像,并逐渐增加透明度。
img = new QImage("slanec.png");if (img.IsNull()){Console.WriteLine("Error loading image");}
我们加载一个 PNG 图片,并进行一些错误检查。
iw = img.Width();ih = img.Height();step = opacity / ih;
我们得到图像的宽度和高度。 步进变量控制第二张图像淡出的强度。
reflected_img = new QImage(iw, ih, QImage.Format.Format_RGB32);
创建一个新的空图像。 它具有原始图像的大小。
void CreateReflectedImage(){QPainter fptr = new QPainter(reflected_img);...
在CreateReflectedImage()方法中,我们绘制空白图像。
while (i < ih){i++;opacity = opacity - step;fptr.Opacity = initial_opacity-opacity;fptr.DrawImage(0, i, img, 0, i, -1, 1);}
我们将原始图像复制到新图像。 逐行。 不透明度在每个循环中逐步降低。
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);
在这里,我们将第二个图像向下移动,比原始图像低一些。 Scale()方法将图像上下翻转。 请注意,平移是图像高度的两倍。 这是必要的,因为缩放操作不仅会翻转图像,还会使图像向上移动。 要了解这一点,只需拍摄一张照片,将其放在桌子上并翻转即可。

图:反射图像
等待效果
在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一条线在移动的错觉。 此类效果通常用于通知用户幕后正在进行繁重的任务。 一个示例是通过互联网流式传输视频。
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 值绘制八条线。
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 行中的每行将连续使用这些值。
count = 0;StartTimer(105);
在这里,我们启动计数值并启动一个计时器。
QPen pen = new QPen();pen.Width = 3;pen.CapStyle = PenCapStyle.RoundCap;
我们使线条更粗一些,以使它们更加可见。 我们用圆帽画线。 带圆帽的线条看起来更好。
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();}
每次调用计时器事件时,我们都会增加计数值并重新绘制窗口区域。

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