精华内容
下载资源
问答
  • 十二状态模式

    2020-09-04 11:09:50
    状态模式状态模式状态模式 介绍概念示例状态模式 演示状态模式 场景有限状态机(finite state machine)实现一个简单的 Promise状态模式 总结 状态模式 介绍 演示 场景 总结 状态模式 介绍 概念 一个对象或者实例...

    状态模式

    • 介绍
    • 演示
    • 场景
    • 总结

    状态模式 介绍

    • 一个对象有状态变化
    • 每次状态变化都会触发一个逻辑
    • 不能总是用if…else来控制

    概念

    一个对象或者实例可能会有不同状态的变化(例如交通信号灯),如果仅用if...else去处理状态变化,扩展性非常差,特别是针对复杂的状态变化。状态模式就提出了一种处理复杂状态变化且扩展性好的设计思路。

    示例

    • 交通信号灯不同颜色的变化
    • 状态模式 演示

    传统的 UML 类图

    在这里插入图片描述

    简化之后的 UML 类图

    在这里插入图片描述

    代码演示

    class State {
        constructor(color) {
            this.color = color
        }
        handle(context) {
            console.log(`turn to ${this.color} light`)
            context.setState(this)
        }
    }
    
    class Context {
        constructor() {
            this.state = null
        }
        setState(state) {
            this.state = state
        }
        getState() {
            return this.state
        }
    }
    
    // 测试代码
    let context = new Context()
    
    let greed = new State('greed')
    let yellow = new State('yellow')
    let red = new State('red')
    
    // 绿灯亮了
    greed.handle(context)
    console.log(context.getState())
    // 黄灯亮了
    yellow.handle(context)
    console.log(context.getState())
    // 红灯亮了
    red.handle(context)
    console.log(context.getState())
    

    状态模式 场景

    有限状态机(finite state machine)

    表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型,例如上一节的交通信号灯的状态变化。

    有一个开源 lib javascript-state-machine 实现了有限状态机的功能。我们使用它来模拟一个比较常用的操作,“收藏”和“取消收藏”

    // 状态机模型
    var fsm = new StateMachine({
        init: '收藏',  // 初始状态,待收藏
        transitions: [
            {
                name: 'doStore',
                from: '收藏',
                to: '取消收藏'
            },
            {
                name: 'deleteStore',
                from: '取消收藏',
                to: '收藏'
            }
        ],
        methods: {
            // 执行收藏
            onDoStore: function () {
                alert('收藏成功')
                updateText()
            },
            // 取消收藏
            onDeleteStore: function () {
                alert('已取消收藏')
                updateText()
            }
        }
    })
    
    var $btn = $('#btn')
    
    // 点击事件
    $btn.click(function () {
        if (fsm.is('收藏')) {
            fsm.doStore()
        } else {
            fsm.deleteStore()
        }
    })
    
    // 更新文案
    function updateText() {
        $btn.text(fsm.state)
    }
    
    // 初始化文案
    updateText()
    

    实现一个简单的 Promise

    先回归一下 Promise 的语法

    function loadImg(src) {
        const promise = new Promise(function (resolve, reject) {
            var img = document.createElement('img')
            img.onload = function () {
                resolve(img)
            }
            img.onerror = function () {
                reject()
            }
            img.src = src
        })
        return promise
    }
    
    var src = 'http://www.imooc.com/static/img/index/logo_new.png'
    var result = loadImg(src)
    
    result.then(function (img) {
        console.log('success 1')
    }, function () {
        console.log('failed 1')
    })
    result.then(function (img) {
        console.log('success 2')
    }, function () {
        console.log('failed 2')
    })
    

    Promise 就是一个有限状态机,有三个状态pending fullfilled rejected,其中的变化必须是pending -> fullfilled 或者pending -> rejected,不能逆向变化,这样就可以根据有限状态即实现一个基本的 Promise

    // 模型
    var fsm = new StateMachine({
        init: 'pending',
        transitions: [
            {
                name: 'resolve',
                from: 'pending',
                to: 'fullfilled'
            },
            {
                name: 'reject',
                from: 'pending',
                to: 'rejected'
            }
        ],
        methods: {
            // 成功
            onResolve: function (state, data) {
                // 参数:state - 当前状态示例; data - fsm.resolve(xxx) 执行时传递过来的参数
                data.successList.forEach(fn => fn())
            },
            // 失败
            onReject: function (state, data) {
                // 参数:state - 当前状态示例; data - fsm.reject(xxx) 执行时传递过来的参数
                data.failList.forEach(fn => fn())
            }
        }
    })
    
    // 定义 Promise
    class MyPromise {
        constructor(fn) {
            this.successList = []
            this.failList = []
    
            fn(() => {
                // resolve 函数
                fsm.resolve(this)
            }, () => {
                // reject 函数
                fsm.reject(this)
            })
        }
        then(successFn, failFn) {
            this.successList.push(successFn)
            this.failList.push(failFn)
        }
    }
    

    然后测试一下

    // 测试代码
    function loadImg(src) {
        const promise = new MyPromise(function (resolve, reject) {
            var img = document.createElement('img')
            img.onload = function () {
                resolve(img)
            }
            img.onerror = function () {
                reject()
            }
            img.src = src
        })
        return promise
    }
    var src = 'http://www.imooc.com/static/img/index/logo_new.png'
    var result = loadImg(src)
    console.log(result)
    
    result.then(function (img) {
        console.log('success 1')
    }, function () {    
        console.log('failed 1')
    })
    result.then(function (img) {
        console.log('success 2')
    }, function () {    
        console.log('failed 2')
    })
    

    注意,本节以讲解状态模式位置,仅实现了简单的 Promise 原型,Promise 其他高级功能大家可自行去扩展

    状态模式 总结

    • 状态模式是什么
    • 主要解决什么问题
    • 核心:不同状态变化的逻辑,分离实现,而不是用if...else放在一起判断
    • 有限状态机

    和观察者模式的对比:两者之间没有矛盾,而是状态模式中可以使用观察者模式。
    - 状态模式:不同状态的变化带来的改变。类似于多种if..else的逻辑。
    - 观察者模式:先订阅、后发布,触发订阅。类似于一次性触发多个事件,并没有if...else的判断。

    设计原则验证:

    • 将状态对象和主题对象分离,状态的变化逻辑单独处理
    • 符合开放封闭原则
    展开全文
  • python图像处理笔记-十二-图像聚类 学习内容 这一章主要在学习的是聚类算法以及其在图像算法中的应用,主要学习的聚类方法有: KMeans 层次聚类 谱聚类 并将使用他们对字母数据及进行聚类处理,以对比效果。 聚类...

    python图像处理笔记-十二-图像聚类

    学习内容

    这一章主要在学习的是聚类算法以及其在图像算法中的应用,主要学习的聚类方法有:

    • KMeans
    • 层次聚类
    • 谱聚类

    并将使用他们对字母数据及进行聚类处理,以对比效果。

    聚类是什么?

    有n个点,把这n个点通过某种方法分成k类就是聚类算法在做的事情,聚类做的越好,分出来的k类的类与类之间差异越明显,同一个类中的差异也越不明显。

    K-means

    聚类思想:

    K-means需要给出最终簇的数量k。它的核心思路是:从某个状态开始,通过不断的迭代来更新中心点的位置,来让分类效果尽可能最优。其实质是在优化下面的函数,使其尽可能地小:

    V=Σi=1kΣxjci(xjμi)2 V = \Sigma_{i=1}^k \Sigma _{x_j \in c_i}(x_j-\mu_i)^2

    这种方法最大的问题在于,需要手动选择最终的簇的数量k,如果选择的簇的数量过大或过小,都会使结果很差。

    因为这个我之前已经学过了,而且已经用烂了,就不介绍详细方法了,大家可以上网去查,一抓一大把。

    聚类的实现:

    为了和书中的代码同步,所以我也学着去使用Scipy中的聚类包。如果你想尝试一下别的,可以去试一下sklearn中的聚类包,它同样非常好用。

    这里我们模仿书上的例子,对手写字体数据集中的数据进行聚类,大体的过程如下:

    • 对数据进行主成分分析
    • 对主成分分析后的进行聚类
    • 对聚类结果进行显示

    代码如下:

    import osimport pickleimport numpy as npfrom PIL import Imageimport pylab as plfrom PCV.tools import pcafrom scipy.cluster.vq import *# 获取字母数据集下的图片文件名,并且保存在列表中imlist = os.listdir('a_selected_thumbs/')imnbr= len(imlist)with open('font_pca_modes.pkl', 'rb') as f:    immean = pickle.load(f)    V = pickle.load(f)# 创建矩阵,存储所有拉成一组形式后的图像immatrix = np.array([np.array(Image.open('a_selected_thumbs/'+im)).flatten()for im in imlist],'f')# 投影到前40 个主成分上immean = immean.flatten()projected = np.array([np.dot(V[:30],immatrix[i]-immean) for i in range(imnbr)])# 进行k-means 聚类projected = whiten(projected)centroids,distortion = kmeans(projected,4)code,distance = vq(projected,centroids)# 绘制聚类簇for k in range(4):    ind = np.where(code==k)[0]    pl.figure()    pl.gray()    for i in range(min(len(ind),40)):        pl.subplot(4,10,i+1)        pl.imshow(immatrix[ind[i]].reshape((25,25)))        pl.axis('off')    pl.show()projected = np.array([np.dot(V[[0,2]],immatrix[i]-immean) for i in range(imnbr)])from PIL import Image, ImageDraw# 高和宽h,w = 1200,1200# 创建一幅白色背景图img = Image.new('RGB',(w,h),(255,255,255))draw = ImageDraw.Draw(img)# 绘制坐标轴draw.line((0,h/2,w,h/2),fill=(255,0,0))draw.line((w/2,0,w/2,h),fill=(255,0,0))# 缩放以适应坐标系scale = np.abs(projected).max(0)scaled = np.floor(np.array([ (p / scale) * (w/2-20,h/2-20) + (w/2,h/2) for p in projected]))# 粘贴每幅图像的缩略图到白色背景图片for i in range(imnbr):    nodeim = Image.open('a_selected_thumbs/'+imlist[i])    nodeim.thumbnail((25,25))    ns = nodeim.size    img.paste(nodeim,(int(scaled[i][0]-ns[0]//2),int(scaled[i][1]-ns[1]//2),int(scaled[i][0]+ns[0]//2+1),int(scaled[i][1]+ns[1]//2+1)))img.save('pca_font.jpg')
    

    跑出来的结果如下:

    • 在PCA降低维度至30维后的分类结果(我跑的时候40维结果非常差,所以调整为了30维):

      [img](https://saiyuwang-blog.oss-cn-beijing.aliyuncs.com/Screenshot from 2020-05-03 15-56-02.png)

    • PCA降维至2维后进行显示:

      img

    像素聚类

    这里我们使用一种非常简单的方法进行聚类,我们不再针对图片本身进行聚类,而是对单张图片中的像素点进行聚类,聚类的依据是图像的RGB三通道信息。我们以某一步长在图像中滑动,每滑动一次记录一次该范围内的均值,将其作为生成的低分辨率图像对应出的像素值,并以该像素值为依据进行聚类。这方法没啥意思,就不实现了,因为想做图像划分的话,这种方法是没用的。

    层次聚类

    层次聚类的方法如下:

    • 在最开始的时候,认为每个数据都是单独的一个簇
    • 每次迭代时,合并距离最小的两个簇
    • 仅剩下一个簇的时候,停止

    这个方法也挺无脑的,不过还是很有用的,实现的时候难点主要在于可视化,我们就不多讲了。

    谱聚类

    解决的问题

    在下面这种情况中(这张图来自于B站白板推理系列):

    [img](https://saiyuwang-blog.oss-cn-beijing.aliyuncs.com/Screenshot from 2020-05-03 16-46-25.png)

    直接使用距离为分类参考依据是的话,分类结果会非常糟糕。

    解决流程(粗略)

    • Step1假设数据集中一共有n个点,给定一个n*n的矩阵S,这个矩阵S是用于度量任意两个元素之间的相似性的,当然S是一个对称矩阵,并且S_{ii}=0。

    • Step2构建一个对角矩阵D,该矩阵对角线上元素的值如下:

      Dii=ΣjSij D_{ii}=\Sigma_j S_{ij}

    也就是说,这个D的对角线上的元素的值就等于当前点到其他所有点的距离之和。

    • Step3构建标准化后的拉普拉斯矩阵L
      L=D12SD12 L = D^{\frac{1}{2}}SD^{-\frac{1}{2}}

    • Step4计算L的特征向量,并使用K个最大的他特征值对应的k个特征向量,构建出一个特征向量集(即一个特征矩阵)。

    • Step5将各自对应的特征向量组成的矩阵按行标准化,最终组成维的特征矩阵F

    • Step6对F中的每一行作为一个k_1维的样本,共n个样本,用输入的聚类方法进行聚类,聚类维数为k_2。

    • Step7根据给出的聚类方法进行聚类,得到簇。

    代码如下(直接用的sklearn):

    from sklearn.cluster import SpectralClusteringfrom sklearn.cluster import KMeansfrom sklearn import metricsimport numpy as npfrom PIL import Imageimport pylab as plimport cv2import osdef getData(dirName):    imlist = os.listdir(dirName)    features = np.zeros([len(imlist), 512])    for i in range(len(imlist)):        im = np.array(Image.open(dirName + imlist[i]))        h, edges = np.histogramdd(im.reshape(-1,3),8,normed=True,range=[(0,255),(0,255),(0,255)])        features[i] = h.flatten()    return features, imlistif __name__ == "__main__":    imDir = '/home/wangsy/Codes/PCVLearning/myCodes/picCluster/flickr-sunsets-small/'    LKDir = '/home/wangsy/Codes/PCVLearning/myCodes/picCluster/L+K/'    KMDir = '/home/wangsy/Codes/PCVLearning/myCodes/picCluster/KMeans/'    X, imNames = getData(imDir)    y_pred = SpectralClustering(n_clusters=10).fit_predict(X)    for i in range(10):        # 把里面的图像都删掉        for j in os.listdir(LKDir+str(i)):            os.remove(LKDir+str(i) + '/' + j)        # 把原来的文件夹也删了        os.rmdir(LKDir+str(i))        # 在建立个新的        os.mkdir(LKDir+str(i))        ndx = np.where(y_pred == i)[0]        for j in ndx:            Image.open(imDir + imNames[j]).save(LKDir+str(i)+'/'+str(j) + '.jpg' )    y_pred = KMeans(n_clusters=10).fit_predict(X)    for i in range(10):        # 把里面的图像都删掉        for j in os.listdir(KMDir+str(i)):            os.remove(KMDir+str(i) + '/' + j)        # 把原来的文件夹也删了        os.rmdir(KMDir+str(i))        # 在建立个新的        os.mkdir(KMDir+str(i))        ndx = np.where(y_pred == i)[0]        for j in ndx:            Image.open(imDir + imNames[j]).save(KMDir+str(i)+'/'+str(j) + '.jpg' )
    

    在这段代码中,我们对100副海边的照片提取了直方图,然后对直方图分别使用Kmeans和谱聚类进行聚类分析,结果如下,我们对比图片1所在的分类中,左边的是KMeans分类的结果,右边的是谱聚类分类的结果:

    [img](https://saiyuwang-blog.oss-cn-beijing.aliyuncs.com/Screenshot from 2020-05-03 20-53-01.png)

    可以看出来,谱聚类在该问题上的分类结果更为理想。

    展开全文
  • 背景:上午12点之前,写代码。中午13.吃饭,睡觉。15.困,18.加班。20.下班回家。复杂的if else 判断,难以修改和维护,方法过长有坏的味道。面向对象设计其实就是希望做到代码责任的分解...UML结构 状态模式的好

    背景:上午12点之前,写代码。中午13.吃饭,睡觉。15.困,18.加班。20.下班回家。复杂的if else 判断,难以修改和维护,方法过长有坏的味道。面向对象设计其实就是希望做到代码责任的分解。

    状态模式(state)

    当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

    状态模式主要解决的是当控制一个对象状态转化的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。(若逻辑判断简单就不用状态模式)

    UML结构图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    状态模式的好处:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
    将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易的增加新的状态和转换。
    目的:消除庞大的条件分支语句。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互之间的依赖。(活字印刷)
    何时使用:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以考虑使用状态模式。另外如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的多分支判断语句实现,此时应该考虑将每一种业务状态定义为一个State子类。这样这些对象就可以不依赖于其它对象而独立的变化,某一天客户需要更改需求,增加或减少业务状态或改变状态流程,都很简单。

    在这里插入图片描述

    C++代码实现

    class Work;
    //抽象状态类
    class State {
    public:
        virtual void writeCode(Work* wk)
        {
            cout << "State ::writeCode(Work wk) " << endl;
        }
    };
    //睡眠状态
    class SleepState :public State {
        void writeCode(Work* wk)
        {
            cout << "SleepState ::writeCode(Work *wk) " << endl;
        }
    };
    
    //休息
    class RestState :public State {
        void writeCode(Work* wk)
        {
            cout << "RestState ::writeCode(Work *wk) " << endl;
        }
    };
    //晚上状态
    class EveningState :public State {
    public:
        void writeCode(Work* wk)
        {
            cout << "EveningState  ::writeCode(Work *wk) " << endl;
            if (wk->getTaskFinsh())
            {
                wk->setState(new RestState()); //工作完成 进入休息
                wk->writeProgrammer();
            }
            else {
                if (wk->getHour() < 21)
                {
                    cout << "加班" << endl;
                }
                else
                {
                    wk->setState(new SleepState()); //超过21点 转到晚上状态
                    wk->writeProgrammer();
                }
            }
    
        }
    };
    //下午工作状态
    class AfterNoonState :public State {
    public:
        void writeCode(Work* wk)
        {
            cout << "AfterNoonState  ::writeCode(Work *wk) " << endl;
            if (wk->getHour() < 17)
            {
                cout << "下午时间" << endl;
            }
            else
            {
                wk->setState(new EveningState()); //超过17点 转到晚上状态
                wk->writeProgrammer();
            }
        }
    };
    //中午工作状态
    class NoonState :public State {
    public:
        void writeCode(Work* wk)
        {
            cout << "NoonState  ::writeCode(Work *wk) " << endl;
            if (wk->getHour() < 13)
            {
                cout << "中午时间 吃饭" << endl;
            }
            else
            {
                wk->setState(new AfterNoonState); //超过13点 转到下午工作状态
                wk->writeProgrammer();
            }
        }
    };
    //上午工作状态
    class ForenoonState :public State {
        void writeCode(Work* wk)
        {
            cout << "ForenoonState  ::writeCode(Work wk) " << endl;
            if (wk->getHour() < 12)
            {
                cout << "上午 精神百倍" << endl;
            }
            else
            {
                wk->setState(new NoonState()); //超过12点 转到中午工作状态
                wk->writeProgrammer();
            }
        }
    };
    //工作类 没有过多的if判断
    class Work {
    private:
        State* m_state;
        double m_hour;
        bool m_TaskFinished;
    public:
        Work()
        {
            m_state = new ForenoonState();
        }
        void setHour(double a) { m_hour = a; }
        double getHour() { return m_hour; }
        void setTaskFinsh(bool b) { m_TaskFinished = b; }
        bool getTaskFinsh() { return m_TaskFinished; }
        void setState(State* s) { m_state = s; }
        void writeProgrammer()
        {
            cout << "Work ::writeCode(Work *wk) " << endl;
            m_state->writeCode(this);
        }
    };
    int main()
    {
        std::cout << "Hello World!\n";
        Work *xm = new Work;//紧急项目
        xm->setHour(9);
        xm->writeProgrammer();
        xm->setHour(12);
        xm->writeProgrammer();
        xm->setHour(13);
        xm->writeProgrammer();
        xm->setHour(14);
        xm->writeProgrammer();
        xm->setHour(17);
        xm->writeProgrammer();
    
        xm->setTaskFinsh(false);
        xm->writeProgrammer();
        xm->setHour(19);
        xm->writeProgrammer();
    
        xm->setHour(22);
        xm->writeProgrammer();
    }
    

    若公司要求员工必须在8点之前离开公司,则增加一个类,并修改傍晚工作状态的类就好了

    展开全文
  • 绘图事件由内部自动调用,比如窗口需要重绘的时候(即状态改变,如窗口大小改变),当然也可以人为调用。 void painterEvent(QPaintEvent *ev); P.S:使用QPainter要引入头文件<QPainter> 下面我们来创建一...


    绘图主要涉及到三个类QPainter、QPaintEngine、QPainDevice。其中,常用的是QPaiterQPaintDevice

    QPainter

    注意:

    • 画图需要重写绘图事件虚函数。
    • 如果在窗口绘图,必须放在绘图事件里实现。
    • 绘图事件由内部自动调用,比如窗口需要重绘的时候(即状态改变,如窗口大小改变),当然也可以人为调用。
    void painterEvent(QPaintEvent *ev);
    

    P.S:使用QPainter要引入头文件<QPainter>
    下面我们来创建一个QPainter对象:

    void Widget::paintEvent(QPaintEvent *event){
    	//方法一
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
        
        //方法二
    //    QPainter p;
    //    p.begin(this);
    //    //绘图操作
    //    p.end();
    }
    

    P.S:创建一个绘图对象QPainter时,要为它指定一个绘图设备,指定绘图设备有两种方式,一种是直接在构造函数中指定,另一种是通过调用QPainter对象的begin()方法和end()方法来实现。
    接着,用这个QPainter对象来画一个背景图:

    #include "Widget.h"
    #include <QPainter>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
        p.drawPixmap(0,0,width(),height(),QPixmap("C:/Users/MSI-NB/Desktop/timg.jpg"));
    }
    

    P.S:

    • 这里的参数是绘图的一个矩形范围,以及Pixmap对象的路径来源。
    • 用rect()代替0,0,width(),height()效果是一样的

    实现效果:
    在这里插入图片描述
    每次改变窗口大小都会引起重绘,系统自动调用paintEvent(),每次都会重新获取窗口的宽度和高度,所以我们看到画面会随着窗口大小的改变而改变,达到了自适应的效果。

    我们还可以用QPainter来画线:

    #include "Widget.h"
    #include <QPainter>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
        p.drawPixmap(0,0,width(),height(),QPixmap("C:/Users/MSI-NB/Desktop/timg.jpg"));
        //画线
        p.drawLine(5,5,50,5);
        p.drawLine(50,5,50,55);
        p.drawLine(50,55,5,55);
        p.drawLine(5,55,5,5);
    }
    

    实现效果:
    在这里插入图片描述

    QPen

    显然,这样画出来的线过于单调,这时需要用QPen来对画笔进行设置:

        QPen pen;
        pen.setWidth(50);	//设置画笔粗细
        pen.setColor(QColor(97,3,5));	//设置画笔颜色
    

    P.S:使用QPen需要先引入头文件<QPen>,并且不要忘了将画笔“交给”画家!

    #include "Widget.h"
    #include <QPainter>
    #include <QPen>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
        p.drawPixmap(0,0,width(),height(),QPixmap("C:/Users/MSI-NB/Desktop/timg.jpg"));
        //定义画笔
        QPen pen;
        pen.setWidth(5);    //设置画笔粗细
        pen.setColor(QColor(97,3,5));   //设置画笔颜色
        
        //将画笔交给画家,非常关键,注意它在代码中的位置,要先于画家的动作
        p.setPen(pen); 
        
        //画线(画家的动作)
        p.drawLine(5,5,50,5);
        p.drawLine(50,5,50,55);
        p.drawLine(50,55,5,55);
        p.drawLine(5,55,5,5);
    
    }
    
    

    P.S:将画笔交给画家这一步一定要先于画家的动作,否则画笔的设置不会生效。

    实现效果:
    在这里插入图片描述
    P.S:常用的颜色可以用Qt::颜色英文名进行设置,如Qt::red,但是能用英文单词表示的颜色有限,一般都采用rgb表示法,如QColor(97,3,5)

    还可以调用QPen的setStyle()setBrush()来设置画笔的风格、填充效果。

    画笔风格如下:
    在这里插入图片描述
    下面来试试对画笔设置样式:

    #include "Widget.h"
    #include <QPainter>
    #include <QPen>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
      //  p.drawPixmap(0,0,width(),height(),QPixmap("C:/Users/MSI-NB/Desktop/timg.jpg"));
        //定义画笔
        QPen pen;
        pen.setWidth(5);    //设置画笔粗细
        pen.setColor(QColor(97,3,5));   //设置画笔颜色
        pen.setStyle(Qt::DashDotLine);  //设置画笔样式
        
        //将画笔交给画家,非常关键
        p.setPen(pen);  
        
        //画线
        p.drawLine(5,5,50,5);
        p.drawLine(50,5,50,55);
        p.drawLine(50,55,5,55);
        p.drawLine(5,55,5,5);
    }
    
    

    实现效果:
    在这里插入图片描述

    QBrush

    当我们画出一个封闭图形时,还可以用画刷QBrush来实现填充效果。

    填充效果如下:
    在这里插入图片描述
    下面我们画出矩形、圆形,并为它们设置填充效果:

    绘制矩形需要调用drawRect(),参数是左上角坐标,以及矩形宽度和长度,当然也有其它重载函数,可以用键盘 ↑ ↓ 键来查看。

    //画矩形
    p.drawRect(100,100,50,50);
    

    绘制圆形需要调用drawEllipse(),参数是圆心位置和长轴、短轴的长度。当长轴和短轴长度一样时,画出来的就是圆形了。

    //画圆形                        
    p.drawEllipse(150,150,80,80);
    

    实现效果:
    在这里插入图片描述
    接下来,引入<QBrush>头文件,并为画家递上画刷:

    #include "Widget.h"
    #include <QPainter>
    #include <QPen>
    #include <QBrush>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
      //  p.drawPixmap(0,0,width(),height(),QPixmap("C:/Users/MSI-NB/Desktop/timg.jpg"));
        //定义画笔
        QPen pen;
        pen.setWidth(2);    //设置画笔粗细
        pen.setColor(QColor(97,3,5));   //设置画笔颜色
        pen.setStyle(Qt::DashDotLine);  //设置画笔样式
        //定义画刷
        QBrush brush;
        brush.setColor(QColor(16,7,117));	//设置画刷颜色
        brush.setStyle(,Qt::HorPattern);	//设置画刷样式
        p.setBrush(brush);	//将画刷交给画家
        p.setPen(pen);  //将画笔交给画家
        //画线
        p.drawLine(5,5,50,5);
        p.drawLine(50,5,50,55);
        p.drawLine(50,55,5,55);
        p.drawLine(5,55,5,5);
        //画矩形
        p.drawRect(100,100,50,50);
        //画圆形
        p.drawEllipse(150,150,80,80);
    }
    
    

    实现效果:
    在这里插入图片描述
    总结:QPainter相当于画家,QPaintDevice相当于画板,画家要使用画笔和画刷时,都要调用相应的setXXX()方法。而画家在画板上绘制的每个图案,都要调用相应的drawXXX()方法。

    手动更新窗口

    我们知道,改变窗口大小会引起重绘,但是每次改变窗口大小才改变画面是不现实的,很多时候我们需要手动更新窗口。

    查看帮助文档可以知道,很多情况下系统都会自动调用窗口的重绘,我们也可以通过调用窗口对象的update()方法,该方法会自动触发重绘事件,相当于间接调用paintEvent()

    P.S:update()可以通过设置参数指定重绘区域,如果不给参数,默认重绘整个窗口。当然还有其它重载函数。

    下面我们来用update()实现点击鼠标让小喻言跑起来的效果:

    //Widget.h
    #ifndef WIDGET_H
    #define WIDGET_H
    #pragma execution_character_set("utf-8")
    #include <QWidget>
    #include <QPushButton>
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = 0);
        ~Widget();
    protected:
        void Widget::paintEvent(QPaintEvent *event);
        void Widget::yuyanRun();    //喻言跑起来
    private:
        int x;
        QPushButton *p_button=new QPushButton(this);
    };
    #endif // WIDGET_H
    
    //Widget.cpp
    #include "Widget.h"
    #include <QPainter>
    #include <QPen>
    #include <QBrush>
    #include <QEvent>
    #include <QDebug>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        x=0;
        p_button->move(300,300);
        p_button->setText("冲啊");
        connect(p_button,&QPushButton::pressed,this,&Widget::yuyanRun);
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);   //创建画家对象,指定当前窗口为绘图设备
    	//这部分代码省略
        //画喻言
        p.drawPixmap(x,70,119,170,QPixmap("‪C:/Users/MSI-NB/Desktop/yuyan.png"));
    }
    void Widget::yuyanRun(){
        x+=20;	//每次图案横坐标移动20
        update();	//系统自动调用paintEvent
    }
    

    实现效果:
    在这里插入图片描述

    图源:cytusss_,侵删。

    P.S:在这里犯了一个错误,导致无论如何点击按钮都没有反应,原因是将变量x=0;写在了paintEvent()函数体内-_-||

    注意:

    • 千万不要把update()放到paintEvent()方法的实现中,否则程序将会进入死循环。
    • 也不要在绘图事件中做太复杂的数据处理,因为你无法知道什么时候绘图事件会自动调用。
    • 如果要在窗口上绘图,一定要在paintEvent()里实现!!

    QBitmap与QPixmap的区别

    QBitmap是QPixmap的子类,QPixmap常用来绘制彩色图片,而QBitmap只有黑白两种颜色。

    使用QPixmap和QBitmap要引用头文件<QPixmap><QBitmap>,虽然后者是继承自前者的,但少了头文件声明还是会报错,提示QBitmap没有构造函数。

    p.drawPixmap(x,70,119,170,QPixmap("‪C:/Users/MSI-NB/Desktop/yuyan.png"));  
    p.drawPixmap(x,400,119,170,QBitmap("‪C:/Users/MSI-NB/Desktop/yuyan.png")); 
    

    实现效果 :在这里插入图片描述
    P.S:如果图片背景是透明的,用QBitmap显示时,也会变成黑色。而如果图片背景是白色的,用QBitmap显示时,则会变成透明。

    绘图设备

    绘图设备主要有QPixmap、QImage、QPicture

    • QPixmap是最常用的,因为它是在屏幕上面绘图的,并且针对屏幕进行优化了,但它是平台相关的,且不能对图片进行修改。
    • QImage是平台无关的,可以对图片进行修改,比如修改某个像素点的颜色,还可以在线程中绘图。
    • QPicture是用来保存绘图状态(二进制文件)的。

    QPixmap

    在前面的实验中,我们都是将图形绘制在窗口中,但是绘图设备不仅可以是窗口,我们可以在上面绘图,然后再将图像保存到指定的路径。

    #include "Widget.h"
    #include <QPainter>
    #include <QPixmap>
    #include <QBrush>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        //绘图设备
        QPixmap map(400,300);
        //画家
        QPainter p(&map);
        //填充背景
        p.fillRect(0,0,400,300,QBrush(QColor(97,3,5)));
        //绘制矩形
        p.drawRect(0,0,120,120);
        //保存图片
        map.save("C:/Users/MSI-NB/Desktop/map.jpg");
    }
    
    Widget::~Widget()
    {
    
    }
    
    

    实现效果:
    在这里插入图片描述
    P.S:填充背景不仅可以调用QPainter的fiilRect()方法,还可以调用QPixmap的fill()方法。

    注意:使用填充的方法来进行背景的绘制时,要先于其它图形的绘制,否则会把其它图形遮盖住。

    QImage

    QImage的用法和QPixmap一样,但是QImage可以很容易地实现使图像背景为透明的效果。

    #include "Widget.h"
    #include <QPainter>
    #include <QPixmap>
    #include <QBrush>
    #include <QImage>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    //    //绘图设备
    //    QPixmap map(400,300);
    //    //画家
    //    QPainter p(&map);
    //    //填充背景
    //    p.fillRect(0,0,400,300,QBrush(QColor(97,3,5)));
    //    //绘制矩形
    //    p.drawRect(0,0,120,120);
    //    //保存图片
    //    map.save("C:/Users/MSI-NB/Desktop/map.jpg");
        //绘图设备
        QImage img(400,300,QImage::Format_ARGB32);	//设置背景为透明
        //画家
        QPainter p(&img);
        //绘制矩形
        p.drawImage(0,0,QImage("C:/Users/MSI-NB/Desktop/yuyan.png"));
        //保存图片
        img.save("C:/Users/MSI-NB/Desktop/img.jpg");
        img.save("C:/Users/MSI-NB/Desktop/img.png");
    }
    
    Widget::~Widget()
    {
    
    }
    

    P.S:使用QImage::Format_ARGB32可以将背景设为透明,查看帮助文档解锁更多用法~

    实现效果:
    在这里插入图片描述
    同样一张图,在背景为透明的前提下,保存成png格式与jpg格式的显示效果是不同的。

    注意:为图片设置背景为透明后,只有保存的文件格式为png时才能正常显示出透明背景!!如果保存为jpg格式,是不会有透明效果的!!补充一个小知识点——jpg是以RGB来存储图像,而png是以ARGB来存储图像,A(Alpha)即为透明度。

    QPicture

    QPicture用来存储绘画的状态,可以记录和重现QPainter命令的绘图设备。QPicture要配合QPainter的begin()方法使用。

    //绘图设备                                                       
    QImage img(400,300,QImage::Format_ARGB32);                   
    //画家                                                         
    QPainter p(&img);
    //绘制图案                                            
    p.drawImage(0,0,QImage("C:/Users/MSI-NB/Desktop/yuyan.png"));
    //定义QPicture对象
    QPicture pic;                                                
    //QPicture对象作为参数传入begin()                                                             
    p.begin(&pic);
    //图像状态保存路径                                               
    pic.save("C:/Users/MSI-NB/Desktop/picture.png");             
    p.end();                                                     
    

    实现效果:
    在这里插入图片描述
    发现文件打不开,那么这个文件有什么用呢?
    虽然我们无法直接通过该二进制文件看到图像内容,但是我们用save()方法将它保存在某个路径,自然可以通过load()方法将其取出,并呈现在我们需要它呈现的地方。结合之前学过的计时器,我们来实现一个简单的小动画:

    //Widget.h
    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #pragma execution_character_set("utf-8")
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = 0);
        ~Widget();
    protected:
        void Widget::paintEvent(QPaintEvent *event);
        void Widget::timerEvent(QTimerEvent *event);
    private:
        int x;
    };
    
    #endif // WIDGET_H
    
    
    #include "Widget.h"
    #include <QPainter>
    #include <QPixmap>
    #include <QBrush>
    #include <QImage>
    #include <QPicture>
    #include <QTimerEvent>
    #include <QDebug>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    		//初始化横坐标
            x=0;
            //启动计时器
            startTimer(100);
            //定义画家
            QPainter p;
            //定义QPicture对象
            QPicture pic;
            //关联画家与状态
            p.begin(&pic);
            //绘制图像
            p.drawImage(0,70+x,QImage("C:/Users/MSI-NB/Desktop/yuyan.png"));
            p.drawText(50,50,"可爱的我来了");
            p.end();
            //指定图像保存路径
            pic.save("C:/Users/MSI-NB/Desktop/picture.png");
    
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
    	//定义QPicture对象
        QPicture pic;
        //读取图像状态
        pic.load("C:/Users/MSI-NB/Desktop/picture.png");
      	//定义画家
        QPainter p;
        //在当前窗口绘制
        p.begin(this);
        p.drawPicture(x,70,pic);
        p.end();
    
    }
    void Widget::timerEvent(QTimerEvent *event){
        x+=5;
        update();	//手动调用重绘事件
    }
    

    在这里插入图片描述

    QPixmap和QImage的转化

    QPixmap对屏幕进行了优化,但它是平台相关的;而QImage是平台无关的,却没有对屏幕进行优化,为了将两者的优点结合起来,有时候需要对它们进行转化。在绘制时使用QPixmap,在传输时使用QImage。

    //QPixmap -> QImage
    QImage image=pixmap.toImage();	//这个是成员函数
    //QImage -> QPixmap
    QPixmap pixmap=Pixmap::fromImage(image);	//这个是静态函数
    

    P.S:注意从QPixmap转化成QImage调用的是QPixmap对象的toImage()成员函数,从QImage转化成QPixmap调用的是QPixmap类的fromImage()静态函数。

    不规则窗口

    有时候我们打开一些应用程序时,会出现一个类似软件LOGO的东西,然后才进入程序操作界面,这个类似软件LOGO的东西也是一种窗口,是一种不规则的窗口。那么如何实现不规则的窗口呢?需要去窗口边表框,并将图像背景设置为透明:

    #include "Widget.h"
    #include <QPainter>
    #include <QPixmap>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        //去窗口边框
        setWindowFlags(Qt::FramelessWindowHint|windowFlags());
        //设置窗口背景为透明
        setAttribute(Qt::WA_TranslucentBackground);
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);
        p.drawPixmap(0,0,QPixmap("C:/Users/MSI-NB/Desktop/yuyan.png"));
    
    }
    
    

    注意:setWindowFlags(Qt::FramelessWindowHint|windowFlags());里的windowFlags()不能忘。

    下面是去边框后的实现效果:
    在这里插入图片描述
    下面是背景设为透明后的实现效果:
    在这里插入图片描述
    是不是很酷!!但是去边框的同时也去掉了边框中带有的关闭按钮,同时也无法自由拖动窗口。因此,要实现一个真正的不规则窗口,我们还应该重写一些事件处理函数。

    首先要明确,当我们点击窗口并拖动的时候,是相对于窗口左上角同步移动的,因此要获取到窗口左上角的初始坐标(相对于屏幕),以及鼠标当前的坐标(相对于屏幕),利用两个坐标之差来改变窗口在屏幕中的位置。

    拖动窗口可以看作由两个鼠标事件组成——鼠标点击和鼠标移动。当鼠标点击事件发生时,求出窗口左上角坐标;当鼠标移动事件发生时,改变窗口在屏幕中的位置。

    移动窗口需要用到move()方法,参数是窗口的左上角位置,因此在我们求出窗口左上角坐标鼠标点击事件发生时鼠标的坐标的差值之后,要利用这个差值和鼠标移动事件发生时鼠标的坐标来反求出拖动窗口时,窗口左上角在屏幕中的坐标位置

    话不多说,上代码:

    #include "Widget.h"
    #include <QPainter>
    #include <QPixmap>
    #include <QMouseEvent>
    #include <QDebug>
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        //去窗口边框
        setWindowFlags(Qt::FramelessWindowHint|windowFlags());
        //设置窗口背景为透明
        setAttribute(Qt::WA_TranslucentBackground);
    }
    
    Widget::~Widget()
    {
    
    }
    void Widget::paintEvent(QPaintEvent *event){
        QPainter p(this);
        p.drawPixmap(0,0,QPixmap("C:/Users/MSI-NB/Desktop/yuyan.png"));
    
    }
    void Widget::mousePressEvent(QMouseEvent *event){
        if(event->button()==Qt::RightButton){
            //若按下的是鼠标右键,则关闭窗口
            close();
        }else if(event->button()==Qt::LeftButton){
            //求出窗口左上角和鼠标当前坐标的差值
            point=event->globalPos()-this->frameGeometry().topLeft();
        }
    
    }
    void Widget::mouseMoveEvent(QMouseEvent *event){
        //判断是否按下鼠标左键
        if(event->buttons()&Qt::LeftButton){
            //移动窗口
            move(event->globalPos()-point);
        }
    }
    

    说明:

    • setWindowFlags()中的WindowFlags是零个或多个窗口系统提示的组合。
      WindowFlags实际上是32位的一个参数,它的每一位都有不同的含义,也对应了不同的功能。
      如果我们要使用某个功能,比如去窗口边框,就要将windowFlags()与Qt::FramelessWindowHint进行按位或( | )
      如果要去掉一个功能,比如去掉最大化按钮,就要将windowFlags()与Qt::WindowMaximizeButtonHint按位取反( ~ )的结果进行按位与( & )
    this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);	//实现功能
    setWindowFlags(windowFlags()& ~Qt::WindowMaximizeButtonHint); //去除功能
    

    【转自:https://blog.csdn.net/zoukp12345/article/details/78014850】
    【转自:https://blog.csdn.net/xuebing1995/article/details/96478891】

    • globalPos()获取的是对象相对于屏幕的坐标。
    • frameGeometry()获取的是带边框的窗口在其父组件中的信息,返回一个矩形。
    • buttons()用来检查可能伴随鼠标事件的鼠标按钮。

    假设我的鼠标左键已经按下。
    若移动鼠标,会发生的move事件,button返回Qt::NoButton,buttons返回LeftButton。
    再按下右键,会发生press事件,button返回RightButton,buttons返回LeftButton|RightButton
    再移动鼠标,会发生move事件,button返回Qt::NoButton,buttons返回LeftButton|RightButton
    再松开左键,会发生Release事件,button返回LeftButton,buttons返回RightButton
    总而言之,button返回“那个按钮发生了此事件”,buttons返回"发生事件时哪些按钮还处于按下状态"
    【转自:https://www.cnblogs.com/fengyaoyao/p/7232859.html】

    实现效果:
    在这里插入图片描述
    P.S:如有错误,欢迎指正~

    展开全文
  • UML   UML解析:  示例程序: 如上面所示,我们有四个不同的状态,以及四个不同的操作,每个操作都可能改变状态. 运用状态模式代码如下: package com.lwf.state; public interface State { public...
  • 1、活动  活动类似于传统意义上的流程,它是用来描述一个过程或者操作的工作步骤,类似于Flow Diagram。...包含活动状态(ActionState),活动状态是指业务用例的一个执行步骤或一个操作,不是普通对象的状
  • 文章目录Buffer状态acquireBuffer流程releaseBuffer流程分析小结 Buffer状态 对于生产者这边,BufferQueue的流程基本讲完了。简单说来,首先提需求,告诉BufferQueue需要什么样的Buffer,大小,格式,usage等等;...
  • 经过设计现在对宽带连接的引用只是产生具体IDoWork对象的接口,这样降低了很多耦合...这里对宽带的操作是调用系统函数,我们只对外提供三个方法,连接宽带、断开宽带、检查连接状态,在该类的构造函数中我们注册Appl...
  • wpa_supplicant/hostapd程序里面的核心部分,应该要算...下面是wpa_supplicant 模块结构,红线部分是状态机模块,它就像一个桥架在那里,如果左边进来的的数据想要到右边去实现加密或者解密,就必须经过这座桥,而且
  • 水题,本来想暴力的,后面突然想到之前做过有道有关状态转存的题目,就想用那个去解。应该算是dp吧,算了下可能会超时,但暴力也会超,所以不如试一试。 思路:建立一个二维数组,每次输入一个新的数时,刷新一下...
  • 十二个UML工具

    2015-01-04 12:30:59
    1. StarUMLStarUML是一个开源UML项目,可以开发快速,灵活,可扩展,多功能并且免费的UML/MDA平台。...2. Netbeans UML PluginNetBeans UML插件目前支持以下UML图:活动图,类图,序列图,状态图,以...
  • 本电路实现了异步十二进制加法计数器的功能. 该设计思路用观察时序的方式求出时钟方程, 再根据时钟取值修改状态表, 之后再求出状态激励方程(D触发器).
  • 一、问题描述 某同步时序逻辑电路如下所示 ⑴写出输出方程、激励方程和状态...⑶试画出状态转换; ⑷设各触发器初始状态均为0,试画出中Q1、Q2和Z的输出波形。 ⑸简单描述该电路的功能。 二、问题解答 ...
  • 乐优商城(十二)购物车

    千次阅读 2020-05-12 00:22:36
    购物车功能分析2.1 需求分析2.2 技术选型2.3 流程3. 未登录购物车3.1 购物车数据结构3.2 获取购物数量3.3 添加购物车3.4 渲染购物车页面3.4.1 封装判断用户的登录状态方法3.4.2 查询购物车3.4.3 渲染页面3.5 修改...
  • PostGIS教程十二:投影数据

    千次阅读 2019-01-14 10:48:10
    目录 一、比较数据 二、转换数据 三、投影练习 3.1、练习 地球不是平的,也没有简单的方法把它放在一张平面纸...一些投影试图找到一个很好的中间混合状态,在几个参数上只有很小的失真。所有投影的共同之处在...
  • (2)根据状态表, 绘制十二进制加法计数器的时序, 并从中找出Q0、Q1、Q2和Q3的变化规律. (此处时序图略) 从时序中可以发现, Q0变化在系统时钟CP的上升沿处, Q1在Q0下降沿处变化, Q2在Q0下降沿处变化, Q3在Q0下降...
  • 含有protues和程序(全注释,连变量都注释) 要求: 1、在锁开的状态下输入密码,设置的密码共4位,用数据开关K1∼K10分别代表数字1、2、…、9、0,输入的密码用数码管显示,最后输入的密码显示在最右边的数码管上...
  • Java自学(十二、Java的多线程详解)Java 多线程详解及实战小例子线程简介程序、进程、线程小结线程实现继承Tread类继承Thread实战案例:下载图片实现Runnable接口(十分推荐,因为Java是单继承,继承了Thread就没...
  • 51单片机(十二)—— 单片机复位

    千次阅读 2020-02-19 19:50:38
    复位期间,大部分控制和状态寄存器被迫使用初始值,单片机的外围模块被禁止,单片机用于复位的复位源有加电复位、外部RESET管脚复位、看门狗复位等。 我们实验对应的硬件复位电路,如下所示。 这个中集成...
  • 任务目的 学习了解 CSS 3 都有哪些新特性...实现正常状态和focus状态宽度不一致的input文本输入框,且鼠标焦点进入输入框时,宽度的变化以动画呈现 不使用JavaScript,实现一个Banner轮流播放的效果,且点击右...
  • 1、马尔可夫链:时刻t+1下状态的概率分布只与时刻t下状态有关,与该时刻以前的状态无关。  数学公式表达:  图形表示:   2、隐马尔可夫模型:状态(z)不可直接观测的马尔可夫链。  HMM由初始概率分布π、...
  •  电路工作原理 该铅酸蓄电池充电器电路由恒流充电电路、短路保护电路和状态显示电路等组成,如5-116所示。  恒流充电电路由电源变压器T、整流二极管VD1~VD4和电源调整管V1等组成。 短路保护电路由电阻器R4...
  • 而现在,在面向对象的时代,我们则是绘制行动图、状态图,以及编写用例说明来完成这部分工作。  在这部分工作中,编写用例说明应当是最主要的工作,之后在一些关键部分辅之以行动图、状态图。现在我们来看看用例...
  • 1.图形数据流:中最左侧是指渲染器,用于生产graphics buffers, 比如状态栏,systemUI等. 再来看看中BufferQueue的工作 2.生成者消费者模式 生产者和消费者运行在不同的进程. 生产者请求一块空闲的缓存区:...
  • 举个例子,假设我们有四个样本(行),每个样本有三个特征(列),如: 我们的feature_1有两种可能的取值,比如是男/女,这里男用1表示,女用2表示。feature_2 和feature_3各有4种取值(状态)。one-...
  • JavaScript前端开发模块化教程任务12 折叠面板任务导入目录成果目标核心知识contents任务实施强化训练学习成果评量PART1任务导入...状态用户可以单击折叠项目标题栏切换当前标题下的内容是否显示本任务完成后的效果如...
  • Activated:是否激活这个摇杆控件,即使这个属性是disable状态摇杆控件也可见。 Visible:是否显示摇杆。 Use Fiexed Update:如果使用物理系统的更新频率的话需要勾选这个属性。 Position&amp;Size:设置摇杆...
  • 马尔科夫模型是一种概率模型,它描述了一类重要的随机过程(随机过程又称为随机函数,是随时间而随机变化的过程)。我们常常需要考察一个随机变量序列,这些随机变量序列并不是相互独立的,每个随机变量的值都依赖于...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 228
精华内容 91
关键字:

十二状态图