Gtk3丰富text widget机制(基于GtkTextBuffer& GtkTextView)同时包含"begin-user-action"和"end-user-actions"信号,可以方便地快速处理用户输入(并将其与 application 生成的对缓冲区或视图的更改区分开来。)
但看起来Qt5中没有类似的东西。例如,我的不完全理解是,QTextEdit::insertHtml或QTextDocument::contentsChange或QTextDocument::contentsChanged不会将与用户输入(键盘或粘贴等)相关的更改与由应用
我想到的是一些面向语法的编辑器。
我可能误解了Qt5富文本编辑器的支持。
(对于好奇的人:我正在重新设计并使用C& GTK重新实现我的MELT monitor与C ++ 11& Qt5暂时称为Basixmo;所有都是免费的GPL软件,但我还没有编写Qt5的东西)
我有一个窗口,其中包含一个按钮say
和一个QTextEdit
作为其中心窗口小部件。当我单击按钮时,文档中插入了"hello"
字符串,我称之为应用程序更改(您可以想象该按钮被与用户无关的内容替换,例如一些网络输入)。当我在文本编辑器中键入一些按键时,还会从该用户操作更改中插入一些字符串。我想区分两者。
#include <QApplication>
#include <QMainWindow>
#include <QTextEdit>
#include <QToolBar>
#include <fstream>
#include <iostream>
int main(int argc, char**argv)
{
QApplication app(argc, argv);
auto win = new QMainWindow;
auto tb = win->addToolBar("Basile example");
auto ted = new QTextEdit;
win->setCentralWidget(ted);
tb->addAction("say",[=]{ted->insertPlainText("hello");});
auto doc = ted->document();
QObject::connect(doc,&QTextDocument::contentsChange,
[=](int pos, int removedchars, int addedchars)
{ std::clog << "contentChange: pos=" << pos
<< " removedchars=" << removedchars
<< " addedchars=" << addedchars
<< std::endl; });
win->show();
app.exec();
delete win;
}
//// Local Variables:
//// compile-command: "g++ -std=c++11 -Wall -g $(pkg-config --cflags Qt5Core Qt5Widgets Qt5Gui) -fPIC $(pkg-config --libs Qt5Core Qt5Widgets Qt5Gui) -o eqb eqb.cc"
//// End:
但是当我运行上面的代码时,会触发contentsChange
信号(连接到我的lambda函数输出到std::clog
)以进行用户操作更改和应用程序更改。
我不关心在QTextEdit
,QTextDocument
或QTextCursor
级别工作,但我想将用户操作更改分开(输入QTextEdit
从应用程序更改中使用鼠标单击和菜单等粘贴小部件或粘贴。我想避免在小部件事件级别工作(例如,在我自己的类中重新定义QWidget::keyPressEvent
的虚拟成员函数,等等),特别是因为我不确定知道 all 影响QTextEdit
实例的可能事件。
emacs
一样可编写脚本的编辑器,在C ++ 11中使用真正的 Qt5编码风格和小部件(当然,通过嵌入一些可编写脚本的解释器来代替Guile)。
PS。如果这很重要,我的桌面系统运行Debian / testing / x86-64。 Qt是5.6.1版,我的代码是由GCC 6.2编译的;编译命令在最后一个广泛的评论中。
答案 0 :(得分:3)
在您的特定情况下,您感兴趣的信号将发送到属于QTextDocument
的{{1}}对象。
您可以在lambda中使用QTextEdit
来阻止发出这些信号:
QSignalBlocker
这样,当应用程序更改文档的上下文时,您将不再收到#include <QSignalBlocker>
// ...
tb->addAction("say", [=]{
const QSignalBlocker blocker{ted->document()};
ted->insertPlainText("hello");
});
信号。
有关详细信息,请参阅official documentation。
请注意,contentsChange
上的信号不会被禁止
它们可能绑定到QTextEdit
并传播(如果您对这些细节感兴趣,可以查看该类的代码)。
如果要区分用户更改和应用程序更改,现在可以扩展类并添加要从lambda中发出的自定义信号(例如QTextDocument
)。
否则,直接打电话给所有人。
做什么主要取决于具体问题以及软件的设计方式。
作为附注并在评论中提到,更多细节可以更好地解释其工作原理
internalContentChange
(我说)转发 QTextEdit
请求到内部insertPlainText
。因此,后者发出一个信号(再次,让我说)被QTextDocument
捕获并传播。
这就是为什么上述技巧通过抑制内部类别的信号在特定情况下很好地适用的原因。换句话说,QTextEdit
都有自己的信号,并作为其组件发出的某些信号(主要是QTextEdit
)的重发射器。
为了清晰起见,我试图保持简单,但您可以通过查看存在的类here(公共存储库)来跟踪请求和信号的流程。
特别是,here是QTextDocument
组件将QTextEdit
调用转发给内部QTextEditControl
组件的位置,其本身就是insertPlainText
和{{3} }到内部光标......等等
这里需要对此进行全面分析。
答案 1 :(得分:0)
与@skypjack's answer略有不同的方法可能是标记应用程序更改而不是阻止它们:
bool application_change = false;
tb->addAction("say",
[=, &application_change]{
application_change = true;
ted->insertPlainText("hello");
application_change = false;
}
);
// ...
QObject::connect(doc, &QTextDocument::contentsChange,
[=, &application_change](int pos, int removedchars, int addedchars){
std::clog << "contentChange: pos=" << pos
<< " removedchars=" << removedchars
<< " addedchars=" << addedchars
<< std::endl;
if(application_change)
std::clog << "this is an application change" << std::endl;
}
);
application_change
上的开关可以包装在储物柜中:
struct BoolLocker
{
bool& locked;
BoolLocker(bool& to_lock_):
locked(to_lock_)
{
locked = true;
}
~BoolLocker(bool& to_lock_)
{
locked = false;
}
};
// ...
bool application_change = false;
tb->addAction("say",
[=, &application_change]{
BoolLocker locker(application_change);
ted->insertPlainText("hello");
}
);
或通过工厂函数在包装lambda中生成:
bool application_change = false;
template<typename F>
auto make_application_change(F&& f)
{
return [&f]{
application_change = true;
f();
application_change = false;
};
}
// ...
tb->addAction("say",
make_application_change(
[=]{
ted->insertPlainText("hello");
}
)
);
请注意,此实现是一个基本示例,应在实际用例中进行调整。例如(如注释中所指出的),多线程应用程序中可能存在问题。