Draggable Text例子展示了怎么样拖放文本数据。
本例程序可拖动放置单个Label控件到本程序任意位置。也可以将各个控件拖到其他应用程序中(如office软件),将显示label上的单词,也可以将其他应用程序中文本拖放到其中,他将拆分并显示为一个个单词的label。
项目文件有两个主要类:DragLabel DragWidget
先看main.cpp:
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(draggabletext);
QApplication app(argc, argv);
DragWidget window;
#ifdef Q_OS_SYMBIAN
window.showMaximized();
#else
window.show();
#endif
return app.exec();
}
Main函数里没有什么特别的,就是实例化并显示了DragWidget。
由于DragWidget的实现依赖DragLabel,下面先讲DragLabel。
class DragLabel : public QLabel
{
public:
DragLabel(const QString &text, QWidget *parent);
};
DragLabel定义很简单,继承QLabel,只添加了一个构造函数。QLabel是继承自QFrame。
DragLabel::DragLabel(const QString &text, QWidget *parent)
: QLabel(text, parent)
{
setAutoFillBackground(true); // 自动填充背景,默认是不填充透明的
setFrameShape(QFrame::Panel); // 设置形状:面板
setFrameShadow(QFrame::Raised);// 设置投影类型:下沉
}
下面看DragWidgetl类。
dragwidget.h:
class DragWidget : public QWidget
{
public:
DragWidget(QWidget *parent = 0);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
void mousePressEvent(QMouseEvent *event);
};
Dragwidget重写了dragEnterEvent,dropEvent和mousePressEvent三个事件响应函数。
DragWidget::DragWidget(QWidget *parent)
: QWidget(parent)
{
QFile dictionaryFile(":/dictionary/words.txt"); // 使用资源文件
dictionaryFile.open(QIODevice::ReadOnly); // 以只读形式打开文件
QTextStream inputStream(&dictionaryFile); // 使用QTextStream提供文本写入操作
int x = 5; // x,y 提供移动宽高的变量
int y = 5;
while (!inputStream.atEnd()) {
QString word;
inputStream >> word; // 从文本流中取出一个单词
if (!word.isEmpty()) { // 非空则创建Label
DragLabel *wordLabel = new DragLabel(word, this); // 创建一个label
wordLabel->move(x, y); // 向右向下移动5像素
wordLabel->show(); // 显示
wordLabel->setAttribute(Qt::WA_DeleteOnClose); // 设置关闭时删除
x += wordLabel->width() + 2; // 创建下一个前,将x移动值加当前label的宽+2
if (x >= 245) { // x大于245就换行
x = 5;
y += wordLabel->height() + 2; // 换行要向下移动label高+2
}
}
}
setAcceptDrops(true); // 设置接受放置
setMinimumSize(400, qMax(200, y)); // 设置控件面板的最小大小,qMax宏返回两参数的较大一个
setWindowTitle(tr("Draggable Text")); // 窗口标题
}
这其中QTextStream是文本流,可以被QIODevice、QByteArray和QString操作。它重载了一系列的<<、>>操作符,使用起来很方便。还支持区域填充,对齐和数据格式化。例如:
QFile data("output.txt");
if (data.open(QFile::WriteOnly | QFile::Truncate)) {
QTextStream out(&data);
out << "Result: " << qSetFieldWidth(10) << left << 3.14 << 2.7;
// writes "Result: 3.14 2.7 "
}
还可以进行控制台标准输入输出:
QString line;
QTextStream in(stdin);
in >> line;
QTextStream out(stdout);
out << line << endl;
void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasText()) // hasText()如果是text(MIME type text/plain)就返回true
{
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else {
event->ignore();
}
}
其中QMimeData还有hasUrls hasHtml hasImage hasColor这些都和hasFormat是类似的。满足一定类型信息就返回true。其他的和之前的Draggable Icons Example是一样的,同源设置事件为移动事件,否则设置为接收目标动作。
void DragWidget::mousePressEvent(QMouseEvent *event)
{
QLabel *child = static_cast<QLabel*>(childAt(event->pos())); // 获取为QLabel的子控件
if (!child)
return;
QPoint hotSpot = event->pos() - child->pos(); // 初始化热点,光标点在控件上的相对位置
QMimeData *mimeData = new QMimeData;
mimeData->setText(child->text()); // 设置文本
mimeData->setData("application/x-hotspot",
QByteArray::number(hotSpot.x())
+ " " + QByteArray::number(hotSpot.y())); // 设置信息(存储的热点)
QPixmap pixmap(child->size());
child->render(&pixmap);
QDrag *drag = new QDrag(this); // 设置拖动类
drag->setMimeData(mimeData); // MIME数据
drag->setPixmap(pixmap); // 拖动图片
drag->setHotSpot(hotSpot); // 热点
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
// exec第二个参数本来是默认Qt::MoveAction,这里设置为拷贝
if (dropAction == Qt::MoveAction) // 接收到返回值为移动,则将原来的label关闭
child->close();
}
鼠标按下事件,创建Drag对象,将MIME信息存入Drag类对象。并执行Drag,对返回值进行处理。
void DragWidget::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasText()) {
const QMimeData *mime = event->mimeData(); // 获取MIME类型信息
QStringList pieces = mime->text().split(QRegExp("\\s+"), // 匹配一个或多个"空格",拆分为单个的单词
QString::SkipEmptyParts);
QPoint position = event->pos(); // 获取事件位置
QPoint hotSpot;
QList<QByteArray> hotSpotPos = mime->data("application/x-hotspot").split(' '); // 将数据里的x,y裂分开
if (hotSpotPos.size() == 2) {
hotSpot.setX(hotSpotPos.first().toInt()); // 设置热点x,y
hotSpot.setY(hotSpotPos.last().toInt());
}
foreach (QString piece, pieces) { // 对拖放的拆分的文本遍历并添加为DragLabel
DragLabel *newLabel = new DragLabel(piece, this);
newLabel->move(position - hotSpot);
newLabel->show();
newLabel->setAttribute(Qt::WA_DeleteOnClose);
position += QPoint(newLabel->width(), 0);
}
if (event->source() == this) { // 同源
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else {
event->ignore();
}
foreach (QObject *child, children()) { // 遍历所有继承自QWidget的对象,让其在没有显示的都将其释放掉
if (child->inherits("QWidget")) {
QWidget *widget = static_cast<QWidget *>(child);
if (!widget->isVisible())
widget->deleteLater();
}
}
}
其中split函数式裂分成子项的功能。例如:
QString str = "a,,b,c";
QStringList list1 = str.split(",");
// list1: [ "a", "", "b", "c" ]
QStringList list2 = str.split(",", QString::SkipEmptyParts);
// list2: [ "a", "b", "c" ]
QRegExp是一个用正则表达式的模板匹配类,在以后博客中将会详细讲它。这里“\s+”是“空白符”。
foreach (QObject *child, children()),children()返回DragWidget对象的所有子类的列表,foreach进行遍历,在判断进行删除隐藏的部件。这是个常用的小技巧。