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

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

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

于是,便有了本文。

何处着手?

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

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

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

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

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

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

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

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

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

注意

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

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

难点?

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

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

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

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

源码

分为三个文件

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

shelldemo.h

创建QPlainTextEdit的派生类

  • runCommand() 将输入的命令进行处理,而后输出
  • onEditFinished() 响应输出完时的回车
  • onScrollBarValueChanged() 滚动条变动时,我们要调整QLineEdit的位置
  • resizeEvent() 窗口变化时,我们也要调整QLineEdit的大小和位置
  1. #ifndef SHELLDEMO_H   
  2. #define SHELLDEMO_H   
  3.   
  4. #include <QPlainTextEdit>   
  5.   
  6. class QLineEdit;  
  7. class ShellDemo:public QPlainTextEdit  
  8. {  
  9.     Q_OBJECT  
  10. public:  
  11.     explicit ShellDemo(QWidget *parent=0);  
  12.     virtual QString runCommand(const QString& cmd);  
  13. protected:  
  14.     void resizeEvent(QResizeEvent *e);  
  15. private slots:  
  16.     void onScrollBarValueChanged();  
  17.     void onEditFinished();  
  18. private:  
  19.     void updateEditPosition();  
  20.     QLineEdit * edit;  
  21. };  
  22.   
  23. #endif // SHELLDEMO_H  

shelldemo.cpp

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

  1. #include "shelldemo.h"   
  2. #include <QLineEdit>   
  3. #include <QTextBlock>   
  4. #include <QDebug>   
  5. #include <private/qplaintextedit_p.h>   
  6.   
  7. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)   
  8. #include <private/v8.h>   
  9. #endif   
  10.   
  11. ShellDemo::ShellDemo(QWidget *parent) :  
  12.     QPlainTextEdit(parent)  
  13. {  
  14.     setReadOnly(true);  
  15.   
  16.     QFont font = this->font();  
  17.     font.setPointSize(font.pointSize()+2);  
  18.     this->setFont(font);  
  19.   
  20.     appendPlainText(">>> ");  
  21.     edit = new QLineEdit(this->viewport());  
  22.     edit->setStyleSheet("border-style:none; background-color:transparent;");  
  23.   
  24.     connect(edit, SIGNAL(returnPressed()), SLOT(onEditFinished()));  
  25.     connect(verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(onScrollBarValueChanged()));  
  26. }  
  27.   
  28. void ShellDemo::resizeEvent(QResizeEvent *e)  
  29. {  
  30.     updateEditPosition();  
  31. }  
  32.   
  33. void ShellDemo::onScrollBarValueChanged()  
  34. {  
  35.     updateEditPosition();  
  36. }  
  37.   
  38. QString ShellDemo::runCommand(const QString &cmd)  
  39. {  
  40. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)   
  41.     QString output;  
  42.     v8::HandleScope handle_scope;  
  43.     v8::Persistent<v8::Context> context = v8::Context::New();  
  44.     v8::Context::Scope context_scope(context);  
  45.     v8::Handle<v8::String> source = v8::String::New(cmd.utf16());  
  46.     v8::TryCatch try_catch;  
  47.     v8::Handle<v8::Script> script = v8::Script::Compile(source);  
  48.     if (script.IsEmpty()) {  
  49.         output = QString::fromUtf16(*(v8::String::Value(try_catch.Exception())));  
  50.     } else {  
  51.         v8::Handle<v8::Value> result = script->Run();  
  52.         if (result.IsEmpty()) {  
  53.             output = QString::fromUtf16(*(v8::String::Value(try_catch.Exception())));  
  54.         } else {  
  55.             output = QString::fromUtf16(*(v8::String::Value(result)));  
  56.         }  
  57.     }  
  58.     context.Dispose();  
  59.     return output;  
  60. #else   
  61.     return QString("Result of %1").arg(cmd);  
  62. #endif   
  63. }  
  64.   
  65. void ShellDemo::onEditFinished()  
  66. {  
  67.     QString cmd = edit->text();  
  68.     if (cmd.isEmpty()) {  
  69.         return;  
  70.     }  
  71.     moveCursor(QTextCursor::End);  
  72.     insertPlainText(cmd);  
  73.     edit->hide();  
  74.     edit->clear();  
  75.   
  76.     appendPlainText(runCommand(cmd));  
  77.   
  78.     appendPlainText(">>> ");  
  79.     updateEditPosition();  
  80.     edit->show();  
  81.     edit->setFocus();  
  82. }  
  83.   
  84. void ShellDemo::updateEditPosition()  
  85. {  
  86.     QPlainTextEditPrivate * d = reinterpret_cast<QPlainTextEditPrivate*>(qGetPtrHelper(d_ptr));  
  87.     QRectF rect = d->control->blockBoundingRect(d->control->document()->lastBlock());  
  88.     edit->move(rect.topRight().toPoint());  
  89.     edit->resize(viewport()->size().width(), edit->size().height());  
  90. }  

简单说一下:

  • updateEditPosition()

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

  • ShellDemo() 构造函数

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

  • runCommand()

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

  • onEditFinished()

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

main.cpp

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

  1. #include <QtGui/QtGui>   
  2. #include "shelldemo.h"   
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     QApplication a(argc, argv);  
  7.   
  8.     ShellDemo w;  
  9.     w.setWindowTitle("Dbzhang800\'s Qt5 V8-Shell");  
  10.     w.show();  
  11.   
  12.     return a.exec();  
  13. }  

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

后记

  • 本例子功能非常不完善,仅供参考

相关推荐