QT实现拖拽TabWidget选项卡形成单独的窗口,双击标题栏可还原的功能【dock功能】

近期做QT,我也是新手,这个主要是用重构父类事件实现的,即子类化。

此文章是在http://blog.csdn.net/zmm19861210/article/details/9036779上看到的,但是我做了一定的修改[备注:下方的代码上未体现,还是老代码],我做了析构函数的修改,就是将堆上那些new出来的私有变量给delete掉,由于代码是在主机上调试的,公司使用远程桌面方式上网,所以没有权限上传到远程桌面来整个demo真是遗憾。

这个功能看似简单,实现起来确实有点儿难度。
在代码里详细说明吧。
//CTabWidget.h
#ifndef CTABWIDGET_H
#define CTABWIDGET_H
#include <QTabWidget>
#include <QtGui>
#include "CTabBar.h"

class CTabWidget :public QTabWidget
{
    Q_OBJECT
public:
    CTabWidget(QWidget* =0);
	virtual ~CTabWidget(){};
public:
	CTabBar *tabBar;
};
#endif // CTABWIDGET_H

//CTabWidget.cpp
#include "CTabWidget.h"

CTabWidget::CTabWidget(QWidget *parent):QTabWidget(parent)
{
	tabBar = new CTabBar;
	setTabBar(tabBar);     //这里是关键,这样用我们自定义的CTabBar替换原来的QTabBar
}

//CTabBar.h
#ifndef CTABBAR_H
#define CTABBAR_H
#include <QTabBar>
#include <QtGui>

class CTabBar :public QTabBar
{
	Q_OBJECT
public:
	CTabBar(QWidget* =0);
	virtual ~CTabBar(){};
protected:
	void mousePressEvent(QMouseEvent *);
	void mouseReleaseEvent(QMouseEvent *);   //通过两个事件模拟出tab被拖动的动作
private:
	bool pressFlag;
signals:
	void sig_tabDrag(int,QPoint);
};
#endif

//CTabBar.cpp
#include "CTabBar.h"
#include <QtGui>
 
CTabBar::CTabBar(QWidget *parent):QTabBar(parent),pressFlag(false)
{
 
}
 
void CTabBar::mousePressEvent(QMouseEvent *event)
{    
    if (event->button()==Qt::LeftButton)
    {
        pressFlag = true;
    }
    QTabBar::mousePressEvent(event);
}
 
void CTabBar::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton &&pressFlag )
    {
        pressFlag = false;     
        if(tabRect(currentIndex()).contains( event->pos()))
            return;
        emit sig_tabDrag(currentIndex(),event->pos());
    }    
}

现在实现窗口标题栏被双击动作
//CWidget.h
#ifndef CWIDGET_H
#define CWIDGET_H
#include <QtGui/QWidget>

class CWidget:public QWidget
{
	Q_OBJECT
public:
	CWidget(QWidget* = 0);
	~CWidget();
protected:
	bool event(QEvent *);
signals:
	void sig_doubleClickedTitleBar();  //被双击时发射的信号
};
#endif

//CWidget.cpp
#include "CWidget.h"
#include <QtGui>
CWidget::CWidget(QWidget *parent):QWidget(parent)
{

}

CWidget::~CWidget()
{

}

bool CWidget::event(QEvent *event)
{
#ifdef unix
    if(event->type() == QEvent::MouseButtonDblClick){   //标题栏单击没反应,就单击窗口边缘替代吧
        emit sig_doubleClickedTitleBar();
        return true;
    }
#endif   
	if (event->type() == QEvent::NonClientAreaMouseButtonDblClick)  //这个事件在Linux下没有触发,应该算是Qt的一个Bug吧
    {
		emit sig_doubleClickedTitleBar();
		return true;
	}
    return QWidget::event(event);
}

//CMainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
#include "CTabWidget.h"
#include "CTabBar.h"

class CMainWindow:public QMainWindow
{
	Q_OBJECT
public:
	CMainWindow(QWidget* = 0);  
private:
	CTabWidget *tabWidget;
private slots:
    void slot_tabBarDoubleClicked();            //响应双击弹回的槽函数
    void slot_tabDrag(int index,QPoint point);  //响应拖动动作的槽函数
    void slot_closeTab(int);                    //关闭tab的槽函数
};

#endif // MAINWINDOW_H

//CMainWindow.cpp
#include "CMainWindow.h"
#include "CTabWidget.h"
#include "CWidget.h"
#include <QtGui>

CMainWindow::CMainWindow(QWidget *parent):QMainWindow(parent)
{
    tabWidget = new CTabWidget(this);
    tabWidget->setMovable(true);
    tabWidget->setTabsClosable(true);
    tabWidget->setTabShape(QTabWidget::Triangular);
    //添加4个tab页
    tabWidget->addTab(new QTextEdit,"eidt 1");
    tabWidget->addTab(new QTextEdit,"eidt 2");
    tabWidget->addTab(new QTextEdit,"eidt 3");
    tabWidget->addTab(new QTextEdit,"eidt 4");
    setCentralWidget(tabWidget);

    connect(tabWidget->tabBar,SIGNAL(sig_tabDrag(int,QPoint)),this,SLOT(slot_tabDrag(int,QPoint)));
    connect(tabWidget,SIGNAL(tabCloseRequested(int)),this,SLOT(slot_closeTab(int)));
    connect(tabWidget,SIGNAL(currentChanged(int)),tabWidget,SLOT(setCurrentIndex(int)));

    resize(800,600);
}

void CMainWindow::slot_tabDrag(int index,QPoint point)
{
    CWidget *widget = new CWidget;
    QWidget *draged = tabWidget->widget(index);
    QString windowName = tabWidget->tabText(index);
    tabWidget->removeTab(index);
    connect(widget,SIGNAL(sig_doubleClickedTitleBar()),this,SLOT(slot_tabBarDoubleClicked()));

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(draged);
    widget->setLayout(layout);
    widget->resize(600,400);
    widget->move(point+pos()+tabWidget->pos());
    widget->setWindowTitle(windowName);
    widget->show();
    draged->show();
}

void CMainWindow::slot_tabBarDoubleClicked()
{
    CWidget *widget = qobject_cast<CWidget*>(sender());
    QObjectList list = widget->children();
    QTextEdit *edit = NULL;
    
    for(int i = 0;i<list.count();++i)
    {
        if(list[i]->inherits("QTextEdit"))
        {
            edit = qobject_cast<QTextEdit*>(list[i]);
            break;
        }
    }
    if(edit == NULL)
    {
        return ;
    }

    edit->setParent(tabWidget);
    tabWidget->addTab(edit,widget->windowTitle());
    delete widget;
}

void CMainWindow::slot_closeTab(int index)
{
    QWidget *draged = tabWidget->widget(index);
    tabWidget->removeTab(index);
    delete draged;
}

//main.cpp
#include "CMainWindow.h"
#include <QApplication>
#include <QCleanlooksStyle>

int main(int argc,char **argv)
{
    QApplication app(argc,argv);
    QApplication::setStyle(new QCleanlooksStyle);
    QTextCodec::setCodecForTr(QTextCodec::codecForName("gb2312"));
    CMainWindow mainwindow;
	mainwindow.show();
    return app.exec();
}

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用QTabBar的setMovable()函数将TabWidget设置为可拖拽,然后在TabBar上设置鼠标事件,当鼠标按下时,记录下当前Tab的位置和大小,创建一个新的窗口并将Tab上的控件移动到新窗口中,同时设置新窗口的标题为当前Tab的标题。当双击窗口标题栏时,将窗口中的控件移动回原来的Tab中,同时关闭该窗口。以下是示例代码: ```cpp void MyTabWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_dragStartPosition = event->pos(); } } void MyTabWidget::mouseMoveEvent(QMouseEvent *event) { if (!(event->buttons() & Qt::LeftButton)) return; if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance()) return; int index = currentIndex(); if (index < 0) return; QWidget *widget = widget(index); if (!widget) return; QMimeData *mimeData = new QMimeData; QByteArray widgetData; QDataStream dataStream(&widgetData, QIODevice::WriteOnly); dataStream << QPoint(event->pos() - widget->pos()); dataStream << widget->size(); mimeData->setData("application/x-dnditemdata", widgetData); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(*widget->grab()); drag->setHotSpot(event->pos() - widget->pos()); if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) { widget->close(); } } void MyTabWidget::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("application/x-dnditemdata")) { if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } void MyTabWidget::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("application/x-dnditemdata")) { if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } void MyTabWidget::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat("application/x-dnditemdata")) { QByteArray widgetData = event->mimeData()->data("application/x-dnditemdata"); QDataStream dataStream(&widgetData, QIODevice::ReadOnly); QPoint offset; QSize size; dataStream >> offset >> size; QWidget *widget = new QWidget(this); widget->setGeometry(QRect(event->pos() - offset, size)); widget->show(); event->setDropAction(Qt::MoveAction); event->accept(); } else { event->ignore(); } } void MyTabWidget::tabCloseRequested(int index) { QWidget *widget = this->widget(index); if (widget) { widget->close(); } } void MyTabWidget::tabDoubleClick(int index) { QWidget *widget = this->widget(index); if (widget) { widget->setParent(this); widget->show(); setCurrentIndex(index); } } ``` 其中,MyTabWidget继承自QTabWidget,重载了鼠标事件和Tab关闭事件。在鼠标事件中,记录下当前Tab的位置和大小,并创建一个新的窗口并将Tab上的控件移动到新窗口中。在Tab关闭事件中,关闭窗口并将控件移动回原来的Tab中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值