做自己的QT图形应用
参与HELPER2416开发板助学计划
原计划研究一下驱动的,不过后来发现,板子所带的材料,里面已经有比较完善的驱动了,可以做做更难的事,不过掂量一下时间,果断决定,做做QT开发吧。
其实楼主从来没有做过QT的程序,趁这个机会学习一下,这样比较好。
所以打开虚拟机,从头开始,新建一个QT工程。
总得巧立名目建立工程……唉,楼主就喜欢马里奥,而且是现成的逻辑,做个移植吧。
建立工程,并把已有的代码加入到工程里。
接着呢,我的处理是这样的去掉了界面的菜单栏、工具栏和状态栏,这些都是在UI设计器里可以做到的。
于是我的应用程序,只剩下一个空荡荡的窗体了。
然后,基本就全指望代码啦。首先是和界面有关的内容。
我不想显示标题栏,希望我的窗体是全屏效果的,所以需要这些代码:
- ScrWidth = 480;
- ScrHeight = 272;
- this->setFixedSize(ScrWidth,ScrHeight);
- this->setWindowFlags(Qt::FramelessWindowHint);
复制代码
而没有了菜单栏,就需要我们自己去实现一个弹出式的菜单:
- menu = new QMenu(this);
- QAction *act = new QAction("Open...", this);
- connect(act, SIGNAL(activated()) , this, SLOT( on_actionOpen_triggered() ) );
- menu->addAction(act);
- act = new QAction("Exit", this);
- connect(act, SIGNAL(activated()) , this, SLOT( on_actionExit_triggered() ) );
- menu->addAction(act);
复制代码
当点击屏幕的时候,就弹出菜单,需要重载继承来的函数:
- void MainWindow::mouseReleaseEvent(QMouseEvent *event)
- {
- menu->exec(this->mapToGlobal(event->pos()));
- }
复制代码
当然,不要忘记了写菜单处理的函数。
在模拟器的帧扫描期间,要把游戏画面画在屏幕上,这个方法太多了,我找来找去,决定用这样的办法:把游戏画面内容拷贝到内存图片里,然后把图片按比例贴在窗体上:
- extern
- void InfoNES_LoadFrame()
- {
- PIXEL *pfb = (PIXEL*)image->bits();
- for(int i=0;i<224 * 256;i++)
- {
- pfb[i] = WorkFrame[i];
- }
- QCoreApplication::postEvent(m_pWindow, new QEvent(CustomEvent_Login));
- qApp->processEvents();
- }
复制代码
可以看到上面的代码,我把画面数据复制到内存中去了,然后给主窗体发了消息,当主窗体接到消息,就来贴图:
- QPainter p;
- QMatrix pm;
- //设置放大倍数
- pm.scale(480.0/image->width(), 272.0/image->height());
- p.begin(this);
- p.setWorldMatrix(pm);
- p.drawImage(0,0,*image);
- //p.drawPixmap(0, 0, *image);
- p.end();
复制代码
楼主也是后来听说QImage很好用……原来用QPixmap,说实话找了很多手册和说明,没看明白怎么用,各种实验各种失败。
于是这个应用程序还要涉及到自定义事件,重载继承来的函数:
- void MainWindow::customEvent(QEvent *event)
- {
- this->repaint();
- }
复制代码
如果你看到这里,需要补充一句,楼主C++不太擅长,在网上查了好久,找了好多资料,大部分资料只告诉你要重载某某函数,实际写的时候呢,一定要记得在头文件里MainWindow的类定义中也加上重载的函数声明,这句话从来没有人提醒楼主,楼主自己摸索了好久,才反应过来需要这样弄。平时接触到的面向对象也是一门语言一个情况啊,让我一开始就有在头文件里声明的意识……楼主做不到啊……
至于被楼主删掉的原来的菜单如何添加响应,在界面设计器里就可以可视化地实现啦,很简单的。我们这里不用默认的菜单,而弹出菜单也就是直接写上代码就好了。
- void MainWindow::on_actionOpen_triggered()
- {
- QFileDialog dlg(this,"open rom file");
- dlg.resize(ScrWidth, ScrHeight);
- dlg.setAcceptMode(QFileDialog::AcceptOpen);
- dlg.setFileMode(QFileDialog::ExistingFile);
- dlg.setNameFilter("All file(*.*);Nes file(*.nes)");
- dlg.showMaximized();
- delete image;
- image = new QImage(256, 224, QImage::Format_RGB16);
- if(dlg.exec() == QFileDialog::Accepted){
- QString path = dlg.selectedFiles().first();
- //QMessageBox::information(NULL, tr("Path"), tr("You selected ") + path);
- /**/
- if(InfoNES_Load(path.toStdString().data()) == 0)
- {
- running = 1;
- QtConcurrent::run(thread_nes,this);
- }
- /**/
- } else {
- QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files."));
- }
- }
- void MainWindow::on_actionExit_triggered()
- {
- QApplication* app;
- app->quit();
- }
复制代码
从上面的代码也可以看到,由于模拟器的核心是一个死循环,为了界面还能工作,楼主决定使用一个单独的线程来跑模拟器核心。核心线程体的函数如下:
- void thread_nes(QObject *win)
- {
- m_pWindow = win;
- InfoNES_Main();
- }
复制代码
对于在图形界面应用开发的重点内容,应该是都在上面了。剩下的就是关于模拟器内核的有关处理了。详细可以参考我以前发的有关移植InfoNES的文章,就不在这里赘述了。
具体内容还是留一份工程源码吧,由于这只是半成品,所以以上的全部内容,再加上模拟器需要的平台相关的处理,都被写到了MainWindow的原文件中,并且还有许多调试用的痕迹。这些痕迹实在是舍不得删了,有一些甚至反映了我学习的过程,考虑再三保留下来一起打包了。
再来看看关于程序运行。请参考之前一位童鞋的帖子——Helper2416助学: 玩转ARM应用程序开发从QT开始:https://bbs.eeworld.com.cn/thread-442619-1-1.html
一般情况下应该都是可以成功的,InfoNES编译得到的InfoNES可执行程序,我都是复制到根目录下运行,
最后运行起来的话嘛……
哇,花屏了!嘿嘿,因为楼主比较懒嘛,一开始并没有初始化贴在屏幕上的QImage。没事的,尽管触摸屏幕好啦!
这里,在Open的命令响应处理里面,是打开文件的对话框,我们把NES的游戏文件也复制到板上,就可以直接打开啦
比如打开马里奥大叔的游戏……
哦,忘记了,现在还没有实现手柄控制,所以只能看看DEMO了,按照这个比例,扁扁的大叔有点丑
再欣赏欣赏魂斗罗,看起来没有太大问题。
当然了,到目前为止,这个程序还没有达到理想的状态,站在学习的角度来说,一些小问题就不重要了,现在一个很重要的问题,就是游戏过程中对于操作的响应不够及时,如何提高对用户的响应效率,这其实是图形界面程序开发的一个重要问题,楼主还需要继续努力呢。
论坛ID:sjtitr
提交时间:2014.08.24