LCFinder 0.1.0 开发日志
2016年02月23日 · 16875 字 · 阅读
2017-04-19
LiteDB 支持 UWP,然而是 C# 写的,看上去在 C 或 C++ 里调用它会比较麻烦。
LevelDB 是 C++ 写的,支持 UWP,而且有提供 C 接口,可以在下个版本中将缓存数据库从 UnqLite 迁移至 LevelDB。
之前提到的 yaml 问题是因为链接的库不同而导致的,Debug 版的 yaml 库和 Debug 版的 exe 应用程序链接的是 msvcrtd 库,也就是 Debug 版的 msvcrt 库,这时应用程序能够正常工作。而当 exe 切换编译成 Release 版后,msvcrt 库的 fopen() 函数返回的文件指针不能给 msvcrtd 库的文件操作函数用,所以 yaml 读取文件内容时就会报错。
编译了一个能在 Windows XP 里面跑的版本,然而界面内容并没有正常显示。
2017-04-16
sqlite、libxml2、libpng、jpeg、freetype 都可以编译成 UWP 版本,然而只有 unqlite 无法编译成 UWP 版本,像 CreateFileW、LockFile、MoveFileW、GetVersionExW、GetProcessId 这些函数在 UWP 中并不提供,看来需要考虑其它替代品。
如果 dll 依赖的其它 dll 不存在的话,运行 exe 会报错:
应用程序无法正常启动(0xc000007b)。请单击“确定”关闭应用程序。
可以用 dependencywalker 查看 exe 的依赖库列表,对比检查 exe 当前目录下有哪些 dll 不存在。
2017-04-15
测试运行 UWP 应用时总是报错:
程序“[26436] UWP.exe”已退出,返回值为 -1073741515 (0xc0000135) '未找到依赖 dll'。
不知道是不是 Debug 版的配置问题,应用程序和依赖库全部编译成 Release 版本才能正常运行,其中要是有一个是 Debug 版本的话运行时直接报错“未找到依赖 dll”,这错误信息太精简了,好歹也给出是哪个 dll 未找到啊,为折腾这个问题依赖库都重新编译了好几次,太浪费时间。
2017-04-11
用 Windows 应用认证工具测试,结果测试不通过,依赖的接口不属于 Windows SDK,看来 UWP 版是没法发布了。
或许用 C++ 基于 Windows SDK 接口写个简化版的 C 标准库可以解决这个问题,但是 libxml2 和 unqlite 库除了标准库还依赖操作系统额外提供的接口,这就麻烦了。
2017-04-09
2017-04-03
打开几十MB的图片一直显示“正在载入”感觉不太好,于是就添加了进度条显示。
为此调整了 LCUI 的图片读取接口,让它能够支持设置一个回调函数,在每读取一行像素后调用该函数。在调试 png 图片的读取进度时发现进度条增长速度很奇怪,刚开始几秒都没有变化,查看源代码后发现 png_read_png()
会读取图片的全部信息和像素数据,而 png_get_rows()
仅仅是取出当前已经读到的像素数据。花了些时间参考 png_read_png()
函数及相关的源代码,改用 png_read_info()
读取图片信息,用 png_read_row()
逐行读取像素。之前都是手动反转像素数据,其实可以调用 png_set_bgr()
函数让 libpng 自己处理。
2017-03-31
内存访问越界的问题处理起来蛮麻烦的,只要有一处越界就会影响到其它地方的代码,可惜 valgrind 不支持 Windows,难道要优先把 linux 支持完成后再在 linux 下用 valgrind 测试?这样下去的话 Beta 1 版本又要推迟发布了。
经过一番搜索后找到了 Dr. Memory 这样的内存调试工具,支持 Windows、Linux、Mac 和 Android,用起来还可以,已经解决了几个内存访问越界问题。
2017-03-22
缩略图数据库自程序启动后一直处于打开状态,直到程序退出时才关闭,这时所有的变更才会写入到数据库中,如果程序未正常退出则会使数据库在下次打开时被清空,应该在每次写入缩略图数据后手动调用 unqlite_commit()
提交数据变更。
2017-03-21
App::OnWindowClosed()
并不会在窗口被关闭时调用,用 VisualStudio 的 DirectX11 示例程序测试也是这种问题,那这块代码是用来干嘛的?
2017-03-04
准备添加文件关联功能。在调用 windows 自带的“照片”应用打开图片文件时,它会扫描该文件所处文件夹内的其它图片文件,LCFinder 也需要这样的功能,然而文档不好找,于是又发了个帖子:[UWP][C++] How to access files like Photos App?,得到的答案是:添加 App::OnFileActivated()
函数响应文件激活事件,事件参数中有个 NeighboringFilesQuery
成员,可以调用它的 GetFilesAsync()
方法获取其它文件。
2017-03-01
缩略图缓存数据库在打开后居然会清空。
2017-02-27
针对文件流操作异常问题发了个帖子:[UWP][C++] Using IRandomAccessStream^ will throws ObjectDisposedException,得到的解决方法是在 DataReader 用完后调用 DetachStream() 方法不让文件流关闭。
2017-02-26
LCUI 的图像读取接口已经整理完成,参考官方文档和示例代码为 LCFinder 添加了 UWP 版的图片文件读取功能,然而运行时会抛出异常:
0x76C2A832 处(位于 UWP.exe 中)有未经处理的异常: Microsoft C++ 异常: Platform::ObjectDisposedException ^,位于内存位置 0x0C67F250 处。 HRESULT:0x80000013 该对象已关闭。
WinRT 信息: 该对象已关闭。
堆栈跟踪:
[外部代码]
UWP.exe!ImageFileStream::Read(unsigned char * buffer, unsigned int size) 行 621
UWP.exe!FileOnRead(void * data, void * buffer, unsigned int size) 行 656
LCUI_D.dll!PNGReader_OnRead(png_struct_def * png_ptr, unsigned char * buffer, unsigned int size) 行 80
LCUI_D.dll!png_read_sig(png_struct_def * png_ptr, png_info_def * info_ptr) 行 134
LCUI_D.dll!png_read_info(png_struct_def * png_ptr, png_info_def * info_ptr) 行 104
LCUI_D.dll!png_read_png(png_struct_def * png_ptr, png_info_def * info_ptr, int transforms, void * params) 行 1034
LCUI_D.dll!LCUI_ReadPNGHeader(LCUI_ImageReaderRec_ * reader) 行 142
LCUI_D.dll!LCUI_InitImageReaderByType(LCUI_ImageReaderRec_ * reader, int type) 行 109
LCUI_D.dll!LCUI_InitImageReader(LCUI_ImageReaderRec_ * reader) 行 117
UWP.exe!FileService_ReadImage(ConnectionRec_ * conn, FileRequestParams_ * params, FileStreamChunk_ * chunk, LCUI_ImageReaderRec_ * reader) 行 687
UWP.exe!FileService_GetFile::__l2::<lambda>(Concurrency::task<Windows::Storage::Streams::IRandomAccessStream ^> t) 行 784
[外部代码]
文件流操作都是在同一个函数里进行的,怎么这些文件操作被封装成 C 风格函数后就不能正常使用了?
2017-02-12
PNG 的读取接口已经修改,参考文档:Reading PNG Images from Memory,libpng 函数库提供了 png_set_read_fn()
函数用于自定义数据读取函数,函数原型需要是 void xxxx(png_structp png_ptr, png_bytep buffer, png_size_t size)
,它和 fread()
类似,在调用 libpng 相关接口读取数据时它就会被调用。
png 这个还算简单,而 jpeg 就有些复杂了,它的头文件中有 jpeg_mem_src()
函数声明,看样子要把整个图像文件读到内存中才能解码 jpeg 图像,这不是优先考虑的方法,于是又找了其它方法,比如这个:How to use libjpeg to read a JPEG from a std::istream?,需要提供的函数比 libpng 多了三个,其中 fill_buffer()
的功能和 png_set_read_fn()
设置的读取函数类似,都是读取一段数据到缓存中。具体使用方法等以后再研究。
至于 bmp,完全需要自己写接口了。综上所述,目前添加的图像读取接口还需要做些调整。
2017-02-02
扫描文件列表时,会随机出现几个文件的名称无效的情况,不知是什么原因。
2017-01-21
缩略图可以尝试直接调用 UWP C++ 接口来获取,但需要先知道 StorageItemThumbnail::ReadAsync()
读到的数据是什么格式的,是压缩过的还是原始 RGB 数据?
2017-01-19
刚接触 concurrency::create_task()
时有些纠结如何让任务连续起来,如何拼接新任务,会遇到语法问题:
error C2338: incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)
也会遇到异常:
HRESULT: 0x80070002 系统找不到指定的文件。
WinRT 信息: 在指定指定的路径(F:\XXXXXX)中找不到某个项目。
WinRT originate error
为解决这些问题,发了个帖子:[UWP][C++]How to catch exception of StorageFile::GetFileFromPathAsync() ?
。其实折腾一段时间后再看这些代码也蛮容易理解的,在使用 .then()
方法连接任务的时候需要注意任务的返回值类型和参数类型,还要考虑到变量的生存周期。顺便贴一段获取文件信息的示例代码。
static void TestGetFile( const wchar_t *wpath )
{
String ^path = ref new String( wpath );
create_task( StorageFile::GetFileFromPathAsync( path ) ).then( []( task<StorageFile^> fileTask ) {
StorageFile^ file = nullptr;
try {
file = fileTask.get();
} catch( Exception^ e ) {
}
return file;
} ).then( [](StorageFile^ file) {
if( !file ) {
return create_task( []() {
return -ENOENT;
} );
}
// TODO: Put code to handle the file when it is opened successfully.
auto t = create_task( file->GetBasicPropertiesAsync() ).then( []( FileProperties::BasicProperties ^props ) {
// TODO: Put code to handle the properties when it is get successfully.
LOG( "mtime: %llu\n", props->DateModified.UniversalTime );
return 0;
} );
if( true/* this file is image */ ) {
t = t.then( [file](int ret) {
return file->Properties->GetImagePropertiesAsync();
} ).then( [file]( task<FileProperties::ImageProperties^> t ) {
FileProperties::ImageProperties ^props;
try {
props = t.get();
LOG( "image size: %d,%d\n", props->Width, props->Height );
} catch( Exception^ e ) {
}
// TODO: Put code to handle the properties when it is get successfully.
return 0;
} );
}
return t;
} ).then( []( int ret ) {
LOG( "return: %d\n", ret );
} );
}
2017-01-17
为减少工作量,这个版本就不弄单独的进程跑文件服务了。UWP 的应用服务够折腾的,官方有个 C++ 版的应用服务例子,然而需要 Visual Studio 2017 RC,由于懒得装 RC 版本就放弃了。而 C# 版的例子也无法生成成功,编译过程中还有警告。
2017-01-15
concurrency::create_task()
创建的任务是在另一个线程里执行的,如果其中有个任务一直未执行完毕,那么任务队列里的其它任务也会卡住。在任务里面调用 concurrency::create_task()
创建新任务,然后调用 wait() 方法等待这个新任务执行完毕,会抛出异常并输出一段错误信息:
0x7745DB18 处(位于 UWP.exe 中)引发的异常: Microsoft C++ 异常: Concurrency::invalid_operation,位于内存位置 0x03C9CE88 处。
2017-01-14
经测试,在调用 StorageApplicationPermissions.FutureAccessList->Add()
将文件夹添加至访问列表后,可以调用 Windows::Storage::StorageFile::GetFileFromPathAsync()
访问该文件夹里面的任意文件,而在添加后返回的 token 在访问文件时不是必须的,仅在操作 FutureAccessList 时需要用到 token,例如调用 FutureAccessList->Remove()
删除记录。
2017-01-10
在 C++ 里好像不能用 LaunchFullTrustProcessForCurrentAppAsync()
函数,找不到函数原型,看来得考虑实现图片的分段载入功能了。
2017-01-08
开始实现 UWP 版的文件服务,目前需要解决的问题有:
- 同一连接发送的数个请求,服务端是同步队列化处理的,还是异步并行处理的?
- 如果是同步队列化处理的,是否可以创建多个连接?
- 图像数据该怎么传输?
2017-01-03
现在只剩下文件扫描功能需要修改成异步模式,考虑到数据写入速度可能会比读取速度快,可能需要限制写缓存的最大容量,当达到限制后,再写入数据时会进入阻塞状态,直到缓存大小低于限制为止。
2017-01-01
之前是想让服务端只负责传输文件内容,不解码图片内容,让客户端一边接受数据一遍解码,但感觉这样做会增加工作量,目前 LCUI 只有一次性载入图片并解码内容的接口,要实现分段解码功能还得参考相关文档,然后添加接口,很麻烦。让服务端解码图片内容看上去传输数据量比较大,但真正需要打开的图片也就那么几张,也没多大影响。
2016-12-30
文件服务端先暂时用 C 标准库的文件操作函数,以后再基于 UWP C++ 接口实现一个 UWP 版的文件服务端,这样服务端进程就不用以 FullTrust 模式运行了。
2016-12-22
开始调整文件操作代码,需要添加一个文件服务端,由它来执行所有的文件操作,而 LCFinder 这边则作为客户端,要操作文件都得先向服务端发送请求,然后异步处理返回结果。目前这个服务端是针对本地的文件系统,以后如果要添加支持访问云盘、FTP 等远程服务器里的文件的话,基于现有的模式或许会比较容易实现。
在 UWP 版中,文件服务端是跑在一个单独进程上,借助 UWP API 实现进程间的数据传输。 而普通版的则将服务端作为一个线程来运行,模拟实现文件的异步操作即可,难度不大。
现在比较纠结的是接口设计,需要参考一些用 socket 实现的 Client/Server 例子。
2016-12-16
2016-12-12
2016-12-11
添加了密码验证对话框,错误提示的样式参考自 Dribbble,接下来是密码设置对话框。
2016-12-08
针对文件操作权限这一问题,我在 MSDN 论坛上发了个帖子:[UWP][C++][Desktop Bridge]How to get filesystem full access permission?,主要是想知道除了自己封装文件操作接口外还有没有其他解决方案。最后等到的答复是:可以将文件操作代码抽离出来,做成 win32 服务程序,让它作为完全信任的进程来启动,然后 UWP 应用这边就封装好与服务端的通信接口,读取文件、扫描文件等需要权限的操作都可以作为指令发送给服务端,发完后异步等待返回的结果并处理就可以了。
2016-12-06
在 UWP 应用里操作文件真麻烦,各种异步接口,用 C 标准库的文件操作接口访问其它位置的文件直接报错,错误信息是“拒绝访问”,就不能添加一项“访问所有文件”的权限吗?或者提供个接口,记录有访问权限的文件/文件夹,只要是该文件夹内的文件都允许应用访问。
看样子需要封装 UWP 的文件操作接口,感觉工作量有点大,UWP 版还是先暂停,等把基本功能做完后再继续。
2016-12-05
UWP 应用没有权限访问其它位置的文件,如果要获得权限则需要调用 FileOpenPicker/FolderPicker 显示文件选取器,让用户指定哪些文件允许应用访问。在用户选择后,需要将文件添加至 StorageApplicationPermissions.FutureAccessList 中以获得访问权限。添加后会返回一个文件访问凭据(token),在以后可以凭借它调用 GetFileAsync() 方法获取与该凭据对应的文件。
2016-12-01
现有的文件夹操作函数不能用,微软貌似只提供了 GetFilesAsync()
这种异步文件扫描接口,配合 concurrency::create_task
创建异步文件扫描任务,看来需要修改 LCFinder 现有的文件扫描功能。
2016-11-29
在 LCFinder 的代码里调用 free() 释放 LCUI 库创建的链表结点,居然会抛异常。难道是因为 LCUI 和 LCFinder 链接的标准库不一致?VS2012 标准库 malloc() 分配的内存不能用 VS2015 标准库里的 free() 释放掉?经过测试后发现的确是这个的原因,看来还需要重新编译 VS2015 版本的依赖库。
2016-11-27
UWP 版的图形输出驱动已经完成,但在调整窗口大小时会有明显的延迟现象,而且会有白色背景闪烁,在下次重绘完成之前,当前的窗口内容也会被拉伸。
这个 yaml 库好像有问题,用 VS2012 工具链编译后在 UWP 应用中使用它会出现莫名其妙的异常,而将它重新用 VS2015 编译后又能正常运作。当换成普通 win32 应用后调用它又会出现异常。之前还遇到过一个问题,release 版用的没问题,debug 版却会出现异常。
2016-11-26
现在需要解决的问题如下:
- 如何让 VisualStudio 在构建 appx 包时将自己的资源文件打包进去?
- 如何打开命令行窗口查看应用在运行时打印的调试信息?
Google 搜索了一番,微软文档也找过一些,都找不到有用的信息,最后只好在 MSDN 论坛发帖提问:
几个小时后得到了解决方案:
- 将程序需要的文件复制到项目的目录内(可以新建一个文件夹来存放),然后在解决方案资源管理器中将这些文件添加进来,之后选中这些文件,右键-》属性-》常规-》将“内容”这一配置项的值改为“是”。在构建 appx 包时会保留这些文件的相对路径并打包至 appx 包中。
- 可以将调试信息输出到“即时窗口”,在 VS2015 中可以通过 ctrl+alt+i 打开,或在顶部菜单中选择 调试-》窗口-》即时,然后在 工具-》选项-》调试 中选中 “将所有输出窗口文本重定向到即时窗口”。
2016-11-24
开始添加 UWP 应用模式下的图形输出支持,在 Github 上搜索到了一份可供参考的源代码:sega/UWP/UWPMain.cpp,大致把这个文件和其它文件看了一遍,其实原理也都差不多,在 UWP 应用初始化时会创建两个位图作为帧缓存,而这帧缓存里的内容由 GUI 系统绘制产生,UWP 应用不主动绘制图像内容,在需要的时候它会直接将帧缓存里的内容绘制到屏幕上,相当于搬运工。
2016-11-21
添加了开关(switch)部件,参考自 Dribbble 上的一个设计,目前 LCUI 还不支持绘制圆角边框,所以选了个矩形的开关。
2016-11-19
找了些相关资料,发现微软开发者账号的注册费用可以用 PayPal 支付,只是中国国内不支持此选项,将地区改成了香港后就能用 PayPal 支付了,但是联系人信息里的地址这项是“香港特別行政区 XX省 XX市 XX区”感觉有点怪,可惜在确认信息后就不能改地区了,其实应该试试在提交前新建个页面改好地区,然后再返回那个页面继续提交的。
签名出错是证书的问题,用 VS2015 新建了个通用应用项目,目录里会有个 appx_StoreKey.pfx 文件,用它可以成功对应用包签名。
其实没必要手写清单文件,可以用 VS2015 创建一个空的通用应用项目,然后打开 Package.appxmanifest 文件,用它自带的图形化的清单编辑工具编辑就好了,编辑完后复制过来,然后再手动用命令打包。
感觉可以把 LCFinder 的项目转成 UWP 项目,但看上去需要做些配置才能顺利编译和打包,以后再弄吧。
手动创建的 appx 包可以用 PowerShell Add-AppxPackage
命令部署到当前设备上,部署效果如下:
之前试了几次,都是在出现启动画面后瞬间退出,后来在文件映射表中补充了依赖库(.dll),重新打包和部署后居然能运行了,效果如下:
启动画面的窗口没有关闭,得想办法关掉这个窗口,或者直接在这个窗口上输出界面。
接下来开始弄私人空间功能,开启/关闭这个功能需要一个新的“开关”部件,还要能设置密码,那设置的密码该存放到哪里?和配置数据存在一起?还是单独开个新文件?忘记密码又怎么办?
2016-11-16
找了些 win32 程序转 UWP 应用的方法,微软官网的这篇文章还可以,按照步骤打包出 appx 文件后,试了几次都是签名失败。
The following certificate was selected:
Issued to: Chao Liu
Issued by: StartCom Class 2 Object CA
Expires: Mon Jan 29 13:06:44 2018
SHA1 hash: 034308CD2E241BCC49B7C4BBB118B0C93843453C
Done Adding Additional Store
SignTool Error: An unexpected internal error has occurred.
Error information: "Error: SignerSign() failed." (-2147024885/0x8007000b)
到时候可以把 LCFinder 打包成 appx,上架到 windows 应用商店。上架 app 需要开发者账号,注册个人开发者账号需要支付 116 RMB,然而支付方式只有信用卡这一种,而且还不能用虚拟信用卡来支付,蛋疼。
图标是蓝色调的,和界面色调不一样,有些纠结要不要改色调,改的话,是改图标色调还是改界面色调?
2016-11-13
文件关联功能已经完成。花了点时间设计了个图标,色调为蓝色,扁平化风格,包含 LC 和标签这两种元素,LC 即 LCFinder 开头两个字母,而标签则是表示 LCFinder 具备标签搜索功能。图标弄出来后又为 LCFinder 加上了启动画面,启动画面是参考网易云音乐的,左边是图标,右边则是应用的名称和网址,画面停留 1.5 秒,在以图片查看器模式启动时不会显示启动画面。
2016-11-12
开始添加文件关联功能。当将图片文件路径作为运行参数来启动程序时,会优先初始化图片查看器视图,其它视图则会等到用户在点击返回按钮时初始化,之前有考虑过拆分 ui.xml 和 ui.css,将主界面和照片查看器的布局及样式数据拆分出来,方便单独加载,后来感觉没多大必要,毕竟界面载入速度本身就很快了。
在图片载入完成后再启动文件扫描线程,缓存当前所在文件夹内的所有图片文件。当扫描过当前文件及上/下几张图片后,生成文件迭代器并启用图片切换功能。
照片查看器可以打开多个,但只能有一个主界面,不然多个主界面同时运行一些操作会有可能产生冲突,尤其是数据库。在切换到主界面前,需要知道哪个窗口已经显示了主界面,如果有则关闭照片查看器窗口,然后前置显示主界面窗口。
2016-10-31
为文件夹视图添加了文件排序方式选择功能,点击按钮显示菜单,在菜单中选择排序方式。
2016-10-24
修改了一下缩略图列表视图的布局处理,之前在开始布局前会锁定布局,不让子部件的位置因列表宽度改变而变乱。现在的做法是直接设置列表视图的宽度,这样子部件即使更新布局也不会乱,但又出现了新问题,子部件数量太多的话,布局耗时会很长,最后由于耗时过多,LCUI 会中断本轮部件任务的处理,导致其他部件的任务被推迟。
2016-10-16
有些纠结,用了 textview-i18n 部件后,之前为 textview 设置的 CSS 样式就应用不到了,毕竟 textview-i18n 只是继承 textview 的方法,没有继承 CSS 样式,现在有三种解决方法:
- 将 xml 和 css 文档中所有 textview 替换成 textview-i18n。
- 将 css 文档中的 textview 替换成 .text,然后为 xml 文档中的 textview 和 textview-i18n 加上 text 样式类。
- 在 css 文档中的样式表加上 textview-i18n 选择器。
textview-i18n 目前只适合处理纯静态的文本,例如:“当前共占用 100MB 的空间” 这段文本,替换成其他语种的文本的话,中间的 100MB 该怎么处理?100MB 是会变化的,把这段文本分割成三段来处理是比较蠢的做法。可以添加类似于 printf() 函数的控制符功能,为 textivew-i18n 添加个函数,用于绑定数据,数据可以是字符串、整数等,而文本内容则可以是“当前共占用 %s 的空间”,在更新时会自动将 %s 替换成绑定的数据内容。
2016-10-15
语言文件已经决定用 yaml 文档格式,参考官方文档实现了 yaml 文档的解析,只做了字符串和哈希表这两种数据结构的处理,其它的用不到。接下来需要弄一个下拉菜单部件来选择语言,点击按钮后能在按钮下面显示下拉菜单。花了些时间把它做出来了,剩下的就是语言文件和全局文本显示(textview)部件的内容刷新功能了。
2016-10-02
补充了一些提示,当图片不存在或无法识别的时会显示提示,顺便修改了图片查看器里的操作按钮,在图片无效或放大/缩小到限定比例时会禁用按钮。
2016-09-27
了解到 TensorFlow,据说它可以用来做语音识别和图像识别,以后可以花点时间折腾它,为 LC-Finder 加入图像识别功能,这样就能根据用户的添加标签的习惯,自动识别图片内容并添加合适的标签。
2016-09-13
完成了时间范围选择器视图,在“集锦”视图中点击时间标题会显示时间范围列表,从中点击一个时间后图片列表就会跳转到这个时间段上。
2016-09-09
写了个用于删除单个文件记录的函数,后来感觉如果批量删除文件时调用这个函数的话效率会很慢,因为每删除一个文件都要先判断是哪个数据库 =》打开数据库 =》删除记录 =》关闭数据库,所以又改成了针对多文件的版本。
2016-09-07
打算改用 unqlite 数据库存储文件列表缓存,看了 unqlite 的文档,遍历数据库中所有的记录的方法是:用 unqlite_kv_cursor_init() 初始化一个游标,再用 unqlite_kv_cursor_first_entry() 将游标定位到第一个记录上,之后就进入循环,调用 unqlite_kv_cursor_next_entry() 逐个遍历记录,官方文档是用 unqlite_kv_cursor_key_callback() 函数来读取 key 的,这种方法要穿函数进去,感觉另外写个函数有些麻烦,于是改用 unqlite_kv_cursor_key() 函数直接读取。然而在测试时发现 unqlite_kv_cursor_key() 一直读不出数据,返回的 key 值的长度一直是非常大的负值。后来插了几段测试代码,调试时发现 unqlite_kv_cursor_key() 函数的第三个参数 pnByte 既是输入参数也是输出参数,作为输入参数时是指定 buf 的空间最大字节数,读取完 key 的内容后,输出的值是实际读取的字节数,之前一直以为第三个参数仅仅是输出参数,由于传递了未初始化的 size 进去,导致 key 的内容未被读取出来。
2016-08-25
上 Google 云端硬盘发现图片浏览界面还不错,于是照着它这个效果修改了 LC-Finder 的图片查看器界面,然而目前还没实现圆角边框、背景色渐变,只能达到这个效果了,以后再花时间实现。
考虑到界面的性能,并没有让图片查看器显示在缩略图列表上面,毕竟绘制底层的部件和透明处理也需要耗费一点时间。
图片的上一张/下一张的切换功能已经实现,但只能通过点击按钮才能切换,切换时没有过渡效果,而且不支持触控拖动切换,以后再改进。
测试时发现部件水平居中还有问题,在父级部件尺寸改变后,坐标有时没有自动更新。
2016-07-04
2016-07-01
注释掉缩略图的写数据库操作的代码后,呈现在界面上的缩略图内容正常,看上去问题出在数据库写操作上,既然数据库写操作能影响到后续读操作读到的数据内容,则说明 unqlite 数据库存在读写冲突,也就说读写操作是非线程安全的。搜索相关资料后发现可以调用 unqlite_lib_is_threadsafe() 函数检测当前 unqlite 函数库是否线程安全,而检测结果是线程不安全的。问题的解决方法很简单,为每个数据库加上一个互斥锁,每次读/写时锁上互斥锁即可。这个问题和以前遇到的 freetype 多线程读字形数据出现的问题类似。
2016-06-28
图片的尺寸读取速度异常的慢,为了不影响图片的呈现速度,需要将每张图片的尺寸记录到数据库中。对于初次载入的图片,默认以固定尺寸呈现,在其缩略图载入完后再将原始尺寸存入数据库,等重新排列缩略图列表时再应用新尺寸进行排列。
总会存在几张内容异常的缩略图,估计是哪里出现了内存访问越界。
搜索视图中,打算像文件夹视图那样,以缩略图+文字的形式呈现前标签列表,顶部加上搜索输入框和搜索按钮,用户可以手动输入标签名称,也可以在下方标签列表中点选。
2016-06-10
加了个图片信息面板,可以查看基本信息,标签和评分管理功能有待添加。
2016-06-06
有打算让 LC-Finder 成为通用应用(UWP),上架到 windows 应用商店。但和普通应用不一样,毕竟界面不是原生的。
2016-05-28
在 Github 的 Issues 页面里有人建议我完善错误处理,也就是针对 malloc() 和 strdup() 返回值为 NULL 的情况做处理,有些地方我会顺手判断一下 malloc() 的返回值,而有的地方不太重要就懒得加了。说到错误处理,以后可以考虑加入运行日志、错误日志和崩溃日志的记录功能,程序崩溃时可以让用户选择反馈错误信息。要实现这个功能,主要需要解决的问题是:如何在程序崩溃时自动调用其它程序?如何获取当前的调用堆栈信息?
错误反馈功能需要做成单独的程序,这样其它程序也能够用它提供的功能。错误反馈的界面可以参考 Chrome 的,只不过附件上传功能可能不会添加。
2016-05-23
当有缩略图很多的时候,如果快速滚动缩略图列表到末尾,会需要很长时间才能呈现出缩略图,因为滚动过的区域内的缩略图都在等待加载。为了解决这个问题,可以加上延迟处理,每次触发滚动加载都会重置延迟时间(定时器),直到停止滚动一段时间后才会开始加载缩略图,这样就能跳过中途滚动过的缩略图,直接加载当前停留区域内的缩略图。
2016-04-24
清空缩略图列表时还需要删除缓存中的缩略图,不然等到自动排除老缩略图时会访问已删除的部件。切换文件夹目录时,有时会遇到有几个文件夹紧贴着的情况,明明都有设置了外边距。
2016-04-23
缩略图列表视图的排版功能已经完成,排版任务是分多次执行的,本以为这样做就不会影响到其它消息的处理,然而实际上还是把整个界面都卡住了,这个问题需要解决。
2016-04-21
已经为主页集锦视图内的图片列表添加了按时间段分割效果,LCUI 的排版还有问题,需要解决。
2016-04-20
Widget_Empty() 函数的子部件销毁操作需要修改成阻塞式直接执行,异步销毁子部件会使其它正使用该部件的功能出现数据访问越界问题,当 LCFinder 切换文件夹目录并清空子集部件时如果有触发滚动加载,那么该功能会遍历子部件列表,一旦遍历到已销毁的子部件时会导致整个程序异常崩溃。
windows 的“照片”应用的文件夹宽度也是自适应的,看来 LCFinder 需要单独弄一个排版功能,统一修改文件夹和图片缩略图的宽度,修改方式有两种:
- 添加样式表,覆盖原有样式,这个对于文件夹的宽度修改比较方便,但目前只提供了添加样式表的功能,不能删除样式表,需要的话可以加上。
- 手动写代码遍历所有部件,逐个设置宽度。这个方法适用于处理图片缩略图的排版,毕竟宽度不是固定的,而且可控制,例如:只对当前可见区域内和前面的缩略图排版,后面的等浏览到时再继续排版。
已经添加了“集锦”视图,只是单纯的把所有图片列了出来,还没加上日期时间分割块,等后面再花时间弄。
2016-04-10
已经能显示缩略图了,PNG 图片的支持还好,而 JPG 图片基本无法载入成功。缩放后的缩略图有锯齿,这个问题不大,以后可以换其它缩放算法来解决。
unqlite_open() 函数是按 UTF-8 编码方式处理文件路径的,怪不得之前写入数据库时报 UNQLITE_IOERR 错误。
2016-04-07
准备弄缩略图的显示功能,缩略图的载入操作需要放在单独的线程上跑。
2016-04-02
Windows 的照片应用中,每行图片的宽度和数量不是固定的,它会根据图片的具体尺寸来调整,这个特性可以不用急着加到LCFinder里面,先暂时按固定尺寸排列图片,毕竟需要纠结一段时间才能想出合适的实现方案。
2016-03-31
现在可以浏览本地文件夹列表了,整个界面就这么点颜色,gif 图居然没有索引到纯白色。
2016-03-20
现在需要让图片在部件滚动到可见区域内时加载,首先得让滚动条部件能在滚动时触发 scroll 事件,这样就能在滚动时判断有哪些部件在容器的可见区域内。
想为滚动条加上 scroll 事件有些麻烦,触发事件时要准备事件ID和名称,而各个类型的事件ID不是统一管理的,要是被作为滚动层的部件本身也有自己的事件,那么事件ID有可能产生冲突。事件名称与ID的映射关系是保存在每个部件中的,看来需要调整,把它们统一存在一个地方。
2016-03-18
文件夹列表可以直接从文件系统里读取,而文件列表则从数据库中读取,这样实现起来就轻松多了。
2016-03-17
在“文件夹”视图浏览文件列表时,文件列表该从哪里读取?目前有两种来源:
- 文件系统,能区分出文件和文件夹,单纯查看文件是没问题,但如果要为文件添加标签、修改其他信息的话就不好处理,因为数据库没文件记录,缓存中也没文件记录,直接往数据库添加文件记录的话,还得往缓存添加文件记录,不然下次同步文件列表时会重复添加文件。目前的文件列表缓存是直接写在一个文件里的,不好操作,要么改用 unqlite 数据库来存储。
- 数据库,目前数据库只记录文件,视图中无法显示文件夹,如果把文件目录结构也保存到数据库里,看上去有些麻烦。
2016-03-15
使用 SQLite 插入数据时可以使用事务、预处理来提升效率,参考文章:http://blog.csdn.net/chenguanzhou123/article/details/9376537
2016-03-13
文件被删除/转移位置后需要保留标签和评分数据,有两种做法:
- 在不破坏文件原有数据的情况下把数据保存在文件中,这个看上去难度有点大。
- 将文件的sha1值和标签列表分别作为 key 和 value 保存到KV数据库中,这样即使文件改变了位置,只要把它的新位置导入进来再重新同步数据,就能恢复标签和评分信息。
SQLite 数据库插入数据有些慢,才添加200多条记录就要等好几秒,删除也是如此。
2016-03-12
测试 LCFinder 的文件列表缓存功能时发现每次都有检测到新增文件,按理来说新文件早就缓存了才对,后来打印 fread() 的返回值,返回值居然与实际记录的文件名长度不一致,奇怪了,数据明显没读完,feof() 也返回非0值。搜索了相关资料,有提到 fopen() 的参数,看了下 LCFinder 的文件读写代码,给两个 fopen() 的参数分别是 “r” 和 “w”,试着改成了 “rb” 和 “wb”,重新测试一遍,OK,问题解决。
2016-03-01
之前看过 Google Photos 网页上的功能介绍,据说可以自动识别照片内容,例如:狗,搜索“狗”就能搜索到相关图片,要是 LCFinder 能有这个功能的话,就不用靠用户自己根据图像内容特征来添加标签了,OpenCV有相关特征匹配算法,这样只需要保存各种对象的特征数据,然后程序在导入图片时自动匹配特征并添加标签。看上去特征匹配算法会很耗时,假设进行一次特征匹配需要耗时10ms,有32组特征,每组特征里有10种相关特征的数据,需处理5000张图片,那么耗时大约为:0.01s*32*10*5000 = 16000s ≈ 4.44小时
,卧槽,这种功能需要单独开来,弄个界面,告诉用户这个功能大约耗时多长,需要用的话就一直开着程序。图像特征库可以导入/导出,这样可以分享给其它用户,省得每个用户都自己积累特征数据。
2016-02-28
为文件夹视图添加了文件同步功能,点击同步按钮后,会显示一个提示框,能看到文件扫描数量的变化。
2016-02-25
纯文字看的很枯燥,以下是目前的效果图,以后也会陆续贴一些图,可以从这些图中看到LCFinder的各种变化。
2016-02-24
缩略图缓存用 unqlite 数据库来保存,毕竟只需要用文件路径(key)来获取缩略图(value)。unqlite_open()
函数的文件路径参数是 char *类型,而从 sqlite 数据库中保存/读取的文件路径是 UTF-8 编码的,不知道非宽字符版的文件操作函数是以何种编码方式处理文件路径,感觉很有可能是 ANSI,那么需要转换编码。
2016-02-23
考虑到 LCFinder 浏览的图片数量会很大,那么就得尽量减少内存占用,起码要能应付10000张图片。首先,需要在内存中弄一块缓存区来缓存缩略图,限制最大占用空间,当达到上限时可释放较老的缩略图缓存来腾出空间。除了内存中,在硬盘中也需要保存缩略图,方便以后读取。缩略图与原图需要保持同步,那么就得在缩略图数据中保存原图的修改时间,当修改时间不一致时更新缩略图。其次,用于显示图片的 LCUI 部件需要弄成单缓存模式,不缓存部件位图,减少内存占用。图片不用一次性载入完,可以分批载入,例如:每次载入100张图片,当浏览到最后几张图片时自动载入下100张图片。
单独开了个日志来记录 LCFinder 的开发动态,代码库:https://github.com/lc-soft/LC-Finder 。LCFinder 是一个资源检索与管理工具,类似于 windows 10 系统中的“照片”应用,开发它是为了满足自己的需求,顺便测试和完善 LCUI 的各项功能。
文章版权归作者所有,未经许可不得转载。