2012年10月25日星期四

Ez看图重构,变化的开始

      Ez看图最初编写时只有一个大概的框架,是一步步在试验中成长起来的,因此导致内部模块划分不够细,每个类都很复杂。比如负责图片显示的类ImageViewer,连文件切换这些功能都要由它实现,不同功能的代码混在一起,难以理清头绪,维护起来也比较困难,容易忽略某些条件,引入bug。
      于是一直想着要进行重构。刚好国庆几天回家有空,就先进行了大略的改写,开始的思路是把文件管理的部分归到一个独立的类PicManager中,作为ImageViewer类的一个成员,这样可以维持ImageViewer的接口不变,通过简单的Adapter模式调用PicManager的功能。修改起来也很简单,很快就完成了。
       但后来一想这样还是要多维护一些代码,而且也不是很干净,于是再次进行重构。这次把ImageViewer作为基类,就只是简单的图片显示功能,每次调用它的loadImage(Qimage&)函数传进一个图像,它就会显示出来,缩放、翻转等功能也都还是封装在里面,但没有了任何文件相关的功能,彻底干净下来了。PicManager类则公开继承ImageViewer,负责管理文件,实现文件切换等,同时如果有动态图片则定时刷新。这样接口还是原来那样,除了这两个类别的几乎不用改,但却使得功能清晰下来了,代码也容易阅读了许多。
       另外为了更好地封装不同图片格式的区别,我引入了一个新的类ImageCache,里面记录了图片的一些信息。每次PicManager读取图片实际就是获得一个ImageCache对象,然后提取其QImage成员,调用ImageViewer类的接口显示。这个类的引入也使得下一个将要实现的功能——图片预读取——的实现变得容易了。
        总而言之,这次重构把原来的ImageViewer类功能进行了合理的区分,代码也整洁了许多。下一步还要继续重构MainWindow类,让整个程序更简单,更合理。

       体验新版Ez看图可以:点击这里


文档信息



2012年9月13日星期四

Ez看图更新了!

看了一暑假的《Effective C++》、《More Effective C++》,不动动手可不行。今天把今年年初写的EzViewer做了一下重构,去除一些无用的功能,新增了Windows下的文件格式关联,算个2.2版吧。还有很多想法没完成,今后还会慢慢改进的。项目地址:http://code.google.com/p/ezviewer/  ,可以用git check 出源码。  下载体验

下面是介绍:

EZ看图是一个简单的图片浏览器,使用Qt4编写,开源跨平台,国际化支持。 支持jpg, bmp, gif, png, jpeg, ico, svg, pbm, pgm, ppm, tif, tiff, xbm, xpm等多种图片格式。 特点有:
  • 采用统一绘图机制,可以旋转、镜像、缩放、拖动图片,包括动态gif。
  • 图片缩放显示时使用抗锯齿功能。
  • 图片缩放大于窗体时,鼠标左键快速拖动图片,释放鼠标后会依惯性滑动。
  • 可直接从资源管理器拖动一个或多个图片到看图器。
  • 动态gif可以暂停或步进。
  • 可自动隐藏的控制栏。
  • 自动播放/暂停功能。
  • 支持复制图片内容到剪贴板。
  • Windows下支持文件格式关联。 (New!)
  • 双击可以全屏。支持快捷键:
    1. 导航键上下左右、PageUp? / PageDown? 以及J/K:切换图片;
    2. O或N:打开图片对话框;
    3. Q或Esc:关闭图片;
    4. +/-:缩放图片;注意按住Ctrl时可以快速缩放,按住Shift时可以慢速缩放。
    5. 回车键:全屏显示;
    6. I:显示图片信息;
    7. D或Del:删除图片对话框;注意按住Ctrl时不会出现对话框,而是直接删除图片;
    8. Ctrl+C:复制图片内容到剪贴板;
    9. L/R:向左/右旋转90度;
    10. H/V:水平/竖直镜像图片;
    11. S:设置;
    12. 空格键/Pause:gif动画暂停/继续。
    13. F:gif动画暂停/步进。
    14. P:自动播放/暂停功能。
下图是同一张图片放大10倍时,不使用抗锯齿与使用抗锯齿的区别: 




文档信息


2012年9月10日星期一

Linux下时间计算与转换相关的类型和函数

最近忙于学院安排的实习,好久没有写博客了。。贴一下之前整理的Linux时间相关的东西,其中有些是来自C/C++标准库的:


Linux下日历时间相关的类型有:
    time_t               //算术类型
    struct tm           //分散时间结构
    struct timeval    //gettimeofday的结构


跟时间转换和计算相关的函数 (只写函数名,不是完整的原型。注释说明的是来自哪个头文件,以及相应的作用。下同):
    time                  //<time.h> ,  get time_t
    gettimeofday    //<sys/time.h> , get timeval
    localtime           //<time.h> , time_t --> tm
    gmtime             //<time.h> , time_t --> tm
    mktime             //<time.h> , tm --> time_t
    ctime                 //<time.h> , time_t --> string
    astime               //<time.h> , tm --> string
    strftime              //<time.h> , tm --> format string
    wcsftime            //<wchar.h> , tm --> format string
    getdate              //<time.h> , string --> tm
    strptime             //<time.h> , string --> tm

    difftime              //<time.h> , 计算time_t之差
    tzset                   //<time.h> , 设置时区



与程序执行时间相关的类型:
    clock_t               //以CLOCKS_PER_SEC或时钟滴答为单位
    struct timeval
    struct timespec   //<time.h>


测量时间间隔的函数:
    clock                   //<time.h> , 得到执行时间
    times                   //<sys/times.h> , 得到进程和子进程的执行时间


文档信息


2012年8月3日星期五

Qt内存管理:从close()函数说起

(这篇文章原发表于CSDN,CSDN密码泄露事件后就删除了,现重发于这里。)

上次修改QtWin类时(见《学习笔记:windows下qt4程序实现Aero效果及动态切换》),为了使WindowNotifier 类的实例能在程序退出时自动回收内存,在其构造函数中加入了这样几行代码:
setAttribute(Qt::WA_QuitOnClose, false);
connect(qApp, SIGNAL(lastWindowClosed()), this, SLOT(close()));
setAttribute(Qt::WA_DeleteOnClose);
为什么要加入这么几句呢?我们知道,凡是继承自QObject的对象,在其构造函数中一般都有一个parent参数,对象创建时会将自己添加到parent的一个列表中,成为其child。当parent析构时会遍历child列表,将表中对象deleteQWidget继承自QObject自然也有这个参数
指定了parentQWidget对象会成为一个普通窗口部件,状态随parent变化(例如parent隐藏时它也被隐藏)。Parent0QWidget对象自动成为顶级窗口,一般带有标题栏、最小化、最大化和关闭按钮,由窗口管理器管理。当用户点击某个顶级窗口的关闭按钮时,相当于调用了它的close()函数。

      QWidget::close()作用流程如下(实质调用了QWidgetPrivate::close_helper()):
1、 调用QApplication::sendEvent()函数发出QCloseEvent事件,如果被拒绝,则close()函数返回false,窗口保持原样,接下来的几个步骤不会发生。很多软件都能在用户点击关闭按钮时弹出对话框要求确认或取消,就是通过实现虚函数QWidget::closeEvent实现的。
2、 接下来检查是否顶级窗口。如果是顶级窗口,并且是剩下的唯一一个设置了Qt::WA_QuitOnClose属性的顶级窗口时,将会调用QApplicationPrivate::emitLastWindowClosed()。这个函数将会分发一个QEvent::Quit事件,然后发出lastWindowClosed()信号。QEvent::Quit事件将引起qAppquit()被调用,从而退出事件循环。(见QCoreApplication::event(QEvent *e)
3、 接下来检查是否设置了Qt::WA_DeleteOnClose属性,如果为true则将窗体自身delete
4、 如果窗体被析构,将会在析构时遍历其child列表,将child全部delete

    综上可以看出:普通窗口部件在创建时要指定parent,这样就不用手工去回收它的内存了;或者设置Qt::WA_DeleteOnClose属性,这样会在析构时自动回收。 顶级窗口只有当其可见才会在QApplication对象析构时自动调用close()函数,但动态创建的顶级窗口仍需自己回收内存,最好设置其Qt::WA_DeleteOnClose属性。(见QApplication::closeAllWindows()函数) 
      接下来可以回到开头的问题了。QtWin类使用一个动态创建的WindowNotifier 对象作为后台监视器来接收来自窗口管理器的消息,这个对象是不可见的顶级窗口。这就导致了当其它所有可见窗口都由用户关闭时,仍然有窗口(WindowNotifier 对象)未析构,存在内存泄漏的风险。为了能回收顶级窗口的内存,通常的做法是设置其Qt::WA_DeleteOnClose属性,使其在被close()后自动析构。然而这个WindowNotifier 对象是不可见的,自然无法由用户点击关闭按钮来触发其close()函数,因此使用connect(qApp, SIGNAL(lastWindowClosed()), this, SLOT(close()))来使其在顶级窗口都关闭时自动调用close()函数。由于这个WindowNotifier 对象也是顶级窗口,故还要使用setAttribute(Qt::WA_QuitOnClose, false)来取消其Qt::WA_QuitOnClose属性。这样在上面步骤3中就会忽略这个WindowNotifier 对象而直接调用QApplicationPrivate::emitLastWindowClosed(),发出lastWindowClosed()信号了。

文档信息


2012年7月16日星期一

学习笔记:windows下qt4程序实现Aero效果及动态切换


(这篇文章原发表于CSDN,CSDN密码泄露事件后就删除了,现重发于这里,略有修改。)

    一直很喜欢windows 7的Aero效果,很想在自己的程序中用上。但Aero效果是由windows的DWM (Desktop Window Manager)提供的,qt并没有提供调用的方式,自己也不懂windows的编程。好在CuteQt上提供了用qt封装好的版本,可以供我尝试。原帖见<<qt如何在vista和windows7下实现Aero效果>>

    下载了它的源码编译,运行后发现有几个问题:

    1、win7下将目标程序设置为xp兼容模式,运行时依然开启了aero。即没有检测系统版本。

    2、win7未开启aero效果,运行目标程序,再开启aero效果。此时程序不能动态切换显示效果。

    3、窗体若启用了aero效果,WindowNotifier会记录它的指针。将窗体delete后,WindowNotifier依然保留其指针。此时若发生系统aero效果切换事件(如由一个支持aero效果的主题换到一个不支持aero效果的主题),程序将会崩溃。这是因为使用了野指针。

     因此我修改了qtwin.h和qtwin.cpp,修正了上面的几个问题。由于QtWin::enableAeroWindow(QWidget*widget,bool enable =true)这个函数一调用就程序崩溃,自己不懂windows编程也无法修改,于是将其注释掉,并添加了两个新的函数staticboolQtWin::enableAeroEffect(QWidget*widget)和staticvoidQtWin::disableAeroEffect(QWidget*widget)。使用时只需包含qtwin.h头文件,无须判断是否windows系统了。

    具体代码请看Ez拼图源码中的qtwin.h和qtwin.cpp:点击下载



文档信息



2012年7月15日星期日

Qt4小游戏:Ez拼图

      Ez拼图是一个简单的小游戏,使用Qt4编写,开源跨平台。功能很简单:选择图片,然后就可以开始移动图块,直至所有图块都放到正确位置上。
      为了增加趣味性,Ez拼图允许指定图片分割块数,最大支持1200块;为了降低难度,游戏过程中可以随时点击查看原图;允许自定义图片,只要把图片拖到窗体中就行了。游戏虽然简单,常用的功能并没有少。
   


项目托管在Google Code上:    项目页面
由于校园网无法使用git上传源码,只能打包了。源码包下载:点击下载
软件下载(适用于Windows系统):点击下载
Linux下需要下载源码编译。首先安装qt4编译环境(这部分各个系统都不一样,google一下)。然后命令行下进入源码目录,运行下面两条命令即可:
    qmake
    make



TODO:
1、保存设置功能
2、增加关卡,难度随关卡数上升
3、设置分割线颜色



文档信息

2012年7月7日星期六

学习笔记:Windows文件关联及有关注册表项


      不得不说Windows的注册表是世界上最混乱的地方之一,各个软件各自为政,没有统一的接口保证,不同版本的Windows具体实现也不一样。为了实现文件关联功能,花了几天时间,找了不少资料,在这里做个总结。
       Windows文件关联是以文件类型为分组,以后缀名为标志的。每种文件类型可以有一个类型描述,Windows中一般称为ProgId(Programmatic Identifiers)。ProgId指示了该类型文件在资源管理器中显示的图标,以及对应打开的程序路径。相同ProId的文件类型可以视为同一组,具有相关的显示图标和相同的打开方式。后缀名与软件的对应是通过ProgId(Programmatic Identifiers)进行的,一个后缀名只能对应一个ProgId,而对应同一个ProgId的后缀名可视为一组。或者说,一个后缀名只能对应一个软件,一个软件可以对应多个后缀。
        在Windows中,这些关联信息保存在注册表中以下两个位置:
        HKEY_CURRENT_USER\Software\Classes
        HKEY_LOCAL_MACHINE\Software\Classes
        其中HKEY_LOCAL_MACHINE\Software\Classes保存的是本机上所有用户的设置,HKEY_CURRENT_USER\Software\Classes保存的是当前用户的设置,而当前用户的设置优先级比较高。对于开启UAC的Windows7来讲,还有一个区别是更改本机设置需要管理员权限,而更改当前用户的设置只需要普通权限。为了方便读取,Windows下还有个HKEY_CLASS_ROOT根键,里面的内容是上面两个位置下的键值合并后的视图。由于是视图,因此最好只用于读取,若要更改则可以更改HKEY_CURRENT_USER\Software\Classes下的内容。
        以HKEY_CURRENT_USER\Software\Classes为例,要关联一个后缀名例如.ext,则需要在上面所说几个位置下有 .ext 这个项,该项的默认值即为对应的ProgId,假设是 YourProgID。在 .ext的相同层级下还应该有一个名为 YourProgID 的项,里面包含了DefaultIconshell\open\command这两个子项。DefaultIcon子项的值为显示的图标,shell\open\command的值为关联的程序。如下:

       HKEY_LOCAL_MACHINE\Software\Classes
       .ext
             (Default) = YourProgID//可以是任何字符串
       YourProgID //与上面的值对应
             DefaultIcon
                    (Default) = iconToShow, 0
             shell
                   open
                          command
                                  (Default) = yourAppPath.exe %1



       这样,当在资源管理器里点击具有.ext后缀名的文件时,Windows会先在HKCR\Software\Classes和HKLM\Software\Classes下寻找 .ext这一项,找到后读取其默认ProgId,再同样寻找此ProgId,找到其shell\open\command的值后就调用该程序。这个过程是一个映射
       在Windows资源管理器中,用户可以通过右键选择“打开方式”改变默认的关联程序,这个设置的优先级在默认的后缀与ProgId关联的方式之上,保存位置则在HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\下。此位置下同样有许多后缀名,如.ext。在XP下,.ext项下面有ProgidApplication子项,Win7下则是UserChoiceApplication子项,子项的值就是关联的ProgId或者应用程序名称(不包括目录,如yourAppPath.exe)。
       至此Windows文件关联机制就很清楚了。首先根据后缀名查找HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\,找不到时查找HKEY_CURRENT_USER\Software\Classes,最后才是HKEY_LOCAL_MACHINE\Software\Classes。因此检查一个文件是否与某个程序关联可以按照这个顺序检查。
       设置一个后缀名与某个程序的关联则有些不一样,主要的原则是先清除原有的关联,再设置新的关联方式。清除关联包括清除用户自己设置的“打开方式”。设置新的关联方式时,最好只在HKEY_CURRENT_USER\Software\Classes下设置。首先设置后缀名的默认ProgId为自己程序的ProgId,再建立一个ProgId指定显示图标和程序路径。
        Win7下清除后缀名与某个程序的关联时要注意,程序只能删除自己拥有的HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ext\UserChoice,因此要清除用户设置的默认打开方式,需要设置一下权限。这可以通过regini.exe这个自带程序来实现。
        还有一点需要注意的是,在更改了文件关联以后,最好通知一下Windows设置已经改变,否则要下次登录才能看到变化。方法是使用SHChangeNotify函数并指定SHCNE_ASSOCCHANGED事件。
       此外在查找资料的过程中,发现很多人更改文件关联时都是将原先对应的ProgId保存起来,要取消关联时则恢复原先的ProgId对应。但根据MSDN上的文章,最好不要取消后.ext对应的值,由系统自己处理更好。
       具体的编程实现可以参考Editor源码中的fileassoc.h和fileassoc.cpp,使用Qt4完成。

      
参考资料:

2012年7月2日星期一

软件发布:Editor - 一个快速的纯文本编辑器

    一直以来,Windows下的记事本就像鸡肋一样,用之无益,弃之可惜。之所以这样,是因为它功能较少,有时候仅仅想知道一个文本的字数,不得不打开庞大的Word。然而在小巧快速方面它又是出色的,占用资源也不多。于是总想着自己做一个类似的软件,同样小巧快速,但要加上一些常用的功能。
    正好之前做某个软件,里面用到了文本编辑功能。于是有天兴起将文本编辑功能独立出来,又提高了性能,Editor就诞生了。

特点: Editor是一个记事本的增强版本,主要特点就一个字: 快! 在我的电脑上,完全载入一本约一千万字的小说(GBK编码,18.4M),记事本要 12秒 左右,而Editor只要 3秒 !!极致的快就是Editor的目标,这是一款专为纯文本而生的软件。除了基本的记事本功能,Editor还能实时统计总字符数和光标选择范围字符数,简单的字数统计再也不用专门打开庞大的Word了。
Editor目前功能如下:
  1. 纯文本编辑
  2. 支持DOS与UNIX换行风格
  3. 支持多种编码格式,自动探测Utf-8编码(根据文本内容前1K字节),对于非Utf-8编码格式的则以本地编码打开(对于中文Windows系统来说是GBK)
  4. 实时统计总字符数和光标选择范围字符数,并在状态栏上显示
  5. 将文件拖入窗体即可打开
  6. 最近文件列表显示最近打开的5个文件。为保护隐私,关闭程序后列表自动清空
  7. 随意设置字体,自动记住窗体大小和位置,随时设置或取消关联txt文件格式(仅支持Windows系统)

change log:
  • 2011/3:v1.0,基本功能和界面。
  • 2012/5:v1.2,主程序重写,使用QPlainTextEdit代替QTextEdit,提升了性能;自动探测Utf-8编码;文件拖入窗体即可打开;增加搜索功能和字体设置;记住窗体大小和位置;最近文件列表。
  • 2012/6/28-2012/7/2: v1.4,增加Windows下文件关联功能。
TODO:
  • 搜索功能增强;
  • 实现替换功能;
  • 编码探测功能增强

软件是用Qt4编写的,采用GPLv2方式开源,项目托管在Google Code上:    项目页面
软件名是叫Editor,无奈这个项目名已经被人使用,只能使用ez-editor。另外由于校园网无法使用git上传源码,只能打包了。
源码包下载:点击下载
软件下载(适用于Windows系统):点击下载
由于Qt是跨平台的,所以Linux下应该也能用,只要下载源码编译就行了。


文档信息


开博展望

       一直很有冲动,想把学习与生活中遇到的问题与反思都写下来,作为一个总结和记录。2011年在CSDN开了一个博客,写了几篇文章,后来遇到CSDN密码泄露,就将文章都删了,不再使用。如今这股冲动又回来了,于是开了这个博客在Google上。我想Google的优点是可以充分掌控自己的数据,导出导入都比较方便,不怕数据丢失。
       这几天会将之前在CSDN上发的文章重新发在这里。希望有了这个博客,能激励我坚持总结、不断进取,学会更多东西!