QT在Windows中的技术总结(四):做整套自定义程序窗口

ps:请跳开吐槽从红色字处看起。。。。。。

        好久没更新这里了~某一部分原因是懒了。。更重要的原因,是在之前写(三)之后到现在,项目不断地发生不同情况~
        之前某工程师说,另外一个工程师说另一个东西比较急用,可不可以先做那个,于是向老板申请了,然后老板也批准了,然后就停掉手头上那个转去做那个,然后做了两个月,后来公司搞体系考核,我又忙于画各种产品的工程图纸,然后还要兼任当质管部负责人(小公司各种身兼数职各种坑爹!!~)公司以前都没有什么进料检验、过程检验、成品检验的各种规范和单据,于是全部都要我做!!估计我是程序员里最苦逼的程序员了T T 最后老板还跟我说,你怎么这个项目做了快一年了都没有完成啊!!我内个去!心里千万只草泥马狂奔啊~亲你敢不让我干各种杂事各种打断我,三两天画张工程图纸么!

        吐槽完毕, 言归正传,当我再回到这个项目的时候,以前的程序界面觉得怎么看怎么丑。于是把心一横,就决定做个全自定义的界面了,可是这条路真的一去不回头啊!!
        我在站酷网看各种别人设计的软件界面,还有各种配色方案,最后选了个主调色彩,还有几个主搭配色,还有图标的样式什么的,因为公司是医疗器械行业,所以个人觉得应该简洁大方,基本上就选了三四个颜色。后来跟几个读设计的朋友聊起,发现他们都是在站酷网拿灵感和素材的><(我真的没有在打广告~)

1、去掉windows的系统自带外框( 标题栏

  1. this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);
Qt : : FramelessWindowHint系统的外框,但是去掉以后,在任务栏上的右键菜单也没有了,个人觉得这个这个东西还是挺有必要的,所有加上Qt::WindowSystemMenuHint,而Qt::WindowMinimizeButtonHint则是让这个菜单上有最小化功能,不加的话,菜单就只有关闭这个功能了。

2、搭建自己的外框(标题栏)
    美观的同时可不能忽略了用户的习惯,所以自定义的外框上要可移动,还有最起码的关闭按钮,我还多加了一个最小化按钮。
    我做的外框的方式,是在ui里面添加一个widget,命名为widget_title,然后用过滤器处理它的移动。用label类做一个按键类(这样点击就可以没虚线框了)。

构造函数中:
  1. ui->widget_title->installEventFilter(this);

重写过滤器:

bool Interface::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::WindowActivate)//窗口最小化还原后重绘整个窗口
    {
        this->repaint();
        //qDebug()<<"repaint";
    }
    if(obj == ui->tableView_patModel)//选择患者后出现添加测试按钮。
    {
        if(event->type() == QEvent::FocusIn)
        {
            if(patModel->rowCount()>0)
            {
                addButton->show();
            }
        }
        else if(event->type() == QEvent::FocusOut)
        {
            addButton->hide();
        }
    }/*
    if(obj == ui->widget_title)
    {
        if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove)
        {
            QMouseEvent *mouEvent = (QMouseEvent *)event;
            if(event->type() == QEvent::MouseButtonPress)
            {
                clickPos = mapToGlobal(mouEvent->pos());//当前鼠标位置
                widgetPos = mapToGlobal(QPoint(0,0));//当前窗口位置
                //qDebug()<type() == QEvent::MouseMove)
            {
                this->move(widgetPos + mouEvent->globalPos() - clickPos);//窗口位置+鼠标移动位置之差=窗口新位置
            }
        }

    }*/

    return QDialog::eventFilter(obj,event);
}
QEvent : : WindowActivate 发现窗口最小化后,的所有按钮都触发不了,于是我让它还原的时候刷新一下界面。

    后来发现一个问题,窗口几乎是没边界的,如果三四个这样的窗口叠加起来,完全不知道哪里属于哪里了。于是打算给窗口加个阴影边框。

重写构造函数:

this->setAttribute(Qt::WA_TranslucentBackground);

重写绘图事件

void Interface::paintEvent(QPaintEvent *event)
{/*
    QPainterPath path;
    path.setFillRule(Qt::WindingFill);
    path.addRect(10, 10, this->width()-20, this->height()-20);

    QPainter painter(this);

    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.fillPath(path, QBrush(QColor(246, 244, 219)));

    QColor color(0, 0, 0, 50);
    for(int i=0; i<10; i++)
    {
        QPainterPath path;
        path.setFillRule(Qt::WindingFill);
        path.addRect(10-i, 10-i, this->width()-(10-i)*2, this->height()-(10-i)*2);
        color.setAlpha(150 - sqrt(i)*50);
        painter.setPen(color);
        painter.drawPath(path);
    }*/
    QDialog::paintEvent(event);
}


    使用了这个绘图边框以后,真正显示的外框四周会各向内缩进10(那10用于显示阴影边框了),因此里面的所有东西都要挪一下,不然就显示不全了。
    这个这么复杂的东东来自度娘的搜索,不过不记得出处了请见谅~

3、按键的美化
    前面有说到,我是用label来做基类的,因为但系pushbutton会有点击出现虚线框等问题,而且本人对label更熟悉,觉得很好控制。
    我的按键分为当前状态、选中状态、点击状态、不可用状态,四个状态,图片放到资源(.qrc)里面调用。

    我不会告诉你我这个苦逼程序员连美工都包了的。。。



funcButton.h

#ifndef FUNCBUTTON_H
#define FUNCBUTTON_H

#include 

class funcButton : public QLabel
{
    Q_OBJECT
public:
    explicit funcButton(QWidget *parent = 0);

    void setButtonPicture(QString pic);//设置按键正常状态
    void setPressPicture(QString pic);//设置按键点击状态
    void setSelectPicture(QString pic);//设置按键选择状态
    void setDisablePicture(QString pic);//设置按键不可以状态
    void set_X_Y_width_height(int x, int y, int width, int height);//设置按键坐标和大小
    void resizeit(int w , int h);//更改按键大小
    void setDisabled(bool);//不使能按键
    void setEnabled(bool);//使能按键

protected:
    void mousePressEvent (QMouseEvent *event);//重新鼠标按下事件
    void mouseReleaseEvent (QMouseEvent *event);//重写鼠标释放事件
    void enterEvent(QEvent *);//重写鼠标进入事件
    void leaveEvent(QEvent *);//重写鼠标离开事件

private:
    QString buttonPicture,pressPicture,selectPicture,disablePicture;//保存图片位置
    bool enableState;//使能标志位

signals:
    void clicked();//点击信号

};

#endif // FUNCBUTTON_H

funcButton.cpp

#include "funcButton.h"

funcButton::funcButton(QWidget *parent) :
    QLabel(parent)
{
    enableState = true;
}

void funcButton::setButtonPicture(QString pic)
{
        buttonPicture = pic;
        this->setStyleSheet(buttonPicture);
}

void funcButton::setPressPicture(QString pic)
{
        pressPicture = pic;
}

void funcButton::setSelectPicture(QString pic)
{
        selectPicture = pic;
}

void funcButton::setDisablePicture(QString pic)
{
        disablePicture = pic;
}

void funcButton::set_X_Y_width_height(int x, int y, int width, int height)
{
        this->setGeometry(x, y, width, height);
}

void funcButton::mousePressEvent (QMouseEvent *event)
{
    if(!enableState)
        return;
    this->setStyleSheet(selectPicture+pressPicture);
}

void funcButton::mouseReleaseEvent (QMouseEvent *event)
{
    if(!enableState)
        return;
    this->setStyleSheet(buttonPicture+selectPicture);
    emit clicked();
}

void funcButton::enterEvent(QEvent *event)
{
    if(!enableState)
        return;

    QString toolTip = "QToolTip {background-color:white;}";
    QString label = QString("QLabel {%1}").arg(buttonPicture+selectPicture);
    //selectPicture写上颜色达到进入后更换底色的效果 selectPicture是图片就只有图片
    this->setStyleSheet(label+toolTip);//tooltip的背景保持白色
}

void funcButton::leaveEvent(QEvent *event)
{
    if(!enableState)
        return;
    this->setStyleSheet(buttonPicture);
}

void funcButton::resizeit(int w , int h)
{
    this->raise();
    this->resize(w,h);
}

void funcButton::setDisabled(bool disable)
{
    if(disable == true)
    {
        this->setStyleSheet(disablePicture);
        enableState = false;
    }
    else
    {
        this->setStyleSheet(buttonPicture);
        enableState = true;
    }
    QLabel::setDisabled(disable);
}

void funcButton::setEnabled(bool enable)
{
    enableState = enable;
    if(enable == true)
    {
        this->setStyleSheet(buttonPicture);
    }
    else
    {
        this->setStyleSheet(disablePicture);
    }
    QLabel::setEnabled(enable);
}


使用funcButton

saveButton = new funcButton(ui->widget_title);
QString pixmapButton,pixmapPress,pixmapSelect,pixmapDisable;
pixmapButton = "image: url(:/Pic/silver-save.png);";//按前图标
pixmapPress = "image: url(:/Pic/textured-save.png);" ;//按时图标
pixmapSelect = "background-color: rgb(14, 24, 33);";//选中时背景
pixmapDisable = "image: url(:/Pic/textured-save2.png);" ;//禁用图标
saveButton->setButtonPicture(pixmapButton);//一般状态
saveButton->setPressPicture(pixmapPress);//按下状态
saveButton->setSelectPicture(pixmapSelect);//选择状态
saveButton->setDisablePicture(pixmapDisable);//禁用状态
saveButton->set_X_Y_width_height(dialogWidth-410,9,61,61);

dialog Width 是窗口大小。由于我的窗口是改,但是又不想用那些布局的类,我总觉得我把握得不是很好,它们的位置老是会出乎我的意料,因此软件界面所有的位置我都是根据一开始获取的屏幕大小,然后用坐标算的,总觉得这样可控度高一些。

    还做了点一下 就处于按下去,再点一下就弹起来的按钮。

定义:

deafnessButton = new funcButton(ui->widget_title);
pixmapButton = "image: url(:/Pic/silver-noise.png);";//按前图标
pixmapSelect = "image: url(:/Pic/textured-noise.png);" ;//按时图标
deafnessButton->setButtonPicture(pixmapButton);//一般状态
deafnessButton->setSelectPicture(pixmapSelect);//选择状态
deafnessButton->set_X_Y_width_height(dialogWidth-121,35,101,31);//图标定位
connect(deafnessButton,SIGNAL(clicked()),this,SLOT(deafnessButton_clicked()));

点击槽:

void Interface::deafnessButton_clicked()
{
    if(deafnessFlg)
    {
        deafnessFlg = false;
        deafnessButton->setButtonPicture("image: url(:/Pic/silver-noise.png);");//一般状态
    }
    else
    {
        deafnessFlg = true;
        deafnessButton->setButtonPicture("image: url(:/Pic/silver-noise.png);background-color: rgb(14, 24, 33);");//按下状态
    }
}

    另外有一个比较特别的按键就是点了会有下拉菜单,放一些什么“关于”、“联系我们”什么的,虽然很简单,也顺道写一下~


QT在Windows中的技术总结(四):做整套自定义程序窗口_第1张图片
定义(构造函数):

moreButton = new funcButton(ui->widget_title);
QString pixmapButton,pixmapPress,pixmapSelect,pixmapDisable;
pixmapButton = "image: url(:/Pic/silver-more.png);";//按前图标
pixmapPress = "image: url(:/Pic/textured-more.png);" ;//按时图标
pixmapSelect = "background-color: rgb(14, 24, 33);";//选中时背景
moreButton->setButtonPicture(pixmapButton);//一般状态
moreButton->setPressPicture(pixmapPress);//按下状态
moreButton->setSelectPicture(pixmapSelect);//选择状态
moreButton->set_X_Y_width_height(dialogWidth-130,0,31,31);
moreButton->setToolTip(tr("更多"));
connect(moreButton,SIGNAL(clicked()),this,SLOT(moreButton_clicked()));

moreMenu = new QMenu(ui->widget_title);
connectAction = new QAction(moreMenu);
bakupAction = new QAction(moreMenu);


moreMenu->setStyleSheet("QMenu {background-color: white;}""QMenu::item:selected {background: rgba(224, 224, 224);}");

moreMenu->addAction(connectAction);
moreMenu->addSeparator();
moreMenu->addAction(bakupAction);

connect(moreMenu,SIGNAL(triggered(QAction *)),this,SLOT(onMenu_Triggered(QAction *)));//菜单响应连接

connectAction->setText(tr("重新连接"));
connectAction->setIcon(QIcon(":/Pic/arrow-bold-cycle.png"));

bakupAction->setText(tr("数据库备份"));
bakupAction->setIcon(QIcon(":/Pic/upload.png"));


moreButton和菜单点击的槽函数:

void Interface::moreButton_clicked()
{
    QPoint pos (moreButton->mapToGlobal(QPoint(0,0)).x(),moreButton->mapToGlobal(QPoint(0,0)).y()+31);

    moreMenu->exec(pos);
}

void Interface::onMenu_Triggered(QAction *action)
{
    if(action == connectAction)
    {
        //...
    }
    else if(action == bakupAction)
    {
        //...
    }
}

4、说说还没解决的小问题
1)如果用windows的窗框口,在子窗口弹出后,父窗口就会变白点,区分是不是焦点窗口,但是这样自定义的窗体就无法做到了~
    本来想过用模态窗口让父窗口变得不可点击,但是因为子窗口与父窗口信号携带的数据好多,做模态exec的话就不能用信号与槽, 传递数据好不方便。而用 this->setAttribute(Qt::WA_ShowModal,   true); 貌似没什么反应,估计可能这个参数本身是基于系统窗框的模式的,于是也只能放弃了。
    不知道怎么才能让子窗口弹出来的时候不让点击父窗口,同时信号与槽也正常。现在这样子窗口弹出来,父窗口还一直可以点击,子窗口可以出来很多个,这不科学~    

2)子窗口new的时候是(this)的话,子窗口就超不出父窗口,超出部分被隐藏了,我不知道用windows窗框会不会这样,于是只能new个(0),但是这样任务栏出现好多窗口,感觉这样子不太好,所以最后我是让父窗口最大化(不是用Qt::WindowFullScreen因为想留下任务栏,所以是用resize窗口做的,然后父窗口不做移动和阴影,子窗口才做)超出部分就直接超出桌面了~


5、程序启动界面(以下算是题外话吧)

    这个功能是给那些加载大的软件用的,加载很多数据的时候不让用户等太久,并告诉用户当前的加载状态。这个对于我这种小软件本来是用不着的,但是由于我看着那样很酷,还可以顺便给用户看个广告什么的,于是就用上啦,哈哈哈哈哈


QT在Windows中的技术总结(四):做整套自定义程序窗口_第2张图片

main.c

QSplashScreen *splash = new QSplashScreen;
splash->setPixmap(QPixmap(":/Pic/Initialization.png"));
//splash->setWindowOpacity(0.9);
splash->show();
Interface w;
Qt::Alignment topRight = Qt::AlignHCenter | Qt::AlignBottom;
splash->showMessage(QObject::tr("正在启动主界面..."),topRight, Qt::black);
splash->showMessage(QObject::tr("正在加载数据库模块..."),topRight, Qt::black);
splash->showMessage(QObject::tr("正在加载..."),topRight, Qt::black);
QDateTime n2=QDateTime::currentDateTime();
QDateTime now;
do{
    now=QDateTime::currentDateTime();
}while (n2.secsTo(now)<=6);//6为需要延时的秒数

splash->finish(&w);
int re = w.Wizard();
if(re == -1)
{
    return re;
}
delete splash;

6、只能运行一次
    这个是一个工程师提出的要求~我把它写在“软件启动界面”前面的~

main.c

QSystemSemaphore sema("JAMKey",1,QSystemSemaphore::Open);
sema.acquire();//在临界区操作共享内存 SharedMemory
QSharedMemory mem("SystemObject");//全局对象名
if (!mem.create(1))//如果全局对象以存在则退出
{
    QMessageBox::information(0, "HEARING FOR AD104",("HEARING FOR AD104 已经在运行!"));
    qDebug()<< sema.release();//如果是Unix系统,会自动释放。
    return 0;
}
sema.release();//临界区

7、将qDebug()打印到文本
    这个是方便调试用的,因为软件涉及到一些系统接口,因此要在xp win7 win8 32位 64位进行调试,但总不能到处装QT Creator吧,于是用了这个可以调试一些程序上的小问题,看看它在哪里出现异常什么的~

main.c

void customMessageHandler( QtMsgType type, const char *msg)
{
    QString txt;
    switch (type)
    {
        case QtDebugMsg:
            txt = QString("Debug: %1").arg(msg);
            break;
        case QtWarningMsg:
            txt = QString("Warning: %1").arg(msg);
            break;
        case QtCriticalMsg:
            txt = QString("Critical: %1").arg(msg);
            break;
        case QtFatalMsg:
            txt = QString("Fatal: %1").arg(msg);
            abort();
    }
    QString runPath = QCoreApplication::applicationDirPath();
    runPath.replace("/","\\");
    runPath += "\\debuglog.log";
    QFile outFile(runPath);
    outFile.open(QIODevice::WriteOnly | QIODevice::Append|QIODevice::Text);
    QTextStream ts(&outFile);
    ts << txt << endl;
}

然后在main函数里加上这么一句话

qInstallMsgHandler(customMessageHandler);

    最后的最后,传个软件主界面的窗口,纪念一下第一个一手包办的,独自完成的,还被各种吐槽的,即将随着设备出去给用户使用的软件,好紧张好忐忑啊~好担心不稳定的说~

    好吧我的ps功底有限,就只能是简单的搭个颜色什么的了~什么立体效果什么的离我几个星球远~我这美其名曰扁平化设计啦  哈哈   


QT在Windows中的技术总结(四):做整套自定义程序窗口_第3张图片

    the next 写一下windows的串口连接吧,单纯串口读写是比较简单的,但是让设备无数次重启软件都能稳定重连,这个做了比较久,下次有时间就写这个吧,软件稳定性比啥都重要,下次做串口连接就可以直接搬了~

    好久没写博客了~
    发现不知道是不是太长了,保存草稿的时候总是只保存前面的一部分。。。
    于是我只能每次换个HTML格式,然后用个文本保存一下~
    当我的浏览器又无缘无故所有网页一起刷新的时候,我很想哭T T
    不过总算大功告成啦啦啦啦啦
    有什么不足或者错的地方欢迎指出  嘻嘻
    the end

你可能感兴趣的:(MFC)