用Qt实现一个简单的shell (Qt5+V8)

17 篇文章 9 订阅

在CSDN论坛看到有网友问用Qt如何实现一个类似shell的东西。

同时呢,前两天V8已经成为Qt5的基础模块了,刚好,可以做个简单的javascript的shell试试看(只支持单行输入)。

于是,便有了本文。

何处着手?

先用google搜索一下,未找到自己想要的答案(当然,有些比较靠谱的答案:比如去看现有shell(像konsole等)的源码,只是自己懒,不想看)。

初看起来,似乎是个比较简单的东西。

  • 用一个只读的控件来显示输入和输出
  • 用一个输入控件来接受输入

按照这个思路,加上对shell的期望:

  • 选择只读的 QPlainTextEdit 作为主控件
  • 用一个 QLineEdit 接受输入

于是:就是下面的效果了,



注意

  • 前两个图,是在Qt5下的结果。(你可以访问V8 初次接触(Qt5)来了解Qt5和V8的关系)

  • 第三个图,是Qt4下编译后的结果(没有V8的参与)。

难点?

在贴出源码之前,先提一点这个:

  • 每次等待输入时,都要有提示符,比如">>> "

  • 我们只需要将QLineEdit定位到">>>"这个位置即可

但是,获取这个位置有些困难,为此,动用了 QPlainTextEditPrivate 这个私有类!

源码

分为三个文件

  • shelldemo.h
  • shelldemo.cpp
  • main.cpp

shelldemo.h

创建QPlainTextEdit的派生类

  • runCommand() 将输入的命令进行处理,而后输出
  • onEditFinished() 响应输出完时的回车
  • onScrollBarValueChanged() 滚动条变动时,我们要调整QLineEdit的位置
  • resizeEvent() 窗口变化时,我们也要调整QLineEdit的大小和位置

#ifndef SHELLDEMO_H
#define SHELLDEMO_H

#include <QPlainTextEdit>

class QLineEdit;
class ShellDemo:public QPlainTextEdit
{
    Q_OBJECT
public:
    explicit ShellDemo(QWidget *parent=0);
    virtual QString runCommand(const QString& cmd);
protected:
    void resizeEvent(QResizeEvent *e);
private slots:
    void onScrollBarValueChanged();
    void onEditFinished();
private:
    void updateEditPosition();
    QLineEdit * edit;
};

#endif // SHELLDEMO_H

shelldemo.cpp

这个是重点了,你可以忽略其中关于Qt5和V8的代码(已经被宏保住了,不会影响你的编译)。

#include "shelldemo.h"
#include <QLineEdit>
#include <QTextBlock>
#include <QDebug>
#include <private/qplaintextedit_p.h>

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <private/v8.h>
#endif

ShellDemo::ShellDemo(QWidget *parent) :
    QPlainTextEdit(parent)
{
    setReadOnly(true);

    QFont font = this->font();
    font.setPointSize(font.pointSize()+2);
    this->setFont(font);

    appendPlainText(">>> ");
    edit = new QLineEdit(this->viewport());
    edit->setStyleSheet("border-style:none; background-color:transparent;");

    connect(edit, SIGNAL(returnPressed()), SLOT(onEditFinished()));
    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(onScrollBarValueChanged()));
}

void ShellDemo::resizeEvent(QResizeEvent *e)
{
    updateEditPosition();
}

void ShellDemo::onScrollBarValueChanged()
{
    updateEditPosition();
}

QString ShellDemo::runCommand(const QString &cmd)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QString output;
    v8::HandleScope handle_scope;
    v8::Persistent<v8::Context> context = v8::Context::New();
    v8::Context::Scope context_scope(context);
    v8::Handle<v8::String> source = v8::String::New(cmd.utf16());
    v8::TryCatch try_catch;
    v8::Handle<v8::Script> script = v8::Script::Compile(source);
    if (script.IsEmpty()) {
        output = QString::fromUtf16(*(v8::String::Value(try_catch.Exception())));
    } else {
        v8::Handle<v8::Value> result = script->Run();
        if (result.IsEmpty()) {
            output = QString::fromUtf16(*(v8::String::Value(try_catch.Exception())));
        } else {
            output = QString::fromUtf16(*(v8::String::Value(result)));
        }
    }
    context.Dispose();
    return output;
#else
    return QString("Result of %1").arg(cmd);
#endif
}

void ShellDemo::onEditFinished()
{
    QString cmd = edit->text();
    if (cmd.isEmpty()) {
        return;
    }
    moveCursor(QTextCursor::End);
    insertPlainText(cmd);
    edit->hide();
    edit->clear();

    appendPlainText(runCommand(cmd));

    appendPlainText(">>> ");
    updateEditPosition();
    edit->show();
    edit->setFocus();
}

void ShellDemo::updateEditPosition()
{
    QPlainTextEditPrivate * d = reinterpret_cast<QPlainTextEditPrivate*>(qGetPtrHelper(d_ptr));
    QRectF rect = d->control->blockBoundingRect(d->control->document()->lastBlock());
    edit->move(rect.topRight().toPoint());
    edit->resize(viewport()->size().width(), edit->size().height());
}

简单说一下:

  • updateEditPosition()

用来更新QLineEdit的位置(这里面的代码?你凑活看吧,其实代码还有些问题)。

  • ShellDemo() 构造函数

基本没做什么。上面设置了大两个点的字体,纯粹是为了舒服一点(不然在我机子上比较难看)。QLineEdit隐藏掉边框,以便和主控件融为一体。

  • runCommand()

根据输入,产生什么输出,你说了算。我只是为了学习下V8,胡乱加了点代码。

  • onEditFinished()

将输入输出显示到主控件中。

main.cpp

很常规的文件,不用多说。

#include <QtGui/QtGui>
#include "shelldemo.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    ShellDemo w;
    w.setWindowTitle("Dbzhang800\'s Qt5 V8-Shell");
    w.show();

    return a.exec();
}

qt5v8.pro

  • .pro 文件很简单,对于Qt5,我们使用了v8模块

contains(QT_VERSION, ^5\\..*\\..*) {
    QT  += v8-private gui-private core-private
}
SOURCES += main.cpp \
    shelldemo.cpp
HEADERS += \
    shelldemo.h \
    shellodemo_p.h

后记

  • 本例子功能非常不完善,仅供参考
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值