Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I am working on a QtQuick 2.0 presentation and I would like to embed some code samples. is it possible easily to create a syntax highlighting QML element.
The objective in question is to apply syntax highlighting to a TextEdit or a TextArea. I know Qt has a QSyntaxHighlighter, but the documentation is not very clear on how exactly to apply it to a TextEdit or a TextArea.
Can you provide examples on how it's done?
Qt Quick's TextEdit item exposes a textDocument property, of type QQuickTextDocument. This is explicitly exposed so you can use QSyntaxHighlighter directly with the document.
QtQuick textEdit documentation for Qt 5.3
I have two answers:
a pure QML answer
a C++ answer involving QSyntaxHighlighter
For a pure QML answer, we can use a TextArea one can set TextArea::textFormat to textFormat: TextEdit.RichText for formatting. We can use TextArea::getText() to get the plain text and set TextArea::text with the rich text. Here's a mock example that:
color uppercase identifiers (e.g. Button) as purple
color lowercase identifiers (e.g. x y z) as red
color numbers (e.g. 123 456) as blue
but leave symbols (e.g. = + ;) as black
Here's the snippet:
TextArea {
id: output
property bool processing: false
text: "<p>x = 123;</p><p>y = 456;</p><p>z = x + y;</p>"
textFormat: TextEdit.RichText
selectByMouse: true
onTextChanged: {
if (!processing) {
processing = true;
let p = cursorPosition;
let markUp = getText(0, length).replace(
/([A-Z][A-Za-z]*|[a-z][A-Za-z]*|[0-9]+|[ \t\n]|['][^']*[']|[^A-Za-z0-9\t\n ])/g,
function (f) {
console.log("f: ", JSON.stringify(f));
if (f.match(/^[A-Z][A-Za-z]*$/))
return "<span style='color:#800080'>" + f + "</span>";
if (f.match(/^[a-z][A-Za-z]*$/))
return "<span style='color:#800000'>" + f + "</span>";
else if (f.match(/^[0-9]+$/))
return "<span style='color:#0000ff'>" + f + "</span>";
else if (f.match(/^[ ]/))
return " "
else if (f.match(/^[\t\n]/))
return f;
else if (f.match(/^[']/))
return "<span style='color:#008000'>" + f + "</span>";
else
return f;
}
);
text = markUp;
cursorPosition = p;
processing = false;
}
}
}
To use Qt's QSyntaxHighlighter, you need the following:
In QML, use TextEdit QML type in your application
In C++, define a QSyntaxHighlighter and connect TextEdit QML type to it via the TextEdit::textDocument property
In C++, implement QSyntaxHighlighter::highlightBlock( const QString& text ) in your derived class, calling QSyntaxHighlighter::setFormat() as often as needed to tokenize the text found.
To make things easier, I create a sample app https://github.com/stephenquan/QtSyntaxHighlighterApp which wraps QSyntaxHighlighter and QTextFormat as SyntaxHighlighter and TextFormat QML Types. That way, one can handle onHighlightBlock signal and put the business logic of the syntax highlighter in Javascript instead of C++:
TextEdit {
id: textEdit
selectByMouse: true
text: [
"import QtQuick 2.12",
"",
"Item {",
" Rectangle {",
" width: 50",
" height: 50",
" color: '#800000'",
" }",
"}",
].join("\n") + "\n"
font.pointSize: 12
}
SyntaxHighlighter {
id: syntaxHighlighter
textDocument: textEdit.textDocument
onHighlightBlock: {
let rx = /\/\/.*|[A-Za-z.]+(\s*:)?|\d+(.\d*)?|'[^']*?'|"[^"]*?"/g;
let m;
while ( ( m = rx.exec(text) ) !== null ) {
if (m[0].match(/^\/\/.*/)) {
setFormat(m.index, m[0].length, commentFormat);
continue;
}
if (m[0].match(/^[a-z][A-Za-z.]*\s*:/)) {
setFormat(m.index, m[0].match(/^[a-z][A-Za-z.]*/)[0].length, propertyFormat);
continue;
}
if (m[0].match(/^[a-z]/)) {
let keywords = [ 'import', 'function', 'bool', 'var',
'int', 'string', 'let', 'const', 'property',
'if', 'continue', 'for', 'break', 'while',
];
if (keywords.includes(m[0])) {
setFormat(m.index, m[0].length, keywordFormat);
continue;
}
continue;
}
if (m[0].match(/^[A-Z]/)) {
setFormat(m.index, m[0].length, componentFormat);
continue;
}
if (m[0].match(/^\d/)) {
setFormat(m.index, m[0].length, numberFormat);
continue;
}
if (m[0].match(/^'/)) {
setFormat(m.index, m[0].length, stringFormat);
continue;
}
if (m[0].match(/^"/)) {
setFormat(m.index, m[0].length, stringFormat);
continue;
}
}
}
}
TextCharFormat { id: keywordFormat; foreground: "#808000" }
TextCharFormat { id: componentFormat; foreground: "#aa00aa"; font.pointSize: 12; font.bold: true; font.italic: true }
TextCharFormat { id: numberFormat; foreground: "#0055af" }
TextCharFormat { id: propertyFormat; foreground: "#800000" }
TextCharFormat { id: stringFormat; foreground: "green" }
TextCharFormat { id: commentFormat; foreground: "green" }
There is no obvious way to achieve syntax highlighting in QML.
One could implement one's own declarative item, performing the actual highlighting with QSyntaxHighlighter but then one would have to define its own highlighting rules for language of the source code in question. I would't do that amount of coding for a presentation.
Instead I would display the code in a WebView item with the highlighting already applied as static HTML markup or with the help of a JavaScript highlighting library, for expample highlight.js.
Update 1
If the WebView item is indeed unusable, even the simple Text item with its rudimentary HTML support should be enough to handle the source code highlighting usecase if fed with static HTML.
in your app file:
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQuickTextDocument* doc = childObject<QQuickTextDocument*>(engine, "textEditor", "textDocument");
Q_ASSERT(doc != 0);
// QSyntaxHighlighter derrived class
MySyntaxHighlighter* parser = new MySyntaxHighlighter(doc->textDocument());
// use parser, see QSyntaxHighlighter doc...
int ret = app.exec();
delete parser;
return ret;
The template function to get child objects (returns the first occurence of objectName, so use unique names to identify objects in your qml files) :
template <class T> T childObject(QQmlApplicationEngine& engine,
const QString& objectName,
const QString& propertyName)
{
QList<QObject*> rootObjects = engine.rootObjects();
foreach (QObject* object, rootObjects)
{
QObject* child = object->findChild<QObject*>(objectName);
if (child != 0)
{
std::string s = propertyName.toStdString();
QObject* object = child->property(s.c_str()).value<QObject*>();
Q_ASSERT(object != 0);
T prop = dynamic_cast<T>(object);
Q_ASSERT(prop != 0);
return prop;
}
}
return (T) 0;
}
in your qml file use a TextEdit (inside a Flickable or whatever you want) with the objectName property correctly set:
....
TextEdit {
id: edit
objectName: "textEditor"
width: flick.width
height: flick.height
focus: true
font.family: "Courier New"
font.pointSize: 12
wrapMode: TextEdit.NoWrap
onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
}
....
Take a look at QSyntaxHighlighter.
If you need a QML Item doing syntax highlighting, you can simply create your own by extending QDeclarativeItem and using the utility above.
Related
I have a plugin for my ckeditor build which should convert pasted content with formulas,
separated by '(' ')', '$$' etc. into math-formulas from ckeditor5-math (https://github.com/isaul32/ckeditor5-math). I changed the AutoMath Plugin so that it supports text with the separators.
I have run into a problem where undoing (ctrl-z) the operation works fine for single-line content, but not for multiline content.
To reproduce the issue, I have built a similar plugin which does not require the math plugin. This plugin converts text enclosed by '&' to bold text.
To reproduce this issue with an editor instance it is required to have the cursor inside a word (not after or before the end of the text, I don't know why that doesn't work, if you know why, help is appreciated^^) and paste it from the clipboard. The content will inside the '&' will be marked bold, however if you undo this operation twice, an model-position-path-incorrect-format error will be thrown.
example to paste:
aa &bb& cc
dd
ee &ff& gg
Undoing the operation twice results in this error:
Uncaught CKEditorError: model-position-path-incorrect-format {"path":[]}
Read more: https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html#error-model-position-path-incorrect-form
Unfortunately, I haven't found a way to fix this issue, and have not found a similar issue.
I know it has to do with the batches that are operated, and that maybe the position parent has to do something with it, that I should cache the position of the parent. However, I do not know how.
Below my code for an example to reproduce:
import Plugin from '#ckeditor/ckeditor5-core/src/plugin';
import Undo from '#ckeditor/ckeditor5-undo/src/undo';
import LiveRange from '#ckeditor/ckeditor5-engine/src/model/liverange';
import LivePosition from '#ckeditor/ckeditor5-engine/src/model/liveposition';
import global from '#ckeditor/ckeditor5-utils/src/dom/global';
export default class Test extends Plugin {
static get requires() {
return [Undo];
}
static get pluginName() {
return 'Test';
}
constructor(editor) {
super(editor);
this._timeoutId = null;
this._positionToInsert = null;
}
init() {
const editor = this.editor;
const modelDocument = editor.model.document;
const view = editor.editing.view;
//change < Clipboard > to < 'ClipboardPipeline' > because in version upgrade from 26 to 27
//the usage of this call changed
this.listenTo(editor.plugins.get('ClipboardPipeline'), 'inputTransformation', (evt, data) => {
const firstRange = modelDocument.selection.getFirstRange();
const leftLivePosition = LivePosition.fromPosition(firstRange.start);
leftLivePosition.stickiness = 'toPrevious';
const rightLivePosition = LivePosition.fromPosition(firstRange.end);
rightLivePosition.stickiness = 'toNext';
modelDocument.once('change:data', () => {
this._boldBetweenPositions(leftLivePosition, rightLivePosition);
leftLivePosition.detach();
rightLivePosition.detach();
}, {priority: 'high'});
});
editor.commands.get('undo').on('execute', () => {
if (this._timeoutId) {
global.window.clearTimeout(this._timeoutId);
this._timeoutId = null;
}
}, {priority: 'high'});
}
_boldBetweenPositions(leftPosition, rightPosition) {
const editor = this.editor;
const equationRange = new LiveRange(leftPosition, rightPosition);
// With timeout user can undo conversation if wants to use plain text
this._timeoutId = global.window.setTimeout(() => {
this._timeoutId = null;
let walker = equationRange.getWalker({ignoreElementEnd: true});
let nodeArray = [];
for (const node of walker) { // remember nodes, because when they are changed model-textproxy-wrong-length error occurs
nodeArray.push(node);
}
editor.model.change(writer => {
for (let node of nodeArray) {
let text = node.item.data;
if (node.item.is('$textProxy') && text !== undefined && text.match(/&/g)) {
let finishedFormulas = this._split(text);
const realRange = writer.createRange(node.previousPosition, node.nextPosition);
writer.remove(realRange);
for (let i = finishedFormulas.length - 1; i >= 0; i--) {
if (i % 2 === 0) {
writer.insertText(finishedFormulas[i], node.previousPosition);
} else {
writer.insertText(finishedFormulas[i], {bold: true}, node.previousPosition);
}
}
}
}
});
}, 100);
}
_split(text) {
let mathFormsAndText = text.split(/(&)/g);
let mathTextArray = [];
for (let i = 0; i < mathFormsAndText.length; i++) {
if (i % 4 === 0) {
mathTextArray.push(mathFormsAndText[i]);
} else if (i % 2 === 0) {
mathTextArray.push(mathFormsAndText[i]);
}
}
return mathTextArray;
}
}
Let me know if I can clarify anything.
This question involves Qt but could be pure C++ problem with my logic.
I am adding QQuickItems to an std::map store info about a list of QQuickItems & their respective parents.
The code:
std::array<std::string, 2> ObjectNamesArray = { "quickitemObj1", "quickitemObj2" };
std::map<QQuickItem*, QQuickItem*> items;
for(const auto& quickitem: ObjectNamesArray) {
QQuickItem * item = Qmlengine->rootObjects()[0]->findChild<QQuickItem*>(quickitem.c_str());
if (item != NULL)
items.insert(std::make_pair(item, item->parent());
// for a test, following works fine with the item pointer within this loop
qreal width ? item->width();
}
Debugging through above loop, the items map shows zero items with the tag not accessible.
Iterating over the map again like this.
std::map<QQuickItem*, QQuickItem*>::iterator it = items.begin();
while (it != items.end()) {
QQuickItem* item = it->first;
QQuickItem * itemParent = it->second; // crashes here as *item is null
it++;
}
Problem:
But, when I try to iterate through the map, there are no valid pointers to my QQuickItems. In fact looks like there are not items added to the map.
Question:
What is wrong with my logic? How should I add QQuickItems to an std::map so that I can safely retrieve them back.
First you have const string inside this method istead of QQuickItem name
findChild<QQuickItem*>("quickitem.c_str()");
So it gives you 0
You should add checking if item is NULL before adding to container.
Second thing is that you talking about searching items map, but here, you are appending to parents
parents.insert(std::make_pair(item, item->parent());
BTW: When you use Qt, I recommend you using Qt containers
EDIT:
This works for me, after some improvements and if i have appropriate objects in QML
CPP
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
std::array<std::string, 2> ObjectNamesArray = { "quickitemObj1", "quickitemObj2" };
std::map<QQuickItem*, QQuickItem*> items;
for(const auto& quickitem: ObjectNamesArray) {
QQuickItem * item = engine.rootObjects()[0]->findChild<QQuickItem*>(quickitem.c_str());
if (item != NULL)
items.insert(std::make_pair(item, (QQuickItem*)item->parent()));
}
std::cout << "Map: " << items.size() << std::endl; //it gives 2
std::map<QQuickItem*, QQuickItem*>::iterator it = items.begin();
while (it != items.end()) {
QQuickItem* item = it->first;
QQuickItem * itemParent = it->second; // no crash
it++;
}
QML
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item
{
objectName: "quickitemObj1"
}
Item
{
objectName: "quickitemObj2"
}
}
After grouping, is there a way for expand/collapse icon of current row not automatically expand/collapse all of the subgrid's rows? Just leave it alone as it was.
var parmColumnName = 'Model';
$('#test').jqGrid('groupingGroupBy'),
parmColumnName,
{
groupCollapse: true,
groupField: ['name']
}
);
//Original setup after playing around with it. (See X5 under BMW)
//Collapse the grouped Make
//Then Expand the grouped Make (All of the model are expanded by default, I do not want it to change and I want it to look like the original snapshot above)
I find your question very interesting, but the solution for the problem is not easy. In my opinion the source code of two jqGrid methods groupingRender and especially groupingToggle should be changed. The solution which I suggest you can see on the demo. The demo overwrites the original code of groupingRender and groupingToggle methods. More full description of my suggestions you will find below.
First of all I want to describe the problem in my words. You used the words "the subgrid's rows" in the text of your question which bring misunderstanding. What you use is multilevel grouping. The first problem in my opinion is the behavior of groupCollapse: true option. In case of multilevel grouping jqGrid collapse currently only data instead of all grouping headers till the top level. The demo uses 3-level grouping and the option groupCollapse: true. It dysplays
instead of intuitively expected
Another problem which you formulate in your question is the current behavior of expending. The problem is that if the user have collapsed the nodes to that all looks compact, like on the last picture which I posted, end then the user expand some node jqGrid expand all children grouping headers of the node till the data. So if one expand for example only "test1" node then all its children nodes will be expanded instead of expending only the next grouping level.
To fix the first problem (opened sub-grouping headers in spite of groupCollapse: true) I changed one line of groupingRender method from
str += "<tr id=\""+hid+"\" role=\"row\" class= \"ui-widget-content jqgroup ui-row-"+$t.p.direction+" "+clid+"\"><td style=\"padding-left:"+(n.idx * 12) + "px;"+"\" colspan=\""+colspans+"\">"+icon+$.jgrid.template(grp.groupText[n.idx], gv, n.cnt, n.summary)+"</td></tr>";
to
str += "<tr id=\""+hid+"\"" +(grp.groupCollapse && n.idx>0 ? " style=\"display:none;\" " : " ") + "role=\"row\" class= \"ui-widget-content jqgroup ui-row-"+$t.p.direction+" "+clid+"\"><td style=\"padding-left:"+(n.idx * 12) + "px;"+"\" colspan=\""+colspans+"\">"+icon+$.jgrid.template(grp.groupText[n.idx], gv, n.cnt, n.summary)+"</td></tr>";
The main problem which you asked was a little more difficult. Below you can find the fixed version of
$.jgrid.extend({
groupingToggle : function(hid){
this.each(function(){
var $t = this,
grp = $t.p.groupingView,
strpos = hid.split('_'),
uidpos,
//uid = hid.substring(0,strpos+1),
num = parseInt(strpos[strpos.length-2], 10);
strpos.splice(strpos.length-2,2);
var uid = strpos.join("_"),
minus = grp.minusicon,
plus = grp.plusicon,
tar = $("#"+$.jgrid.jqID(hid)),
r = tar.length ? tar[0].nextSibling : null,
tarspan = $("#"+$.jgrid.jqID(hid)+" span."+"tree-wrap-"+$t.p.direction),
getGroupingLevelFromClass = function (className) {
var nums = $.map(className.split(" "), function (item) {
if (item.substring(0, uid.length + 1) === uid + "_") {
return parseInt(item.substring(uid.length + 1), 10);
}
});
return nums.length > 0 ? nums[0] : undefined;
},
itemGroupingLevel,
collapsed = false, tspan;
if( tarspan.hasClass(minus) ) {
if(grp.showSummaryOnHide) {
if(r){
while(r) {
if($(r).hasClass('jqfoot') ) {
var lv = parseInt($(r).attr("jqfootlevel"),10);
if( lv <= num) {
break;
}
}
$(r).hide();
r = r.nextSibling;
}
}
} else {
if(r){
while(r) {
itemGroupingLevel = getGroupingLevelFromClass(r.className);
if (itemGroupingLevel !== undefined && itemGroupingLevel <= num) {
break;
}
$(r).hide();
r = r.nextSibling;
}
}
}
tarspan.removeClass(minus).addClass(plus);
collapsed = true;
} else {
if(r){
var showData = undefined;
while(r) {
itemGroupingLevel = getGroupingLevelFromClass(r.className);
if (showData === undefined) {
showData = itemGroupingLevel === undefined; // if the first row after the opening group is data row then show the data rows
}
if (itemGroupingLevel !== undefined) {
if (itemGroupingLevel <= num) {
break;// next item of the same lever are found
} else if (itemGroupingLevel === num + 1) {
$(r).show().find(">td>span."+"tree-wrap-"+$t.p.direction).removeClass(minus).addClass(plus);
}
} else if (showData) {
$(r).show();
}
r = r.nextSibling;
}
}
tarspan.removeClass(plus).addClass(minus);
}
$($t).triggerHandler("jqGridGroupingClickGroup", [hid , collapsed]);
if( $.isFunction($t.p.onClickGroup)) { $t.p.onClickGroup.call($t, hid , collapsed); }
});
return false;
},
});
The demo demonstrates the results of all changes which I suggest. I'll post the changes as pull request to trirand. I hope that the changes will be included in the main code of jqGrid.
UPDATED: I posted the pull request with the changes which I suggested above.
UPDATED 2: My pull request was merged with the main code of jqGrid. The new 4.5.4 version of jqGrid published today includes the changed. The new demo uses jqGrid 4.5.4 and it works like you expect. So to fix the problem which you described in your question you need just update jqGrid.
I have a CKeditor instance (version 4.1.2) with font, size, text and background color buttons in its toolbar, all completely default.
It's created by replacing a <textarea> whose contents are loaded from a database.
When the loaded html contains elements such as:
<h3><font color="red">Big Red Heading</font></h3>
CKeditor is simply stripping away the tags, to leave:
<h3>Big Red Heading</h3>
Whereas, my expectations (according to the docs) were that it should convert this to:
<h3><span style="color:red">Big Red Heading</span></h3>
(It strips tags with size and face attributes also, just the same way).
I haven't changed allowedContent or colorButton_foreStyle, or any other config setting that ought to have any effect on this issue. I've tried removing all custom config (leaving an absolutely default instance of the editor), and it still happens.
Can anyone explain why this might be happening, and how to fix it?
Thanks.
EDIT: The default value of colorButton_foreStyle is set like this in the CKeditor source:
CKEDITOR.config.colorButton_foreStyle = {
element: 'span',
styles: { 'color': '#(color)' },
overrides: [ { element: 'font', attributes: { 'color': null } } ]
};
...which is why I expected it would automatically convert font tags..?
CKEditor hasn't got all possible transformations defined by default. There is a set of them and it will be enlarged in the future, but this specific one wasn't defined yet.
From Advanced Content Filter guide - content transformations:
Currently, we have defined content transformations for only a handful of editor features, but their number will increase in future releases.
So, there are two solutions:
If you want to keep your font tags, then extend the Advanced Content Filter settings by defining config.extraAllowedContent and change the font plugins settings like in HTML output sample.
If you want to automatically transform your font tags to their newer equivalents, then you can add a new transformations. Read more in filter#addTransformations doc.
I got same problem in CKeditor 4. i searched but i didnt get solution. but i need it so i created my own method in js. its working great.
i created ownFunctoin:
function ConvertFontTagToSpanTag(str) {
var startIndex = str.indexOf('<font');
while (startIndex >= 0) {
var endIndex = str.substring(startIndex).indexOf('>');
var subString1 = str.substring(startIndex, (startIndex + endIndex) + 1);
var subString2 = subString1;
var mapObj = {
size: "font-size:",
face: "font-family:",
color: "color:"
};
subString2 = subString2.replace(/size|face|color/gi, function (matched) {
return mapObj[matched];
});
subString2 = subString2.replace(/['"]/g, ';');
subString2 = subString2.replace(/=;/g, '');
subString2 = subString2.replace('font', 'span');
if (subString2.length > 6) {
subString2 = [subString2.slice(0, 6), 'style=\"', subString2.slice(6)].join('');
subString2 = [subString2.slice(0, subString2.length - 1), '\"', subString2.slice(subString2.length - 1)].join('');
}
//Converting Font-size
var sizeIndex = subString2.indexOf('font-size:');
if (sizeIndex >= 0) {
var sizeEndIndex = subString2.substring(sizeIndex).indexOf(';');
var size = subString2.substring(sizeIndex + 10, (sizeIndex + sizeEndIndex));
//Removing Font size
subString2 = subString2.slice(0, (sizeIndex + sizeEndIndex) - 1) + subString2.slice((sizeIndex + sizeEndIndex));
//Adding Font Size
subString2 = [subString2.slice(0, (sizeIndex + sizeEndIndex)-1), ConvertSpanFontSize(size), subString2.slice((sizeIndex + sizeEndIndex)-1)].join('');
}
//end
str = str.replace(subString1, subString2);
startIndex = str.indexOf('<font');
}
str = str.replace(/font>/g, 'span>');
return str;
}
function ConvertSpanFontSize(size) {
switch (size) {
case '1': return '0.63em';
case '2': return '0.82em';
case '3': return '1.0em';
case '4': return '1.13em';
case '5': return '1.5em';
case '6': return '2em';
case '7': return '3em';
default: return '4em';
}
Cheers...! Thank you
I have a series of QTextEdits and QLineEdits connected to a slot through a QSignalMapper(which emits a textChanged(QWidget*) signal). When the connected slot is called (pasted below), I need to be able to differentiate between the two so I know whether to call the text() or toPlainText() function. What's the easiest way to determine the subclass type of a QWidget?
void MainWindow::changed(QWidget *sender)
{
QTextEdit *temp = qobject_cast<QTextEdit *>(sender);
QString currentText = temp->toPlainText(); // or temp->text() if its
// a QLineEdit...
if(currentText.compare(""))
{
...
}
else
{
...
}
}
I was considering using try-catch but Qt doesn't seem to have very extensive support for Exceptions... Any ideas?
Actually, your solution is already almost there. In fact, qobject_cast will return NULL if it can't perform the cast. So try it on one of the classes, if it's NULL, try it on the other:
QString text;
QTextEdit *textEdit = qobject_cast<QTextEdit*>(sender);
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender);
if (textEdit) {
text = textEdit->toPlainText();
} else if (lineEdit) {
text = lineEdit->text();
} else {
// Return an error
}
You can also use sender->metaObject()->className() so you won't make unnecesary casts. Specially if you have a lot of classes to test. The code will be like this:
QString text;
QString senderClass = sender->metaObject()->className();
if (senderClass == "QTextEdit") {
QTextEdit *textEdit = qobject_cast<QTextEdit*>(sender);
text = textEdit->toPlainText();
} else if (senderClass == "QLineEdit") {
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender);
text = lineEdit->text();
} else {
// Return an error
}
I know is an old question but I leave this answer just in case it would be useful for somebody...