双击目录树展示图片

页面布局

在ProTreeWidget的构造函数中添加双击信号的槽函数连接

connect(this, &ProTreeWidget::itemDoubleClicked, this, &ProTreeWidget::SlotDoubleClickItem);

接下来实现双击逻辑,判断为鼠标左键,就用一个成员变量_selected_item缓存双击的item,然后发送SigUpdateSelected信号通知右侧区域刷新显示。

void ProTreeWidget::SlotDoubleClickItem(QTreeWidgetItem* doubleItem, int col){
    qDebug() << "ProTreeWidget::SlotDoubleClickItem" << endl;

    if(QGuiApplication::mouseButtons() == Qt::LeftButton)   //判断是否为左键
        {
            auto * tree_doubleItem = dynamic_cast<ProTreeItem*>(doubleItem);
            if(!tree_doubleItem){
                return;
            }
            int itemtype = (int)(tree_doubleItem->type());
            if(itemtype == TreeItemPic){
                emit SigUpdateSelected(tree_doubleItem->GetPath());
                _selected_item = doubleItem;
            }
    }
}

之前在MainWindow的构造函数里添加了ProTree,这次我们要在添加一个PicShow类,PicShow类是我们新增的Qt设计师界面类,这里介绍它的ui内容。 1 在PicShow里添加一个网格布局,然后将PicShow设置为水平布局。 2 然后拖动一个QWidget放到PicShow左侧,再拖动一个QWidget放到PicShow的右侧,拖动一个label放到中间。 3 然后设置两个Widget的宽度都为固定的80像素。将两个widget设置为垂直布局,然后分别添加一个button。 4 拖放两个水平的spacer和垂直的spacer分别放在label的上下左右,保证label居中。 效果如下 https://cdn.llfc.club/1674899963320.jpg ui信息 https://cdn.llfc.club/1674900340012.jpg 然后在MainWindow里添加PicShow

    _protree = new ProTree();
    ui->proLayout->addWidget(_protree,0);

    _picshow = new PicShow();
    ui->picLayout->addWidget(_picshow);

同时我们为PicShow新增qss文件

PicShow {
    border-color: #9F9F9F;
    border-style: solid;
    border-width: 1px 1px 1px 1px;
    padding: 10px;
    background: rgb(64,66,68);
}

自定义按钮

现在需要实现按钮的悬浮,点击效果,所以需要继承QPushButton,实现我们自己定义的按钮类PicButton。 添加C++类PicButton,基类选择QPushButton。 实现一个设置图标的函数,参数分别为正常状态,悬浮状态,以及按下状态的效果

void PicButton::SetIcons(const QString &normal, const QString &hover, const QString &pressed)
{
    _normal = normal;
    _hover = hover;
    _pressed = pressed;

    QPixmap tmpPixmap;
    tmpPixmap.load(normal);
    this->resize(tmpPixmap.size());
    this->setIcon(tmpPixmap);
    this->setIconSize(tmpPixmap.size());
}

重载event函数,实现根据事件类型刷新按钮样式的逻辑

bool PicButton::event(QEvent *event)
{
    switch (event->type())
        {
        case QEvent::Enter:
            setHoverIcon();
            break;
        case QEvent::Leave:
            setNormalIcon();
            break;
        case QEvent::MouseButtonPress:
            setPressIcon();
            break;
        case QEvent::MouseButtonRelease:
            setHoverIcon();
            break;
        default:
            break;
        }
         return QPushButton::event(event);
}

设置按钮样式的函数很简单,都是通过QPixmap加载的。

void PicButton::setNormalIcon(){
    QPixmap tmpPixmap;
    tmpPixmap.load(_normal);
    this->setIcon(tmpPixmap);
}

void PicButton::setHoverIcon(){
    QPixmap tmpPixmap;
    tmpPixmap.load(_hover);
    this->setIcon(tmpPixmap);
}

void PicButton::setPressIcon(){
    QPixmap tmpPixmap;
    tmpPixmap.load(_pressed);
    this->setIcon(tmpPixmap);
}

所以回到picshow.ui中,将两个QPushButton升级为PicButton。 这样我们在界面上将鼠标在按钮上点击和悬浮等就能看到效果了。

按钮的渐隐动画

接下来我们要实现将鼠标滑动到PicShow区域才显示前进和后退按钮,滑出PicShow区域则不显示。 为使按钮实现渐隐渐现的效果,所以我们通过动画实现。在PicShow的构造函数里创建两个渐隐渐现的动画,将效果绑定到两个按钮上。

    QGraphicsOpacityEffect *opacity_pre = new QGraphicsOpacityEffect(this);
    opacity_pre->setOpacity(0); //设置透明度0.5,透明范围:[0,1]
    ui->previousBtn->setGraphicsEffect(opacity_pre);


    QGraphicsOpacityEffect *opacity_next = new QGraphicsOpacityEffect(this);
    opacity_next->setOpacity(0); //设置透明度0.5,透明范围:[0,1]
    //应用到需要透明变化的控件;
    ui->nextBtn->setGraphicsEffect(opacity_next);

    //使用属性动画类让控件在透明度范围内变化
    _animation_show_pre = new QPropertyAnimation(opacity_pre, "opacity",this);
    _animation_show_pre->setEasingCurve(QEasingCurve::Linear);
    _animation_show_pre->setDuration(500); //动效时长3s


    _animation_show_next = new QPropertyAnimation(opacity_next, "opacity",this);
    _animation_show_next->setEasingCurve(QEasingCurve::Linear);
    _animation_show_next->setDuration(500); //动效时长3s

除此之外还重写PicShow的event函数,捕获其中的enter和leave事件

bool PicShow::event(QEvent *event)
{
    switch (event->type())
        {
        case QEvent::Enter:
            ShowPreNextBtns(true);
            break;
        case QEvent::Leave:
            ShowPreNextBtns(false);
            break;
        default:
            break;
    }
    return QDialog::event(event);
}

根据enter还是leave设置按钮显示和隐藏。显示隐藏的函数通过bool参数控制,根据是否可见并且是否隐藏综合控制动画。

void PicShow::ShowPreNextBtns(bool b_show)
{
    if(!b_show&&_b_btnvisible){
        _animation_show_pre->stop();
        _animation_show_pre->setStartValue(1);
        _animation_show_pre->setEndValue(0);
        _animation_show_pre->start();

        _animation_show_next->stop();
        _animation_show_next->setStartValue(1);
        _animation_show_next->setEndValue(0);
        _animation_show_next->start();
        _b_btnvisible = false;
        return;
    }

    if(_selected_path ==""){
        return;
    }

    if(b_show&&!_b_btnvisible){
        _animation_show_pre->stop();
       _animation_show_pre->setStartValue(0);
       _animation_show_pre->setEndValue(1);
       _animation_show_pre->start();

       _animation_show_next->stop();
       _animation_show_next->setStartValue(0);
       _animation_show_next->setEndValue(1);
       _animation_show_next->start();
       _b_btnvisible = true;
    }

}

这样鼠标滑动和移出PicShow区域就能显示和隐藏button了。

双击左侧目录树实现图片切换

为ProTreeWidget绑定双击的槽函数,并且实现双击后发送信号通知PicShow显示图片 ProTreeWidget构造函数添加

connect(this, &ProTreeWidget::itemDoubleClicked, this, &ProTreeWidget::SlotDoubleClickItem);

双击逻辑

void ProTreeWidget::SlotDoubleClickItem(QTreeWidgetItem* doubleItem, int col){
    qDebug() << "ProTreeWidget::SlotDoubleClickItem" << endl;

    if(QGuiApplication::mouseButtons() == Qt::LeftButton)   //判断是否为左键
        {
            auto * tree_doubleItem = dynamic_cast<ProTreeItem*>(doubleItem);
            if(!tree_doubleItem){
                return;
            }
            int itemtype = (int)(tree_doubleItem->type());
            if(itemtype == TreeItemPic){
                emit SigUpdateSelected(tree_doubleItem->GetPath());
                _selected_item = doubleItem;
            }
    }
}

在mainwindow构造函数中添加信号连接

    auto * pro_pic_show = dynamic_cast<PicShow*>(_picshow);
    connect(pro_tree_widget, &ProTreeWidget::SigUpdateSelected,pro_pic_show,
            &PicShow::SlotSelectItem);

在PicShow里实现SlotSelectItem显示选中图像

void PicShow::SlotSelectItem(const QString& path)
{
    _selected_path = path;

    _pix_map.load(path);
    auto width = this->width()-20;
    auto height = this->height()-20;
    _pix_map = _pix_map.scaled(width,height,Qt::KeepAspectRatio);
    ui->label->setPixmap(_pix_map);

}

这样就实现了点击左侧不同的目录树item显示不同的图片。

图像重绘

因为每次图像形状不同,都会导致重回,这样会造成资源浪费,所以将重回事件写在MainWindow,只在MainWindow改变时重绘

void MainWindow::resizeEvent(QResizeEvent *event)
{
    auto * pro_pic_show = dynamic_cast<PicShow*>(_picshow);
    pro_pic_show->ReloadPic();
    QMainWindow::resizeEvent(event);
}

picshow实现重新加载逻辑

void PicShow::ReloadPic()
{
   if(_selected_path != ""){
     const auto &width = ui->gridLayout->geometry().width();
     const auto &height = ui->gridLayout->geometry().height();
     _pix_map.load(_selected_path);

     _pix_map = _pix_map.scaled(width,height,Qt::KeepAspectRatio);
     ui->label->setPixmap(_pix_map);
   }
}

qss样式补充

因为目录树双击选中效果,悬浮,以及折叠等效果,我们补充一下qss. 以及按钮需要设置为无边框效果

PicShow {
    border-color: #9F9F9F;
    border-style: solid;
    border-width: 1px 1px 1px 1px;
    padding: 10px;
    background: rgb(64,66,68);
}

QWizard {
    color:rgb(231,231,231);
    background-color:rgb(46,47,48);
}

QWizard QLabel#tips {
    color: red;
}

QTreeView {
    color:rgb(231,231,231);
    background-color:rgb(46,47,48);
    border: 0px;
}

QTreeView::item:hover {
    background: rgb(38, 95, 153);
}

QTreeView::item:selected {
    background: rgb(38, 95, 153);
}

/*设置选中条目字体变粗*/
#TreeProActiveItem {
    font-weight:bold;
}


QTreeView::branch:open:has-children:!has-siblings,
QTreeView::branch:open:has-children:has-siblings
{image: url(:/icon/down.png);}


QTreeView::branch:closed:has-children:!has-siblings,
QTreeView::branch:closed:has-children:has-siblings
{image: url(:/icon/right.png);}

#nextBtn,#closeBtn,#previousBtn  {
    border: 0px;
}

前进后退按钮切换图片

在picshow的构造函数中绑定按钮点击信号

    connect(ui->nextBtn,&QPushButton::clicked,this, &PicShow::SigNextClicked);
    connect(ui->previousBtn,&QPushButton::clicked,this, &PicShow::SigPreClicked);

当按钮点击后左侧目录树的选中条目也进行更新,所以在MainWindow构造函数中

    connect(pro_pic_show, &PicShow::SigPreClicked,pro_tree_widget,&ProTreeWidget::SlotPreShow);
    connect(pro_pic_show, &PicShow::SigNextClicked,pro_tree_widget,&ProTreeWidget::SlotNextShow);
    connect(pro_tree_widget,&ProTreeWidget::SigUpdatePic,pro_pic_show,&PicShow::SlotUpdatePic);

ProTreeWidget的更新逻辑, 设置选中item,并且发送更新图片信号。

void ProTreeWidget::SlotPreShow(){
    if(!_selected_item){
        return;
    }

    auto * curItem = dynamic_cast<ProTreeItem*>(_selected_item)->GetPreItem();
    if(!curItem){
        return;
    }
    emit SigUpdatePic(curItem->GetPath());
    _selected_item = curItem;
    this->setCurrentItem(curItem);
}
void ProTreeWidget::SlotNextShow(){

    if(!_selected_item){
        return;
    }

    auto * curItem = dynamic_cast<ProTreeItem*>(_selected_item)->GetNextItem();
    if(!curItem){
        return;
    }
    emit SigUpdatePic(curItem->GetPath());
    _selected_item = curItem;
    this->setCurrentItem(curItem);
}

SigUpdatePic信号会被PicShow的SlotUpdatePic函数绑定,该函数实现了图片的切换展示。

void PicShow::SlotUpdatePic(const QString &_path)
{
    _selected_path = _path;
    if(_selected_path != ""){
      const auto &width = ui->gridLayout->geometry().width();
      const auto &height = ui->gridLayout->geometry().height();
      _pix_map.load(_selected_path);

      _pix_map = _pix_map.scaled(width,height,Qt::KeepAspectRatio);
      ui->label->setPixmap(_pix_map);
    }
}

关闭和激活项目

前文提到了我们要实现关闭项目的功能,在这里补充一下。ProTreeWidget的构造函数添加

     _action_setstart = new QAction(QIcon(":/icon/core.png"), tr("设置活动项目"),this);
    _action_closepro = new QAction(QIcon(":/icon/close.png"), tr("关闭项目"), this);
     connect(_action_closepro, &QAction::triggered, this, &ProTreeWidget::SlotClosePro);
    connect(_action_setstart, &QAction::triggered, this, &ProTreeWidget::SlotSetActive);

关闭项目的槽函数中现弹出一个删除对话框,这个也是我们添加的设计师界面类,不再赘述了。 删除对话框的布局ui是这样的 https://cdn.llfc.club/1674963688531.jpg 可以勾选同时删除本地文件夹项目文件,这样我们的程序也会把copy的文件删除。 关闭项目逻辑如下

void ProTreeWidget::SlotClosePro()
{
    RemoveProDialog  remove_pro_dialog;
    auto res = remove_pro_dialog.exec();
    bool b_remove = remove_pro_dialog.IsRemoved();
    auto index_right_btn = this->indexOfTopLevelItem(_right_btn_item);
    auto * protreeitem = dynamic_cast<ProTreeItem*>(_right_btn_item);
    auto * selecteditem = dynamic_cast<ProTreeItem*>(_selected_item);

    auto delete_path = protreeitem->GetPath();
     qDebug() << "remove project from path: " << delete_path;
    _set_path.remove(delete_path);
    if(b_remove){ 
       QDir delete_dir(delete_path);
       delete_dir.removeRecursively();
    }

    if(protreeitem == _active_item){
        _active_item = nullptr;
    }

    if(selecteditem && protreeitem == selecteditem->GetRoot()){
        selecteditem = nullptr;
        emit SigClearSelected();
    }

    delete this->takeTopLevelItem(index_right_btn);
    _right_btn_item = nullptr;

}

判断当前选中的item是否在删除的项目中,如果在则发送SigClearSelected信号。 如果是设置活动项目,我们只需要在槽函数里设置字体变粗即可。

void ProTreeWidget::SlotSetActive()
{
    if(!_right_btn_item){
        return;
    }

    QFont nullFont;
    nullFont.setBold(false);
    if(_active_item){
        _active_item->setFont(0,nullFont);
    }

    _active_item = _right_btn_item;
    nullFont.setBold(true);
    _active_item->setFont(0,nullFont);

}

到目前为止我们点击按钮和item等切换图片的功能就实现了,下一篇实现幻灯片放映。

源码链接

源码链接 https://gitee.com/secondtonone1/qt-learning-notes

results matching ""

    No results matching ""