git 使用writer_GitHub - Vpredictor/WriterFly: [QT/C++] 写作天下,为作家创造世界而生,执云作笔,诉尽平生意。...

写作天下

简介

为作家们创造世界而诞生,执云作笔,诉尽平生意。

集简约UI与人性化AI于一体的码字工具,无论是小说、作文、日记、报告,都能轻松驾驭。

QQ交流群:705849222

特点

已有功能:

自由的目录:自动序号,导入导出

一体化界面:全局自定义主题

输入动画:独创文字平滑输入效果、光标平滑移动

智能引号:增删移动全在一键之间

智能空格:缩进、标点、移动光标融为一体

智能回车:双引号内回车、自动添加句末标点、自动缩进

自动标点:语气词后面自动加上标点符号

同音词覆盖:选词错误,无需删除,直接覆盖旧内容

自动分段:换行/排版时,过长段落自动在合适的地方分段

自动提示:输入时自动提示同义词、相关词、常用句

随机取名:最轻便的取名方式,要啥来啥

一键排版:全自动

未来功能(近期繁忙,无力开发):

大纲列表

章节细纲

名片注释

名字高亮

综合搜索

全书替换

右下角通知卡片

自定义主题

自定义快捷键

小黑屋

云同步

排行榜

拼字房间

章节分享

“求评价”广场

主角背包

快捷键

ctrl + ←/→ 按词语移动

alt + ←/→ 按句子移动

ctrl + alt + ←/→ 按段落移动

ctrl + shift + ↑/↓ 扩大/缩小选择

ctrl/alt + ↑/↓ 屏幕滚动,alt更快

空格键 智能空格

引号键 智能引号

回车键 智能回车

Tab键 句子补全+光标跳过

ctrl+T 一键排版

ctrl+D 句内同音词替换(不必连续)

ctrl+F 章内文字搜索

ctrl+P 全局综合搜索

更多快捷键请等待后期加入(或将支持自定义快捷键)

技术特点

开发环境:C++/Qt5.11.3,Qt Creator

运行环境:Windows、Android

1、标点AI

虽然是简单暴力的枚举(都是自己遇到的),但是有特判了上千种情况,也不容易吧……

若是人物语言描写,还能根据人物的表情动作神态来分析情感程度,进一步提高准确度。

手动判断,故覆盖不全,不过在一定情况下,枚举简单易用,是为上上之选!

还有各种常用按键的自动化操作,极大程度上增加效率!

/**

* 小说语气识别的AI类

*/

class NovelAIBase

{

/*...略了一些方法...*/

void operatorSmartSpace(); // 智能空格:按下空格触发

void operatorSmartEnter(); // 智能回车:按下回车触发

void operatorSmartQuotes(); // 智能引号:按下引号触发

void operatorSmartBackspace(); // 智能删除,按下删除触发

bool operatorAutoPunc(); // 自动标点:语气词自动触发

bool operatorSentFinish(); // 句末标点:增加或转化成结尾

void operatorSmartQuotes2(int left, int right); // 智能引号,仅在选中文本的情况下调用

virtual int getWordCount(QString str); // 字数统计,交给 NovelAI

QString getPunc(QString para/*段落*/, int pos/*光标*/); // ☆核心:取标点(句子)

QString getPunc(/*QString fullText, int pos*/); // 这个是全部文本中的某一部分

QString getPunc(int pos); // 全部文本,特定位置的标点

QString getPunc2(int pos); // 把","改成"。"

QString getPunc2(); // 把","改成"。"

int getDescTone(QString sent); // 句子语气标点,影响语气导向

QString getTalkTone(QString sent, QString sent2, int tone, QString left1, QString left2);

void updateCursorChars(); // 修改光标附近的字符

bool canDeletePairPunc(); // 是否能够删除成对文本(能删就删)

bool isCursorInQuote(QString text, int pos); // 是否在引号里面

virtual void moveCursor(int x) = 0; // 移动光标

virtual void insertText(int pos, QString text) = 0; // 插入文本

virtual void insertText(QString text) = 0; // 插入文本

virtual void deleteText(int start, int end) = 0; // 删除文本

// bool isNextLang(); // 是否是后一句话(双引号前面多的是逗号)

bool isSentPunc(QString str); // 是否为句末标点(不包含引号和特殊字符,不包括逗号)

bool isSentSplitPunc(QString str); // 是否为句子分割标点(包含逗号)

bool isSentSplit(QString str); // 是否为句子分割符(各类标点,包括逗号)

bool isASCIIPunc(QString str); // 是否为英文标点(不包含引号和特殊字符)

bool isBlankChar(QString str); // 是否为空白符

bool isBlankChar2(QString str); // 是否为换行之外的空白符

bool isBlankString(QString str); // 是否为空白字符串

bool isSymPairLeft(QString str); // 是否为对称标点左边的

bool isSymPairRight(QString str); // 是否为对称标点右边的

QString getSymPairLeftByRight(QString str); // 根据右边括号获取左边的括号

QString getSymPairRightByLeft(QString str); // 根据右边括号获取左边的括号

QString getCursorFrontSent(); // 获取当前面的句子

QString getCurrentChar(int x); // 获取当前位置的附近汉字

bool isQuoteColon(QString str); // 汉字后面是否需要加标点

protected:

QString _text, _pre_text; // 文本

int _pos, _pre_pos, _dif; // 光标位置和字数差

QString _left1, _left2, _left3, _right1, _right2; // 光标附近的文本

bool isInQuotes; // 是否在引号里面(用来判断是否为语言或者描述)

private:

QString _shuo_blacklists, _dao_whitelists, _wen_blacklists; // “说”黑名单、“道”白名单、“问”黑名单

QString _symbol_pair_lefts, _symbol_pair_rights; // 成对符号左边/右边

QString _quote_no_colon, _quantifiers; // 引号前面没有冒号

QString _sent_puncs, _sent_split, _sent_split_puncs, _blank_chars; // 句末标点、句子分割符、空白符

QString _auto_punc_whitelists; // 自动标点白名单

};

子类为NovelAI,再下一个子类 NovelEditor 使用到了多继承,同时继承 QTextEdit 与 NovelAI。

其实设计得不是很好,AI 类需要大量调用 Edit 的方法,于是加了许多虚函数,将三个类紧紧耦合到一起(不过关系不大,本来就是怕一个文件代码太长而分开的)。

2、仿IDE思路

当今所谓的写作软件,单论写作方面,其实和一个记事本没多少差别,无非多了目录与自动缩进,以及简单的自动保存等。其一些亮点,比如强制写作的小黑屋、多人竞争的在线拼字,都无法从“码字”本身来帮助作者达到更高效的办公,这些附加的功能甚至会让作者分心,无法好好静心创作。

写作天下定位一款单纯的“文学创作”编辑器,主打从“写作”本身提高效率,弱化与文字无关的功能。或许后期将会添加小黑屋、拼字等,但绝不会将这些作为主要功能。

备注:近期繁忙,无力开发,所以只是“思路”!

一些功能:

增强的编辑功能

自动提示

面向对象写作:名片系统

文字高亮(名片高亮)

高度个性化设置(功能细节、快捷键、主题等)

追求自动的编辑器

一句话:能自动的,绝不手动!

为了实现更加人性化的功能,程序中使用了大量的算法。

比如括号匹配功能,输入左括号时自动添加右括号、删除键删除成对的符号,这里使用数据结构中的栈,判断光标前后各自左右括号的数量,入栈出栈,来判断是否需要添加/删除,而不仅仅依靠光标前一个字符。

还有光标移动、在标点前面换行、在语言描写内回车自动插入前后引号、修改引号前面的逗号与冒号等,一个按键,多种功能。

轻松提示

经过多种方案的性能测试,选取了速度最快、运行最稳定的方式。

以下为输入文字后自动提示算法:

/**

* 某个句子的某个位置处进行搜索

* @param sent 欲搜索的完整句子(短句,不包含标点)

* @param cursor 光标在句子中的相对位置

* @return 是否找到

*/

bool Lexicons::surroundSearch(QString sent, int cursor)

{

int len = sent.length();

search_result.clear();

match_sentence = false;

bool find = false;

matched_key = "";

int start_pos = 0;

QString l1 = "", l2 = "", l4 = "";

if (cursor >= 1)

l1 = sent.mid(cursor-1, 1);

if (cursor >= 2)

l2 = sent.mid(cursor-2, 2);

if (cursor >= 4)

l4 = sent.mid(cursor-4, 4);

if (random_inited)

{

// 随机种类列表

if (((matched_key = l2) == "随机")

|| ((matched_key = l2) == "取名")

|| ((matched_key = l4) == "随机取名"))

if (searchRandom("随机取名"))

{

matched_case = COMPLETER_CONTENT_RAND_LIST;

return true;

}

// 姓氏

if (surname_inited && (((matched_key = l1) == "姓")

|| ((matched_key = l2) == "姓氏")))

if (searchRandom("姓氏"))

{

matched_case = COMPLETER_CONTENT_SURN;

return true;

}

// 人名

if (name_inited && (((matched_key = l2) == "人名")

|| ((matched_key = l2) == "名字")))

if (searchRandom("人名"))

{

matched_case = COMPLETER_CONTENT_NAME;

return true;

}

// 随机列表

for (QString s : random_sort_list)

{

if (cursor >= s.length() && sent.mid(cursor-s.length(), s.length()) == s && isFileExist(lexicon_dir + "random/" + s + ".txt"))

{

matched_key = s;

matched_case = COMPLETER_CONTENT_RAND;

searchRandom(s);

return true;

}

}

}

// 搜索4个字

if (!find)

{

start_pos = 0; // 开始搜索的位置

if (start_pos < cursor-4) start_pos = cursor-4;

for (int i = start_pos; i <= len-4 && i < cursor; i++)

{

if (search(sent.mid(i, 4), true))

{

find = true;

matched_key = sent.mid(i, 4);

break;

}

}

}

// 搜索三个字

if (!find)

{

start_pos = 0;

if (start_pos < cursor-3) start_pos = cursor-3;

for (int i = start_pos; i <= len-3 && i < cursor; i++)

if (search(sent.mid(i, 3), true))

{

find = true;

matched_key = sent.mid(i, 3);

break;

}

}

// 搜索两个字

if (!find)

{

start_pos = 0;

if (start_pos < cursor-2) start_pos = cursor-2;

for (int i = start_pos; i <= len-2 && i < cursor; i++)

if (search(sent.mid(i, 2), true))

{

find = true;

matched_key = sent.mid(i, 2);

break;

}

}

//qDebug() << "surround search:" << matched_key << " result:" << search_result;

search_last = sent;

matched_case = COMPLETER_CONTENT_WORD;

return find;

}

/**

* 在词库中搜索某一个词语

* @param key 欲搜索的词语

* @param add 是否为添加模式。如果不是,则先清空已经找到的列表

* @return 是否有搜索结果

*/

bool Lexicons::search(QString key, bool add)

{

if (key.isEmpty()) return false;

/* // 上次搜索的缓冲区,但是没必要了,因为每次surroundSearch的时候

* // 都把上次的搜索结果清空了,key还在但是result没了

if (key == search_last)

{

return true;

}*/

if (!add) search_result.clear();

bool find = false;

int key_len = key.length();

if (synonym_inited)

{

QStringList synonym_list;

int pos = 0;

while (1)

{

//pos = synonym_text.indexOf(key, pos);

pos = synonym_text.indexOf(QRegExp("\\b"+key+"\\b"), pos);

if (pos == -1) break;

int left = synonym_text.lastIndexOf("\n", pos)+1;

int right = synonym_text.indexOf("\n", pos);

if (right == -1) right = synonym_text.length();

QString para = synonym_text.mid(left, right-left);

QStringList list = para.split(" ", QString::SkipEmptyParts);

// TODO 后期将改成 QList

synonym_list.append(list);

pos += key_len;

find = true;

}

if (synonym_list.size() > 0 && shouldRandom())

{

if (shouldRandom())

{

std::random_shuffle(synonym_list.begin(), synonym_list.end());

}

search_result.append(synonym_list);

}

}

if (related_inited)

{

QStringList related_list;

int pos = 0;

while (1)

{

pos = related_text.indexOf(QRegExp("\\b"+key+"\\b"), pos);

if (pos == -1) break; // 找不到了

if (pos > 0 && related_text.mid(pos-1, 1) == "{") // 是标题

{

int left = related_text.indexOf("[", pos)+1;

int right = related_text.indexOf("]", pos);

if (right < left-1) // 出现了错误

{

pos = left+1;

continue;

}

if (right == -1) right = related_text.length();

QString para = related_text.mid(left, right-left);

QStringList list = para.split(" ", QString::SkipEmptyParts);

// TODO 后期将改成 QList

related_list.append(list);

pos = right;

}

else // 是内容

{

// 如果是本程序标准格式

int left = related_text.lastIndexOf("[", pos)+1;

int right = related_text.indexOf("]", pos);

// 如果只是一段一段分开的

int left_n = related_text.lastIndexOf("\n", pos)+1;

if (left_n >= left)

{

left = left_n;

right = related_text.indexOf("\n", pos);

}

if (right == -1) right = related_text.length();

QString para = related_text.mid(left, right-left);

QStringList list = para.split(" ", QString::SkipEmptyParts);

// TODO 后期将改成 QList

related_list.append(list);

pos = right;

}

find = true;

}

if (related_list.size() > 0)

{

if (shouldRandom())

{

std::random_shuffle(related_list.begin(), related_list.end());

}

search_result.append(related_list);

}

}

if (sentence_inited && find)

{

if (sentence_text.indexOf(key) > -1)

{

match_sentence = true;

search_result.append("-->");

}

}

//qDebug() << "search:" << key << " result:" << search_result;

return find;

}

/**

* 通过一个词语,来获取应该显示的随机取名提示列表

* @param key 欲搜索的词语

* @return 是否找到结果

*/

bool Lexicons::searchRandom(QString key)

{

// ==== 随机取名列表 ====

if (key == "随机取名" || key == "随机" || key == "取名")

{

for (QString s : random_sort_list)

search_result.append(s);

std::random_shuffle(search_result.begin(), search_result.end());

return true;

}

// ==== 随机取名具体 ====

for (int i = 0; i < random_sort_list.size(); i++)

if (random_sort_list.at(i) == key)

{

QStringList list = random_text_list.at(i).split(" ", QString::SkipEmptyParts);

search_result = list;

std::random_shuffle(search_result.begin(), search_result.end());

search_result = search_result.mid(0, 100);

return true;

}

return false;

}

/**

* 搜索后返回结果

* @return 搜索结果

*/

QStringList Lexicons::getResult()

{

return search_result;

}

3、平滑输入

创作,或是为了宁静的内心,或是为了美好的生活,终是需要解放内心的压抑。

而写作天下的输入动画与平滑光标,为了打造愉悦的创作环境,可谓是呕心沥血。

动画算法

输入动画

输入动画中每一个字符都是一个Label对象,设置文字颜色为对应字符,然后使用一个动画管理器进行统一管理,开放管理器的API给编辑器,编辑文字的同时通过管理器调整所有文字的动画,便不会出现冲突的情况。

难点在要将每个Label和对应文字连在一起,尤其是支持实时修改。看了其他相同功能的开源项目,大多只支持英文,并且是监听到文字输入后才开始动画,等动画结束再上屏,无法在输入后立即删除或者修改,非常影响效率。

/**

* 输入动画管理器类

*/

class EditorInputManager : public QObject

{

Q_OBJECT

public:

EditorInputManager();

void setEditor(QTextEdit* edit); // 设置开启动画对应的编辑器

void setColor(QColor color); // 设置全文颜色

void textChanged(int old_position, int diff); // 文字改变时调整动画控件

void updateRect(int range_start, int rande_end); // 更新动画控件的位置

void addInputAnimation(QPoint point, QString str, int position, int delay, int duration); // 添加一个文字动画

void addInputAnimation(QPoint point, QString str, int position, int delay, int duration, QColor color); // 添加一个带有不同颜色的文字动画

void updateTextColor(int current_position); // 修改全文颜色,同时修改正在动画的颜色

public slots:

void aniFinished(int position, EditorInputCharactor *); // 动画结束,传参文字位置

private:

QTextEdit* _edit; // 编辑器

QColor font_color; // 全文颜色

QList ani_list; // 动画控件列表

};

/**

* 输入动画的某一个字符实体,存储动画信息、光标位置、光标坐标、光标文字等

*/

class EditorInputCharactor : public QLabel

{

Q_OBJECT

Q_PROPERTY(int fontsize READ getFontSize WRITE setFontSize RESET resetFontSize)

public:

EditorInputCharactor(QWidget* parent, QPoint point, QString str, int position, QFont font, int delay = 0, int duration = 200);

int getPosition(); // 获取动画的文字位置,更新位置、动画结束文字变回原色时要用到

void changePosition(int x); // 修改动画的位置

void updateRect(QPoint point); // 更新控件坐标

// 字体动画接口

void setFontSize(int x);

int getFontSize();

void resetFontSize();

signals:

void aniFinished(int position, EditorInputCharactor* charactor); // 动画结束

private:

QPoint point; // 坐标

QString str;

int position; // 文字位置

int duration; // 动画时长,同时输入的不同位置的文字时长不一样

QPropertyAnimation *ani; // 动画对象

int origin_position; // 一开始的位置

int font_size; // 文字大小动画属性

int font_size_l;

};

平滑光标

平滑光标添加一个矩形控件为光标样式,每次改变时存储其坐标,然后借助动画移动控件。

其实还简单,但难点在要好好控制什么时候才开始动画,不然光标会乱飘或者闪动,看起来效果很差。

最大的难点在于,与功能“光标行固定”以及“底部光标固定”有很大的冲突,还有每次修改文字时(这是自定义事件经常使用QTextEdit.setText()方法才会出现的问题)需要大大约束光标动画的时机。

4、MVD目录结构

Qt标准 QListView 的MVD模型,一开始以为很难,刚走来发现其实就是重写几个方法。

这里使用的 MVD似乎与标准的不太一样,看下去就知道了。

Model

由于写作天下的目录比较复杂,所以专门写了一个类NovelDirMData来存放多个NovelDirItem的信息。

此处 Model 用到了多继承,以及友元。(其实这个模型已经变了味了)

class NovelDirModel : public QAbstractListModel, public NovelDirMData

{

/*...其余代码略...*/

QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const

{

// 不需要 index 的 data

if (role == Qt::UserRole+DRole_ROLL_COUNT)

{

return roll_subs.size();

}

else if (role == Qt::UserRole+DRole_CR_COUNT)

{

return cr_list.size();

}

else if (role == Qt::UserRole+DRole_ROLL_NAMES)

{

return roll_names;

}

// 判断 index

if ((!index.isValid()) || (index.row() >= cr_list.size()))

return QVariant();

NovelDirItem item = cr_list.at(index.row());

if (role == Qt::UserRole+DRole_CHP_NAME)

{

return item.getName(); // 章节名

}

else if (role == Qt::UserRole+DRole_CHP_NUM)

{

return item.getNumber(); // 序号

}

else if (role == Qt::UserRole+DRole_CHP_NUM_CN)

{

return item.getNumber_cn(); // 序号

}

else if (role == Qt::UserRole+DRole_CHP_ROLLINDEX)

{

return item.getRollIndex(); // 序号

}

else if (role == Qt::UserRole+DRole_CHP_CHPTINDEX)

{

return item.getChptIndex(); // 序号

}

else if (role == Qt::UserRole+DRole_CHP_DETAIL)

{

return item.getDetail(); // 细纲

}

else if (role == Qt::UserRole+DRole_CHP_OPENING)

{

return item.isOpening(); // 是否编辑中

}

else if (role == Qt::UserRole+DRole_CHP_ISROLL)

{

return item.isRoll(); // 是否为分卷

}

else if (role == Qt::UserRole+DRole_CHP_ISHIDE)

{

return item.isHide(); // 是否隐藏

}

else if (role == Qt::UserRole+DRole_CHP_STEMP)

{

return item.getS_temp(); // 双击编辑,出错后恢复原来的文本

}

else if (role == Qt::UserRole+DRole_ROLL_SUBS)

{

int rIndex = item.getRollIndex();

return roll_subs[rIndex]; // 分卷章数量

}

else if (role == Qt::UserRole+DRole_ROLL_COUNT)

{

return roll_subs.size();

}

else if (role == Qt::UserRole+DRole_CR_COUNT)

{

return cr_list.size();

}

else if (role == Qt::UserRole+DRole_CHP_FULLNAME)

{

return item.getFullChapterName();

}

else if (role == Qt::UserRole+DRole_RC_ANIMATING)

{

return item.isAnimating();

}

else if (role == Qt::UserRole+DRole_ROLL_NAMES)

{

return roll_names;

}

else if (role == Qt::UserRole+DRole_RC_SELECTING)

{

return item.isSelecting();

}

return QVariant();

}

// 重写 flags 和 setData 使 Model 可双击编辑

Qt::ItemFlags flags(const QModelIndex &index) const

{

Qt::ItemFlags flags = QAbstractItemModel::flags(index);

//if (index.row() > 0 && us->one_click) // 作品相关卷名不允许更改

//flags |= Qt::ItemIsEditable;

if (index.row() > 0)

flags |= Qt::ItemIsEditable;

return flags;

}

bool setData(const QModelIndex &index, const QVariant &value, int role)

{

if (!index.isValid()) return false;

NovelDirItem item = cr_list.at(index.row());

if (role == Qt::EditRole) // 修改章节名

{

QString old_name = item.getName();

bool isNew = false;

if (canRegExp(old_name, "新[章卷]\\d+")) // 分卷是聚集焦点用

isNew = true;

bool rst = tryChangeName(index.row(), value.toString());

if (/*isNew && */rst) // 重命名成功

{

if (isNew) // 如果是新建的

emit signalOpenChapter(index.row());

else // 否则就是重命名,修改已经打开的旧名字标签页

emit signalChangeFullName(novel_name, value.toString());

}

return rst;

}

else if (role == Qt::UserRole+DRole_CHP_STEMP)

{

cr_list[index.row()].setS_temp(value.toString());

}

else if (role == Qt::UserRole+DRole_CHP_FULLNAME)

{

cr_list[index.row()].setFullChapterName(value.toString());

deb(value.toString(), "model.setFullChapterName");

}

return true;

}

}

View

这里重写的方法有点多……就放一个设置Model和Delegate的。

不是很标准,因为为了方便,把应该解耦的Model和Delegate都声明为成员变量了。

// 设置 delegate

novel_dir_delegate = new NovelDirDelegate(this);

setItemDelegate(novel_dir_delegate);

// 设置 model

novel_dir_model = new NovelDirModel(this);

setModel(novel_dir_model);

Delegate

因为用到了作品设置,有用户自定义的完全不同的目录结构,所以和Model一样使用了多继承。

有三大特色:

用户自定义文字显示:例如,阿拉伯数字自动排序、不显示分卷等

圆形显示效果:不知道该怎么描述……至少要比默认的蓝色选中效果好看吧?

支持全局主题色/点缀色,实时修改

/**

* 目录列表代理类

*/

class NovelDirDelegate : public QItemDelegate, public NovelDirSettings

{

/* ... 略了一些 ... */

QString getItemText(const QModelIndex &index) const

{ /*...获取章节序号,略...*/}

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

{

QStyleOptionViewItem op(option);

op.palette.setColor(QPalette::Highlight, us->getOpacityColor(us->accent_color, 128));

painter->save();

// 获取文字

QString text= getItemText(index);

int deviate = DIR_DEVIATE; // 初始偏移位置

int subs = -1; // 分卷数量,章节为-1

if (index.data(Qt::UserRole+DRole_CHP_ISROLL).toBool()) // 是分卷,画数字

{

subs = index.data(Qt::UserRole+DRole_ROLL_SUBS).toInt();

}

else // 是章节

{

if (isNoRoll() && index.data(Qt::UserRole+DRole_CHP_NUM).toInt() > 0) // 不显示分卷 且 是正文,则取消缩进

;

else

deviate += DIR_DEVIATE_ADD; // 增加章节的缩进

}

// 获取文字区域并绘制

QFontMetrics fm(painter->font());

QRect text_rect = QRect(op.rect.topLeft()+QPoint(deviate,(op.decorationSize.height()+us->mainwin_sidebar_spacing*2-fm.height())/2),QSize(fm.width(text),fm.height()));

if (!us->round_view)

drawBackground(painter, op, index); // 画选中的颜色的(不加这行的话就是透明选中)

// 先绘制背景

if (!index.data(Qt::UserRole+DRole_RC_ANIMATING).toBool()) // 不是在动画中(动画时不显示文字,即背景透明)

{

// 绘制选中状态的纸片形状背景

if (us->round_view && (option.state & QStyle::State_Selected))

{

int text_padding = text_rect.height()/2-1;

QColor bg_color = us->getOpacityColor(us->accent_color, 128);

QRect round_rect(text_rect.left()-text_padding, text_rect.top()-3, text_rect.width()+text_padding*2, text_rect.height()+6);

painter->setRenderHint(QPainter::Antialiasing);

QPen pen(bg_color, Qt::NoPen);

painter->setPen(pen);

QPainterPath path;

int radius = round_rect.height()/2-1;

path.addRoundedRect(round_rect, radius, radius);

painter->fillPath(path, bg_color);

painter->drawPath(path);

painter->setRenderHint(QPainter::Antialiasing, false);

}

// 绘制文字

if (isNoRoll() && subs >= 0) // 不使用分卷,并且刚好是分卷,则使用灰色,弱化分卷存在感

{

painter->setPen(QColor(128, 128, 128));

}

else

painter->setPen(QColor(0, 0, 0));

painter->drawText(text_rect, text);

// 画左边的小标记

if (subs >= 0) // 是分卷(章节默认 -1)

{

}

else // 是章节,绘制打开状态

{

if (index.data(Qt::UserRole+DRole_CHP_OPENING).toBool()) // 打开状态中,画右边的小点

{

QRect state_rect = QRect(op.rect.topLeft()+QPoint(0,1),QSize(1,op.rect.height()-2));

painter->setPen(QPen(QColor(us->accent_color), Qt::SolidLine));

painter->setBrush(QBrush(QColor(us->accent_color), Qt::SolidPattern));

painter->drawRect(state_rect);

}

}

// 画右边的小标记,分卷数量

if (subs >= 0) // 是分卷(章节默认 -1)

{

if (subs == 0) // 没有章节,灰色的数字

{

painter->setPen(QColor(128, 128, 128));

}

else // 章节,黑偏灰色的

{

painter->setPen(QColor(64, 64, 64));

}

QString subs_text = QString("%1").arg(subs);

QRect display_rect2 = QRect(op.rect.topRight()-QPoint(fm.width(subs_text)+10,-3),QSize(fm.width(subs_text),fm.height()));

painter->drawText(display_rect2, subs_text);

}

}

// drawFocus(painter, option, displayRect); // 画选区虚线

drawFocus(painter, op, QRect(0, 0, 0, 0));

painter->restore();

}

QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const

{

Q_UNUSED(index);

//QSize size = option.rect.size(); // 这个是整个QListView的矩形……

QSize size = option.decorationSize;

size.setHeight(size.height()+us->mainwin_sidebar_spacing*2);

return size;//QItemDelegate::sizeHint(o2, index);

}

/* 编辑框修改名字 */

QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex&index) const

{

Q_UNUSED(option);

Q_UNUSED(index);

QLineEdit* edit = new QLineEdit(parent);

edit->setAcceptDrops(false);

QString style = "";//"background-color:"+us->getColorString(us->accent_color)+";";

if (us->round_view)

{

QString text= getItemText(index);

QFontMetrics fm(edit->font());

int r = fm.height()/2;

QString rs = QString("%1").arg(r);

style += "border:1px;border-radius:"+rs+"px; padding-left: "+rs+"px;";

}

else

style += "border:none;";

edit->setStyleSheet(style);

QPalette palette = edit->palette();

if (us->editor_font_color.alpha() > 0)

palette.setColor(QPalette::Text, us->editor_font_color);

palette.setColor(QPalette::Base, us->accent_color/*editor_bg_color*/);

if (us->editor_font_selection.alpha() > 0)

palette.setColor(QPalette::HighlightedText, us->editor_font_selection);

palette.setColor(QPalette::Highlight, us->editor_bg_selection);

edit->setPalette(palette);

return edit;

}

void setEditorData(QWidget *editor, const QModelIndex &index) const

{

QString name = index.data(Qt::UserRole+DRole_CHP_NAME).toString();

if (index.data(Qt::UserRole+DRole_CHP_STEMP).toString() != "")

name = index.data(Qt::UserRole+DRole_CHP_STEMP).toString();

QLineEdit* edit = static_cast(editor);

// renameEdit = edit;

edit->setText(name);

}

void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const

{

QLineEdit* edit = static_cast(editor);

QString str = edit->text();

//closeEditor(edit, QAbstractItemDelegate::NoHint);

if (model->setData(index, str))

model->setData(index, str, Qt::UserRole+DRole_CHP_STEMP);

else

model->setData(index, str, Qt::UserRole+DRole_CHP_STEMP);

}

void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const

{

Q_UNUSED(index);

int deviate = DIR_DEVIATE-2;

if (!(index.data(Qt::UserRole+DRole_CHP_ISROLL).toBool()))

deviate += DIR_DEVIATE_ADD;

if (us->round_view)

{

QString text= getItemText(index);

QFontMetrics fm(editor->font());

int delta = fm.height()/2;

deviate -= delta;

}

QRect rect = option.rect;

rect.setLeft(rect.left()+deviate);

editor->setGeometry(rect);

}

QLineEdit* getEditor()

{

return rename_edit;

}

private :

QLineEdit* rename_edit;

};

使用这个代理后,发现效果还是挺好看的,比原先不知道好了多少。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Linux内核》课本读书笔记 第三章

转载于:https://www.cnblogs.com/bushifudongjing/p/5339017.html

android 动态切换aar_Android应用开发之动态更改AndroidManifest.xml中节点属性操作教程...

本文将带你了解Android应用开发之动态更改AndroidManifest.xml中节点属性操作教程&#xff0c;希望本文对大家学Android有所帮助。在Android使用Gradle进行编译打包时&#xff0c;有时候需要动态更改AndroidManifest.xml中application、activity等节点属性&#xff0c;大多数情…

ANSYS Workbench对称建模

ANSYS Workbench对称建模 &#xff08;使用版本为ANSYS 2020R2&#xff09; 一、循环对称建模 1、建立三维模型。拖出一个静力学分析模块&#xff0c;材料保持系统默认&#xff0c;在Design Modeler中建立圆盘的1/4模型。外圆半径20m&#xff0c;内圆半径10m&#xff0c;厚度5m…

毕业论文排版之Word 中公式居中,编号靠右该怎么设置(针对左右不对称页边距)

首先感谢博主这篇&#xff0c;大家基本可以参照这个&#xff1a; http://t.csdn.cn/s0FY7&#xff1b; http://t.csdn.cn/FjmbH&#xff1b; 但是由于博主的是规规整整的页面边距&#xff0c;一篇为上下左右均为2cm&#xff0c;另一篇上下2.54cm左右3.18cm。ps:如果你为以上的页…

python opencv报错_OpenCV in Python 入门问题,python报错 -问答-阿里云开发者社区-阿里云...

OpenCV是Intel开源计算机视觉库。它由一系列 C 函数和少量 C 类构成&#xff0c;实现了图像处理和计算机视觉方面的很多通用算法。在这篇文章(译自 http://glowingpython.blogspot.com/2011/10/beginning-with-opencv-in-python.html) 中将介绍如何使用 Python 版的 OpenCV。 下…

workbench出现“Unable to start the geometry editor”

今天上午打开workbench遇到这个“Unable to start the geometry editor”问题&#xff0c;但是重启软件还是没用&#xff0c;所以网上找了下。 方法1、来源百度——大向日葵爱太阳&#xff1a; 我刚出现这样的问题&#xff0c;解答办法是将你安装主程序下的破解文件的路径改一…

ant4 多个form 验证_爬虫遇到头疼的验证码?Python实战讲解弹窗处理和验证码识别...

点击上方“早起Python”&#xff0c;关注并“星标”每日接收Python干货&#xff01;本文含 3321 字&#xff0c;9代码片段建议阅读 8 分钟前言 在我们写爬虫的过程中&#xff0c;目标网站常见的干扰手段就是设置验证码等&#xff0c;本就将基于Selenium实战讲解如何处理弹窗和验…

如何写计算机会议的rebuttal

其实最好的教材就是实例&#xff0c;恰好NIPS会议会把往年所有论文的Rebuttal都贴出来。。。&#xff0c;见这里&#xff1a;http://papers.nips.cc/ 同时&#xff0c;圈内同行也总结了不少经验&#xff0c;下面转帖其他人的经验 如下转自&#xff1a;http://qiyuhua.github.io…

一建机电实务教材电子版_20年一建其实并不难,官方出版:复习题集(精修),速做速提90分...

20年一建其实并不难&#xff0c;官方出版&#xff1a;复习题集&#xff08;精修&#xff09;&#xff0c;速做速提90分一建法规管理经济建筑市政机电水利等根据《一-级建造师执业资格考试大纲》(2018 年版) 和《2020年版全国一级建造师执业资格考试用书》&#xff0c;组织全国著…

C#在dataGridView中遍历,寻找相同的数据并定位

1、 C#在dataGridView中遍历&#xff0c;寻找相同的数据并定位 [c-sharp] view plain copy int row dataGridView1.Rows.Count;//得到总行数 int celldataGridView1.Rows[1].Cells.Count;//得到总列数 for (int i 0; i < row; i)//得到总行数并在之内循环 { for (int j 0…

pcb板子开窗_PCB 层定义

PCB 层定义在EDA软件的专门术语中&#xff0c;有很多不是有相同定义的。以下就字面上可能的意义来解释。 Mechnical: 一般多指板型机械加工尺寸标注层 。Keepoutlayer: 定义不能走线、打穿孔(via)或摆零件的区域。这几个限制可以独立分开定义。 Topoverlay: 无法从字面得知其意…

上的img表示什么_方向盘上的SET、RES和CNL,表示什么意思?别弄错了

很多人买车都希望能买到一款称心如意的车型&#xff0c;包括价格、配置、性能以及质量都符合自己的需求。当然&#xff0c;不管是什么东西都不可能做到完美&#xff0c;就拿汽车来说&#xff0c;即便是几百万上千万的豪车也是有缺点的&#xff0c;更何况普通的十几万的家用车。…

Mysql编辑工具中使用(Navicat查询结果显示行号)

Mysql编辑工具中使用(Navicat查询结果显示行号) select rownum:rownum1 as rownum,a.roleId from base_userroles a,(select rownum:0) t where a.roleIdadmin; 转载于:https://www.cnblogs.com/xrhou12326/p/5345522.html

python的web可视化_Python的Web可视化框架Dash(8)---核心组件

本节介绍Dash应用的常用核心组件&#xff0c;导入本节用到的所有包import pandas as pdimport plotly.graph_objs as goimport dashimport dash_core_components as dcc # 交互式组件import dash_html_components as html # 代码转htmlfrom dash.dependencies import Input, Ou…

【Android 应用开发】Activity 状态保存 OnSaveInstanceState參数解析

作者 : 韩曙亮转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38297083一. 相关方法简单介绍1. 状态保存方法演示样例package com.example.octopus_saveinstance;import android.app.Activity; import android.os.Bundle;public class MainActivity exten…

python 机器视觉测量_用Opencv python实现精密测量

实际上&#xff0c;我正在使用OpenCV和Python开发一个机器视觉项目。目标&#xff1a;项目的目标是以高精度测量组件的尺寸。主要硬件&#xff1a;巴斯勒5MP照相机(aca-2500-14gm)一个红色背光灯(100毫米x 100毫米)(我的组件大小约为60毫米)实验因为我正在考虑非常严格的公差限…

ireport 循环_ireport5.6.0分组显示

一&#xff0c;ireport中分组二&#xff0c;java调用实现分组一&#xff0c;ireport中分组&#xff1a;1&#xff0c;新建模板文件&#xff0c;纸张随意&#xff0c;名称随意&#xff0c;路径随意2&#xff0c;连接要分组的数据源3&#xff0c;添加测试表和数据CREATE TABLEyan…

数学题 HDOJ——2086 简单归纳

哎 真的是懒得动脑子还是怎么滴。。。 题目如下 Problem Description有如下方程&#xff1a;Ai (Ai-1 Ai1)/2 - Ci (i 1, 2, 3, .... n).若给出A0, An1, 和 C1, C2, .....Cn.请编程计算A1 ?参考网上题解。。。因为&#xff1a;Ai(Ai-1Ai1)/2 - Ci, A1(A0 A2 )/2 - C1;A2…

python正则匹配html标签_Python正则获取、过滤或者替换HTML标签的方法

本文实例介绍了Python通过正则表达式获取,去除(过滤)或者替换HTML标签的几种方法&#xff0c;具体内容如下python正则表达式关键内容:python正则表达式转义符:. 匹配除换行符以外的任意字符\w 匹配字母或数字或下划线或汉字\s 匹配任意的空白符\d 匹配数字\b 匹配单词的开始或结…

海选女主角

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 40591 Accepted Submission(s): 18214 Problem Descriptionpotato老师尽管非常喜欢教书&#xff0c;可是迫于生活压力。不得不想办法在业余时间挣点外快以养家糊…