QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Alles rund um die Programmierung mit Qt
Antworten
freakonaleash99
Beiträge: 43
Registriert: 23. Juli 2017 12:35

QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von freakonaleash99 »

Hallo zusammen.
Ich möchte bei einem Text in einem QTextbrowser mittels Button alle ' - ' (Minus) -Zeichen mit einer Hintergrundfarbe versehen, damit diese deutlich sichtbar sind.
Ich schaffe es aber nicht so richtig.
Mein erster Ansatz war folgender:
Den Text zunächst komplett in einen String lesen und diesen dann Zeichen für Zeichen wieder in den TextBrowser schreiben.
Über if/else dann selektieren, ob das Zeichen dann ein Minus ist und entsprechend den Hintergrund setzten.

hier der Code:

Code: Alles auswählen

    QString text;
    text=ui->textBrowser->toPlainText();
    ui->textBrowser->clear();
    QString temp;
    ui->textBrowser->setTextBackgroundColor(Qt::green);
    for(int i=0; i<text.size(); i++){
        if(text.mid(i,1) == "-"){
            ui->textBrowser->insertPlainText(temp);
            temp="";
            ui->textBrowser->setTextBackgroundColor(Qt::red);
            ui->textBrowser->insertPlainText(text.mid(i,1));
            ui->textBrowser->setTextBackgroundColor(Qt::green);
        }else{
            temp = temp+ text.mid(i,1);
        }
    }
    ui->textBrowser->insertPlainText(temp);
Prinzipiell passiert genau das, was ich will. Aber: wenn es sehr viele Zeichen sind, dauert die Ausführung ein paar Sekunden.
QString temp habe ich bereits eingeführt, um das Ganze zu beschleunigen.

Hat evtl. noch jemand eine Idee, wie es schneller funktioniert?
Ich habe mir auch den QSyntaxHighlighter angeschaut. Allerdings komme ich damit nicht so ganz klar wenn ich einzelne Zeichen färben will...
Volker75
Beiträge: 59
Registriert: 8. April 2009 21:04

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Volker75 »

Auf Anhieb sehe ich erstmal, dass du immer zwei mal text.mid(i,1) berechnest. Evtl den Wert nur einmal abfragen in einem temp-Variablen speichern? Evtl. reicht das schon.

Ich persönlich hätte es nicht in Plaintext gesetzt, sondern in html und dann per "replace" die Leerzeichen ersetzt. Ob das aber wirklich schneller ist kann ich nicht sagen.

Evtl. schneller könnte folgender Trick sein:
Den Text nicht als PlainText, sondern als html-Text abzuspeichern bei dem alle Leerzeichen als sein "<span class="Leerzeichen"> </span>" gespeichert sind. Dann braucht man nicht immer den ganzen Text neu durchsuchen (nur ein einziges mal beim ersten Einlesen; danach nie mehr); der Textstring würde immer so bleiben. Man müsste dann am Anfang vor den Text nur noch per css die Farbe wie gewünscht.
Der Umschalten der Farbe würde auf ein einfaches QString cssFarbe+text; hinauslaufen. (Wobei Qt diesen String natürlich auswerten müsste.)
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Christian81 »

Ich bringe einfach mal das Stichwort 'QSyntaxhighlighter' ins Spiel :)
http://doc.qt.io/qt-5/qtwidgets-richtex ... ample.html

/edit:
Und hier eine etwas schnellere Variante:

Code: Alles auswählen

int start = 0;
while (true) {
    int idx = text.indexOf(QLatin1Char('-'), start);
    if (idx < 0) {
        ui->textBrowser->insertPlainText(text.right(start));
	break;
    }
    ui->textBrowser->insertPlainText(text.mid(start, idx - start);
    ui->textBrowser->setTextBackgroundColor(Qt::red);
    ui->textBrowser->insertPlainText(QStringLiteral("-");
    ui->textBrowser->setTextBackgroundColor(Qt::green);
    start = idx + 1;
}
Zumindest so in der Art, ist komplett ungetestet.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Volker75
Beiträge: 59
Registriert: 8. April 2009 21:04

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Volker75 »

Christian81 hat geschrieben: 12. April 2018 17:51 Ich bringe einfach mal das Stichwort 'QSyntaxhighlighter' ins Spiel :)
Das wusste er schon. Zitat:
freakonaleash99 hat geschrieben: 11. April 2018 21:29 Ich habe mir auch den QSyntaxHighlighter angeschaut. Allerdings komme ich damit nicht so ganz klar wenn ich einzelne Zeichen färben will...
Ich gehe davon aus, dass er schon " ", "[ ]" und "\s" ausprobiert hat und es nicht zufriedenstellend funktioniert. Bin bei mehr aber überfragt, da ich reguläre Ausdrücke für meine Programme nie gebrauchen kann und daher überfragt bin.
Christian81 hat geschrieben: 12. April 2018 17:51 Und hier eine etwas schnellere Variante:
hmm... ungetestet, aber ich überlege, ob das z.B. für text=" " stimmt.
Das Programm würde doch so ablaufen:
idx=0;
dann wird das Leereichen bunt geschrieben.
start=1;
idx=-1
und jetzt der Fehler?!: Das Leerzeichen wird noch einmal geschrieben (Diesmal nicht bunt).
Volker75
Beiträge: 59
Registriert: 8. April 2009 21:04

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Volker75 »

Das sollte wohl "ui->textBrowser->insertPlainText(text.right(text.count() - start));" statt "ui->textBrowser->insertPlainText(text.right(start));" sein, oder?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Christian81 »

Das war eher ein Denkanstoß ohne Anspruch auf Richtigkeit. +/-1 Probleme sind sicher drin :)
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Volker75
Beiträge: 59
Registriert: 8. April 2009 21:04

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Volker75 »

Wie das so immer ist :-)

Man könnte sich vielleicht auch überlegen auf "mid" und "indexOf" zu verzichten, weil das ja nicht immer gerade die schnellsten DInge sind. Evtl. z.B. nur das erste Zeichen dem temporären Test auslesen und entfernen. Dann kommt man ohne "mid" und "indexOf" aus, weil man immer mit nur mit dem ersten Zeichen arbeitet. Ich vermute allerdings, dass dies man diesen Datensatz nicht hilf/schneller macht.


Frage mich gerade mal wieder, welche sinnvollen Test durchgeführt werden müssten sobald man glaubt die richtige Lösung gefunden zu haben.

Ich komme auf etwa 6 Testdaten um 10 Tests durchzuführen:
1. "" (Leeren String)
2. " " (nur ein Leerzeichen)
3. "a" (kein Leerzeichen)
4. a) und b) " a" (Leerzeichen am Anfang bzw. kein Leerzeichen am Ende)
5. a) und b) "a " (Kein Leerzeichen am Anfang bzw. Leereichen am Ende)
6. a) und b) und c) „a b c d[...]“ (Sehr langer Text (Geschwindigkeit); mit mehreren Leerzeichen; auch mehrere Leerzeichen direkt hintereinander.)

Habe ich einen nötigen Test/Sonderfall übersehen?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Christian81 »

Man könnte sich vielleicht auch überlegen auf "mid" und "indexOf" zu verzichten, weil das ja nicht immer gerade die schnellsten DInge sind. Evtl. z.B. nur das erste Zeichen dem temporären Test auslesen und entfernen.
Das verstehe ich nicht wirklich. Was soll schneller sein als indexOf? Und warum sollte man die Daten erst nochmal umkopieren und dann auch noch am Anfang etwas entfernen - das ist ein worst-case Szenario...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Volker75
Beiträge: 59
Registriert: 8. April 2009 21:04

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Volker75 »

Kommt auf den Fall an. Ich brauche soetwas nicht in einen einfachen QString, aber ich habe z.B. bei mir öfters Listen von QString in denen ich suche. VOm Prinzip brauche ich nur ein einfaches "indexOf". Das ist leider in der Praxis viel zu langsam und daher habe ich bei meinen Daten oft den Index mittels QHash angelegt. Klar brauche ich dadurch mehr speicher also ohne, aber es ist viel schneller. Bei mir sind die Daten aber auch sehr statisch sobald sie einmal angelegt sind. Das ist hier wahrscheinlich nicht der Fall.
Diesen Fall habe ich im Hinterkopf und wollte ihn übertragen. Wenn ich bei einer Liste vorne (oder hinten) Daten entnehme, dann muss ich ja nichts gezungen umkopieren. Wenn das z.B. die Daten in einer doppelt verketten Liste vorliegen, dann würde ich ja auch nicht immer mit "index" erst suchen, wenn ich einfach nur das erste Element immer nehmen muss. Da kopiere ich auch nichts um (zumindest nicht mehr als in der jetzigen Lösung. Ich habe mich ja auch nciht beschwert mit: Warum erst in QString test umkopieren. Eine Lösung ohne umkopieren, wäre ja das Ändern direkt in ui->textBrowser.
Dein sehr guter Performacetrick ist, dass du bei indexOf erst ab einen bestimmten Wert "start" weitersuchen musst. Den Trick kann ich in meiner Praxis lieder nur selten verwerden bzw. ist trotzdem zu langsam. Wie ich aber auch schon oben vermutet habe: Ich denke, dass meine Idee wahrschienlich hier nicht schneller ist. Kann mir vorstellen, dass es am schnellsten ist wenn man direkte in ui->textBrowser ändert (, wenn man weiß wie es geht. Da müsste ich nichts umkopieren, wie es in der jetzigen Version ist.)
Habe jetzt nicht geprüft wie QString intern vorgeht beim Verkleinern, aber die werden mit die Größe des allokierten Speichers nicht immer sofort reduzieren nur wenn man das erste Element löscht. Wenn die es schlau gemacht haben, dann wird der Speicher weiterhin "festgehalten" für den Fall das später ein schnelles prepend benötigt wird. Sprich nur ein Zeiger auf den "richtigen" Start wird geändert. Bin mir sicher, dass es beim Vergrößern ebenso war. Wenn ich es richtig im Kopf habe, dann wird immer sofort mehr Speicher reserviert als man eigentlich braucht. Damit nicht bei jedem hinzufügen eines Zeichens im schlimmsten Fall "umkopiert" werden muss. Wäre ja viel zu langsam. Umgekehrt werden die so etwas ja wohl auch gemacht haben. Aber wie gesagt: Deine Variante ist wahrscheinlich schneller als mein Vorschalg.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Christian81 »

Dein use-case ist nicht mit diesem hier zu vergleichen. Hier wird einmalig über einen QString iteriert. Da lohnt es sich nicht noch irgendwelche anderen Dinge anzulegen nur um danach den String nochmal zu durchsuchen. Exakt einmal über alle Zeichen des Strings zu itererieren ist das Optimalste was möglich ist und mein Beispiel zeigt wie es gehen kann.
Und QString (wie auch std::string) haben keine Funktion die am Anfang ein Zeichen wegnehmen kann ohne einen neuen String erzeugen zu müssen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Volker75
Beiträge: 59
Registriert: 8. April 2009 21:04

Re: QTextbrowser: Hintergrundfarbe für einzelne Zeichen setzen?

Beitrag von Volker75 »

Ja. Dein erster Punkt wird wohl stimmen. (Wobei wir da beide voraussetzen, dass freakonaleash99 wohl einen "normalen" Editor programmiert. Vielleicht ist diese Annahme aber falsch und er wertet ganz andere Daten aus; vielleicht kommt das Leerzeichen in seinen Daten extrem selten vor,...)
Hatte gestern irgendwie im Kopf gehabt, dass es bei QString ein .delete gibt. Habe mich offensichtlich falsch erinnert. Habe daher mal spaßeshalber QString &QString::remove(int pos, int len) durchgelesen. (zu finden in qtbade/src/qstring.cpp ). Dort sehe ich bisher kein anlegen eines neuen Strings. Wenn ich es richtig sehe, dann gibt es verschiedene Fälle. Nimmt man am Ende weg, dann wird ::resize gemacht ohne Speicher freizugeben. Ansonsten wird es mit "memcopy" gemacht. Sehe leider gerade nicht wie sie das machen; könnte auch ohne neuen String sein; muss mal gucken wo das steht. Aber du hast schon recht, dann wird zumindest jedes folgende Zeichen kopiert(verschoben).
Antworten