网站首页 > 技术文章 正文
openKylin便签贴作为侧边栏的一个小插件,提供便捷的文本记录和灵活的页面展示。openKylin便签贴分为两个部分:便签列表和便签页。其中,便签列表以列表形式展示所有内容,可切换图标/列表视图,并提供搜索查找功能;便签页提供编辑内容功能,可对内容进行字体大小/颜色、斜体、下划线、有序/无序、插入图片等操作。
openKylin便签贴及相关软件包安装:
$sudo apt install ukui-notebook
注意:openKylin 1.0.1及2.0 Alpha版本均已预装
一.openKylin便签贴功能介绍
1.便签列表
- 实时按照修改时间倒序排序
- 显示每条便签的修改时间和部分文本内容
- 新建:列表条目增加并打开一个便签页
- 搜索:匹配列表中所有便签的文本内容进行搜索
- 删除:删除当前列表选中条目,删除后自动选中列表中上一条便签,若删除时,对应条目的便签为打开状态,则同时关闭此便签页;若无列表中无条目选中,则删除无效
- 支持双击列表/图标条目,打开或重新激活置顶便签并获取输入焦点
2.便签页
- 支持文本修改自动保存
- 支持用户自定义便签头颜色并保存数据库
- 文本修改后,此便签页对应便签列表中条目自动置顶排序
- 便签头颜色修改后,此便签页对应便签列表中条目自动更新同步
- 删除此便签:删除此便签,并删除此便签页对应便签列表中对应条目
- 打开便签:任意便签可重新唤起便签列表
- 新建:在任一便签页新建会创建新便签页,同步到便签
- 关闭:关闭当前便签页,若当前便签页文本内容为空,则删除此便签,并删除此便签页对应便签列表中条目
- 支持加粗、斜体、下划线、删除线、无序列表、有序列表
- 支持修改字体大小,字体颜色
二.openKylin 便签贴实现原理
便签贴基于QT实现,主要涉及便签列表的QListView类和便签编辑页的QTextEdit类。所以在讲便签贴具体实现之前,简单介绍一下这两个类。
1. QListView
QListView可以用来以列表的形式展示数据,在Qt中使用model/View结构来管理数据与视图的关系,model负责数据的存取,数据的交互通过delegate来实现.
(1)数据模型
- QT提供了一些现成的models用于处理数据项:
- QStringListModel 用于存储简单的QString列表。
- QStandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。
- QDirModel 提供本地文件系统中的文件与目录信息。
- QSqlQueryModel, QSqlTableModel,QSqlRelationTableModel用来访问数据库。
模型中的每个数据项都有一个与之对应的role来存储某一类数据。需要存取自定义数据可以使用UserRole,UserRole+1...
便签使用QAbstractListModel,自定义了可编辑列表模型noteModel。便签定义NoteRole存储数据对象:
enum NoteRoles{
NoteID = Qt::UserRole + 1,
NoteFullTitle,
NoteCreationDateTime,
NoteLastModificationDateTime,
NoteDeletionDateTime,
NoteContent,
NoteScrollbarPos,
NoteColor,
NoteMdContent,
};
数据的存取:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
(2)自定义delegate
模型的交互和绘制通过自定义delegate来实现。便签定义了两种delegate——list和icon,这两种基本只在item的绘制有区别。
2.QTextEdit
QTextEdit类是一个多行文本框控件,可以显示多行文本内容,当文本内容超出控件显示范围时,可以显示水平垂直滚动条,用于编辑和显示纯文本和富文本。
操作函数:
- setPlainText() 设置多行文本框的内容
- toPlainText() 返回多行文本框的文本内容
- setHtml() 设置多行文本框的文本内容为HTML文档,HTML文档是描述网页的
- toHtml() 返回多行文本框的HTML内容
- clear() 清除多行文本框的内容
信号:
- textChanged():文本改动信号
- currentCharFormatChanged(const QTextCharFormat &format):文本风格改动信号
- void cursorPositionChanged():光标位置变化信号
- QTextEdit常与QTextCursor一起使用,提供接口进行编辑。
常用函数:
- beginEditBlock() endEditBlock():分组游标操作
- insertBlock() 将新文本块(段落)插入光标位于光标位置的文档,并将光标移动到新块的开头。
- insertFragment() 将现有文本片段插入到光标位置的文档中。
- insertImage() 将图像插入到光标位置的文档中。
- insertText() 在光标位置将文本插入到文档中。
- insertFrame() 在光标的当前块之后将框架插入到文档中,并将光标移动到新框架中空块的开始。
- insertList() 在光标位置将列表插入到文档中,并将光标移动到列表中第一个项目的开始。
- insertTable() 在光标的当前块之后将表插入到文档中,并将光标移动到表后块的开始。
三.openKylin 便签贴具体实现
1. 便签列表
首先,介绍一下便签列表涉及的几个类。
- NoteData 便签数据类,记录便签id,头颜色,标题,最新编辑时间等内容
- NoteModel 继承QAbstractListModel,便签列表模型抽象类,展示和管理列表数据。
- QModelIndex 可以用来引用模型中的项,它包含确定这个项在模型中的位置所需的所有信息。索引拥有行信息、列信息,可以使用row()、column()和parent()函数来获取这些信息。为noteModel 模型和noteView 列表提供“桥梁”,供索引。
- NoteView 继承Qlistview,设置NoteModel 为模型,并显示listview。最终自定义的列表模型中的数据以列表形式显示。
(1) 新建
首先确定是否有其他正在进行的操作,若无则可以开始新建便签操作,并将列表滚动到最高处。m_noteCounter记录便签数,加一。产生新便签数据类,插入新便签到便签模型中,并将数据保存到数据库中。
void Widget::createNewNote()
{
if (!m_isOperationRunning) {
m_isOperationRunning = true;
m_noteView->scrollToTop();
++m_noteCounter;
NoteData *tmpNote = generateNote(m_noteCounter);
// insert the new note to NoteModel
QModelIndex indexSrc = m_noteModel->insertNote(tmpNote, 0);
// update the editor header date label
QString dateTimeFromDB = tmpNote->lastModificationdateTime().toString(Qt::ISODate);
QString dateTimeForEditor = getNoteDateEditor(dateTimeFromDB);
// 从排序过滤器模型返回与给定 indexSrc 对应的源模型索引。
m_currentSelectedNoteProxy = m_proxyModel->mapFromSource(indexSrc);
saveNoteToDB(m_currentSelectedNoteProxy);
// 设置索引 m_currentSelectedNoteProxy 所在的页面为当前页面
m_noteView->setCurrentIndex(m_currentSelectedNoteProxy);
m_isOperationRunning = false;
}
......
}
(2) 便签列表
双击列表项打开便签,或者右键弹出操作菜单,可打开、删除和清空列表。
- 打开:在滚动区域单机便签,为取消突出显示上一个选定的便签。如果在临时便签存在时选择便签,即为删除临时便签,突出显示所选便签,并将所选便签内容加载到textedit。
- 删除:通过当前列表模型获取noteid,删除对应noteid的便签项,保存到数据库。
- 清空:清空便签模型、列表等所有信息,并删除数据库内容。
(3)内容搜索
// 搜索栏文本输入
connect(m_searchLine, &QLineEdit::textChanged, this, &Widget::onSearchEditTextChanged);
使用Queue队列获取搜索栏文本内容,将用于过滤模型内容的固定字符串设置为给定模式,根据过滤模型的noteid,显示筛选后的listview.
void Widget::onSearchEditTextChanged(const QString &keyword)
{
m_searchQueue.enqueue(keyword);
if (!m_isOperationRunning) {
m_isOperationRunning = true;
// disable animation while searching
m_noteView->setAnimationEnabled(false);
while (!m_searchQueue.isEmpty()) {
qApp->processEvents();
QString str = m_searchQueue.dequeue();
if (str.isEmpty()) {
clearSearch();
} else {
m_noteView->setFocusPolicy(Qt::NoFocus);
// 过滤
findNotesContain(str);
}
}
m_noteView->setAnimationEnabled(true);
m_isOperationRunning = false;
}
}
void Widget::findNotesContain(const QString &keyword)
{
// 将用于过滤源模型内容的固定字符串设置为给定模式
m_proxyModel->setFilterFixedString(keyword);
// 如果匹配到不止一行
if (m_proxyModel->rowCount() > 0) {
selectFirstNote();
} else {
m_currentSelectedNoteProxy = QModelIndex();
}
}
(4)视图切换
视图有列表视图和图标视图,自定义两种模型代理iconViewModeDelegate和listViewModeDelegate。
m_proxyModel->setSourceModel(m_noteModel); // 代理真正的数据模型,对数据进行排序和过滤
m_proxyModel->setFilterKeyColumn(0); // 此属性保存用于读取源模型内容的键的列,listview只有一列所以是0
m_proxyModel->setFilterRole(NoteModel::NoteMdContent);// 此属性保留项目角色,该角色用于在过滤项目时查询源模型的数据
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);//
m_noteView->setItemDelegate(new iconViewModeDelegate(m_noteView)); // 安装定制delegate提供编辑功能
m_noteView->setModel(m_proxyModel); // 设置view的model是proxyModel,proxyModel作为view和QAbstractListModel的桥
2.便签页
便签页操作区:
(1)文本编辑
文本编辑是基于QTextEdit实现的。
connect(ui->textEdit, &QTextEdit::textChanged, this, &EditPage::textChangedSlot);
void EditPage::textChangedSlot()
{
emit texthasChanged(m_noteId, this->m_id);
}
绑定m_noteId为便签id,m_id为当前编辑页面id。两个一起确定编辑的便签页面。通过此信号texthasChanged()传递给便签列表页面,实时更新对应列表项标题等内容。
connect(m_editors[m_editors.size() - 1], SIGNAL(texthasChanged(int,int)), this,
SLOT(onTextEditTextChanged(int,int)));
(2)便签头
自定义便签头noteHead,继承QWidget,显示便签头颜色。便签头菜单noteHeadMenu,也是一个QWidget。包括新建按钮、调色板、选项和关闭按钮。
点击调色板按钮,可弹出调色板菜单,这个菜单由PaletteWidget.ui实现。
点击以上调色按钮选择颜色后,以选择红色为例:
void EditPage::redBtnSlot()
{
QColor color((PaletteWidget::KY_RED));
m_editColor = color;
emit colorhasChanged(m_editColor, m_noteId);
m_noteHead->colorWidget = color;
m_noteHeadMenu->colorWidget = color;
update();
}
设置便签头颜色为红色并更新,同时给便签列表窗口发送颜色改变信号colorhasChanged(),便签列表收到信号后更新列表头颜色。
connect(m_editors[m_editors.size() - 1], SIGNAL(colorhasChanged(QColor,int)), this,
SLOT(onColorChanged(QColor,int)));
(3)字体风格
字体风格包括字体大小、颜色、加粗、下划线等。首先以字体大小/颜色按钮组为例,介绍一下。
- 按钮组
- CustomPushButtonGroup继承QFrame,通过加载qss样式文件设置字体大小/颜色按钮组。
- 按钮下拉选项
- 字体大小下拉选项SetFontSize和字体颜色下拉选项SetFontColor,都是基于QListWidget编写的widget窗口。
// 字体颜色大小
connect(m_setSizePage->ui->listWidget, &QListWidget::itemClicked, this,
&EditPage::setFontSizeSlot);
connect(m_setColorFontPage->ui->listWidget, &QListWidget::itemClicked, this,
&EditPage::setFontColorSlot);
以字号修改为例,字体大小和颜色都是采用QTextCharFormat对象实现的。
// 字号
void EditPage::setFontSizeSlot()
{
int num = m_setSizePage->ui->listWidget->currentRow();
ui->fontTwinButtonGroup->getFontSizeBtn()->setButtonSize(QString::number(num+10));
m_setSizePage->close();
update();
QTextCharFormat fmt;
InformationCollector::getInstance().addMessage(QString("set font size to %1.").arg(num+10));
fmt.setFontPointSize(num+10);
mergeFormatOnWordOrSelection(fmt);
}
选择字号后,设置QTextCharFormat文本格式,根据格式作以下操作:
void EditPage::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = ui->textEdit->textCursor();
if (!cursor.hasSelection()) {
// cursor.select(QTextCursor::WordUnderCursor);
}
cursor.mergeCharFormat(format);
ui->textEdit->mergeCurrentCharFormat(format);
ui->textEdit->setFocus(Qt::TabFocusReason);
}
获取控件的焦点,假设当前控件上的文本并没有被选中,就指定光标区域所在的词为高亮选定词,从而设置字体风格样式。其他的字体风格,如加粗、斜体等:
- 加粗:
- fmt.setFontWeight(QFont::Bold);
- 斜体:
- fmt.setFontItalic(QFont::StyleItalic);
- 下划线:
- fmt.setFontUnderline(ui->fontPropertyWidget->underlineBtn()->isCheckable());
- 删除线:
- fmt.setFontStrikeOut(ui->fontPropertyWidget->strikeOutBtn()->isCheckable());
(4)图片插入
插入图片和字体风格类似,采用对QTextImageFormat 对象进行操作。在光标处进行图片插入。
connect(ui->fontPropertyWidget->insertBtn(), &QPushButton::clicked, this, &EditPage::insertpicture);
void EditPage::insertpicture()
{
......
QTextCursor cursor = ui->textEdit->textCursor();
if(cursor.atStart())
{
m_isInsImg = true;
}
QTextImageFormat imageFormat;
imageFormat.setWidth ( image.width() );
imageFormat.setHeight ( image.height() );
imageFormat.setName ( QString("data:image/%1;base64,%2")
.arg(QString("%1.%2").arg(rand()).arg(format))
.arg(base64l.data())
);
cursor.insertImage ( imageFormat );
}
(5)有序/无序列表
对QTextListFormat对象进行操作,listFmt.setStyle(style)设置列表样式,无序为QTextListFormat::ListDisc,有序为QTextListFormat::ListDecimal。
connect(ui->fontPropertyWidget->unorderedBtn(), &QPushButton::clicked, this, &EditPage::setUnorderedListSlot);
connect(ui->fontPropertyWidget->orderedBtn(), &QPushButton::clicked, this, &EditPage::setOrderedListSlot);
QTextListFormat listFmt;
if (cursor.currentList()) {
listFmt = cursor.currentList()->format();
}
listFmt.setStyle(style);
cursor.createList(listFmt);
上述代码首先检查游标是否在现有列表中,如果是,则为新列表的列表格式提供适当的缩进级别。这允许创建嵌套列表,增加缩进级别。更复杂的实现还将对列表每个级别的项目符号使用不同的符号。
3.pc/平板模式切换
在openKylin平板模式下,便签贴支持全屏化,隐藏最大化按钮,dbus信号监听模式切换。
QDBusConnection::sessionBus().connect(KYLIN_ROTATION_SERVICE, KYLIN_ROTATION_PATH, KYLIN_ROTATION_INTERFACE,
QString("rotations_change_signal"), this, SLOT(rotationChanged(QString)));
QDBusConnection::sessionBus().connect(KYLIN_ROTATION_SERVICE, KYLIN_ROTATION_PATH, KYLIN_ROTATION_INTERFACE,
QString("mode_change_signal"), this, SLOT(modeChanged(bool)));
模式切换:
4. 数据同步
便签使用SQLite数据库对数据增删改查操作,具体表结构如下:
id | INTEGER | 便签在数据库中唯一标识,用以区分不同便签 |
creation_date | INTEGER | 便签创建日期 |
modification_date | INTEGER | 便签修改日期 |
deletion_date | INTEGER | 便签删除日期 |
content | TEXT | 便签内容,以html形式存储 |
full_title | TEXT | 便签标题 |
note_color | INTEGER | 便签头颜色 |
md_content | TEXT | 文本化的数据内容,用于实现便签的查找操作 |
以上就是openKylin便签贴实现原理,界面的加载逻辑简单,其中由于便签编辑控件选择的是textedit,对于获取图片插入状态,缩进控制等相关功能存在问题,这也是后续openKylin便签需要继续改进的地方。欢迎感兴趣的小伙伴加入我们~
猜你喜欢
- 2024-10-20 Qt 开发经验总结 qt软件开发
- 2024-10-20 Qt QTableWidget用法总结 qtablewidget qtableview
- 2024-10-20 实战PyQt5: 068-MV框架中的项视图部件
- 2024-10-20 Qt开发经验小技巧231-235 qt开发入门简介
- 2024-10-20 Python+PyQt5进阶(5) pyqt5 django
- 2024-10-20 Qt项目升级到Qt6经验总结 qt更新界面
- 2024-10-20 QTableWidget表格中增删数据 qtablewidget清空表格
- 2024-10-20 C/C++ Qt StatusBar 底部状态栏应用
- 2024-10-20 Qt的常用控件 qt控件详解
- 2024-10-20 Python PyQt5通过QTableWidget表格控件操作SQLite数据库
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)