How to have a QTextBrowser to display contents of a QTextEdit? - qt

I am trying to connect QTextEdit to QTextBrowser, so the text browser widget outputs what is entered in text edit widget. As a signal I used textChanged(), and as a slot I used setText(QString). And these two don't have same parameters.
If I used QLineEdit instead of QTextEdit, in that case there is textChanged(QString) function which is compatible with the slot,but I need to make it work with QTextEdit. Here is the code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtWidgets>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QWidget * mainWidget=new QWidget(this);
ui->setupUi(this);
QTextEdit * mainTextEdit=new QTextEdit();
QTextBrowser * textDisplay=new QTextBrowser();
connect(mainTextEdit,SIGNAL( textChanged() ),
textDisplay,SLOT( setText(QString) ) );
QHBoxLayout * Alayout=new QHBoxLayout();
Alayout->addWidget(mainTextEdit);
Alayout->addWidget(textDisplay);
mainWidget->setLayout(Alayout);
setCentralWidget(mainWidget);
}
MainWindow::~MainWindow()
{
delete ui;
}

Thankfully, the QTextEdit and QTextBrowser are views onto a QTextDocument model. So, you can simply set the editor's document on the browser. QTextBrowser::setDocument is semantically equivalent to QAbstractItemView::setModel:
textDisplay->setDocument(mainTextEdit->document());
In Qt, there are really two basic model classes: QAbstractItemModel and QTextDocument. A QTextDocument is a model in its own model-view framework. We simply set another view onto the document that the editor operates on. The editor allows modifications to the model, the browser doesn't. It's no different from using the same model on two QListViews, etc.
A QTextEditor is a view with a default model (document). You can replace that default model with one from another view, or even with one that you yourself provide. You could have multiple editors all displaying the same QTextDocument document and allowing editing of it, in parallel. You can also have multiple browsers doing the same.
Complete example:
#include <QApplication>
#include <QTextEdit>
#include <QTextBrowser>
#include <QHBoxLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget window;
QHBoxLayout layout(&window);
QTextEdit edit;
QTextBrowser browser;
layout.addWidget(&edit);
layout.addWidget(&browser);
browser.setDocument(edit.document());
window.show();
return a.exec();
}

I would do it in the following way:
Declare the pointers to the text edit and text browser widgets as member variables in the class,
Create a slot onTextChanged() in MainWindow class that will be called as soon as the text edit is changed and setup the connection as:
connect(mainTextEdit, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
Implement the onTextChanged() slot in the following way:
MainWindow::onTextChanged()
{
QString text = mainTextEdit->toPlainText();
textDisplay->setPlainText(text);
}

Related

Not mirroring layout direction in some of Qt widgets

I have an application which should have a right-to-left layout direction. But there some widgets(e.g. a QComboBox and a QlistWidget) which i don't want to mirror layout-direction (they should have left-to-right layout-direction whatever the layout-direction of app is).
What I'm looking for is something like LayoutMirroring.enabled in qml.
Is there a solution for this?
Edit:
This is a very simplified version of my code:
file widget.h:
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
file widget.cpp:
Widget::Widget(QWidget *parent): QWidget(parent){
setMinimumSize(300, 300);
QLabel *label1 = new QLabel("Right to left 1");
QLabel *label2 = new QLabel("Right to left 2");
QLabel *label3 = new QLabel("Right to left 3");
QComboBox *mCombo = new QComboBox();
mCombo->setMinimumWidth(150);
mCombo->addItems(QStringList({"Left to Right 1", "Left to Right 2", "Left to Right 3"}));
mCombo->setStyleSheet("QComboBox{padding: 0 10 0 10;}");
mCombo->setLayoutDirection(Qt::LeftToRight);
QVBoxLayout *mainlayout = new QVBoxLayout();
mainlayout->setAlignment(Qt::AlignLeft);
mainlayout->addWidget(mCombo);
mainlayout->addWidget(label1);
mainlayout->addWidget(label2);
mainlayout->addWidget(label3);
setLayout(mainlayout);}
and this my main.cpp:
#include "widget.h"
#include <QApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setLayoutDirection(Qt::RightToLeft);
Widget w;
w.show();
qDebug()<<a.layoutDirection() <<w.layoutDirection();
return a.exec();
}
comment: my project uses a stylesheet file and after playing with different parts of style for QComboBox I realized that the style "QComboBox{padding: 0 10 0 10;}" was causing the problem. So I included that, here. If I remove that line the problem will be solved.
note: I also realized that theWidget->setLayoutDirection(Qt::LeftToRight); will do what I was looking though I don't know it's the proper way or not!
So, The Problem was with the stylesheet that my app is using. this line of stylesheet "QComboBox{padding: 0 10 0 10;}" was the cause of problem. I removed it and problem solved. Though I don't know the reason.
Also for a specific widget that shouldn't get the app's layout-direction, the layout-direction must be set explicitly. like: theWidget->setLayoutDirection(Qt::LeftToRight);
And I realized it from Qt documentaion

Restore geometry and state of an arbitrary QDialog

In our applications we are using customizable dialogs using exhaustively QSplitter, so that our customers can rearrange the dialogs to fit their needs.
(Sometimes we are also using QDockWidget, but this seems to be similar.)
Now, it is very annoying to rearrange the dialog every time it is opened again. Or even between different starts of the program.
After consulting the documentation I was able to restore the state and the geometry of a specific dialog containing one QSplitter.
#include <QApplication>
#include <QLabel>
#include <QDebug>
#include <QSplitter>
#include <QPushButton>
#include <QTextEdit>
#include <QDialog>
#include <QSettings>
#include <QHBoxLayout>
int main(int argc, char** args) {
QApplication app(argc, args);
app.setOrganizationName("Tech");
app.setOrganizationDomain("qt.us");
app.setApplicationName("RestoreLayout");
app.setApplicationVersion("1.0");
QDialog dialog;
dialog.setLayout(new QHBoxLayout);
auto splitter = new QSplitter;
splitter->addWidget(new QLabel("Left"));
splitter->addWidget(new QLabel("Right"));
dialog.layout()->addWidget(splitter);
auto accept = new QPushButton("Accept");
accept->connect(accept, &QPushButton::clicked, [&](){
dialog.accept();
});
splitter->addWidget(accept);
auto geom= QSettings().value("Geom").toByteArray();
auto splitterState = QSettings().value("State").toByteArray();
qDebug() << geom;
qDebug() << splitterState;
dialog.restoreGeometry(geom);
splitter->restoreState(splitterState);
dialog.show();
dialog.connect(&dialog, &QDialog::accepted, [&]() {
QSettings().setValue("Geom", dialog.saveGeometry());
QSettings().setValue("State", splitter->saveState());
app.quit();
});
app.exec();
}
Unfortunately, this seems to be an approach, which is not usable in general.
Assume, that there is some arbitrary dialog, that needs to restore its geometry and state. Even worser QSplitter and QDockWidget might be even used in a nested fashion, which is done in our applications.
How can an outside programmer restore the geometry and the state of a arbitrary dialog that might be easily applicable to all possible dialogs?
For saving states of QDockWidget each it must be named: dockWidgetN->setObjectName("dock-widget-N");
But you can save only QMainWindow state for saving states of docks in this window.
You can separatelly save states via QSettings (it's QByteArray) and use some one state for many windows.
See here: How to save state of a dialog in Qt?

Button is not showing on main window even after successful execution of code in Qt

I have tried this code but the button isn't displaying on the main window.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QGridLayout>
#include<QLabel>
#include<QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPushButton *l=new QPushButton();
l->setText("abc");
QGridLayout *q=new QGridLayout();
q->addWidget(l);
this->setLayout(q);
this->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
I have tried to change the code even by passing enums for alignment but nothing worked.
When you create new Qt widgets Application, the default form (MainWindow ui) is created with centralWidget to put all other widgets. In your code you created the QGridLayout without a parent, typically such layout should be placed in ui->centralWidget (as far as you are not creating another widget to be set as centralWidget), moreover I assume your mainWindow is shown from main.cpp (need not use show()). your code could thus be:
QPushButton *l=new QPushButton();
l->setText("abc");
QGridLayout *q=new QGridLayout(ui->centralWidget);
q-> addWidget(l);
Try adding the widget to the GridLayout with index using addWidget function
void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = ...)
like:
q-> addWidget(l, 0, 0);
P.S. also consider using better names for your variables!

How to continue text editing after formatting changes of QGraphicsTextItem?

I am trying to make changes (font changes) to a QGraphicsTextItem that is editable.
I am trying to change formatting of fragments of text, or the formatting applied at typing point (if I set text bold, the text I type after that action at cursor position would be bold).
Setting formatting for text fragments works - but I can't find a way to return the focus correctly to the item.
I can show the caret at the right position, but I can't type in the box unless I actually click in box (even though it seems hat I should be able to).
Simple sample (for some reason it crashes on closing program but I don't care about that since I am testing the text class, not the main program):
header: mytextitem.h
#include <QGraphicsTextItem>
class MyTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
MyTextItem();
~MyTextItem() {}
public slots:
void setItemBold(const int b);
};
mytextitem.cpp
#include "mytextitem.h"
#include <QTextCursor>
MyTextItem::MyTextItem()
{
setPlainText("ABCD");
setFont(QFont("Arial", 20));
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
setTextInteractionFlags(Qt::TextEditorInteraction);
}
void MyTextItem::setItemBold(const int b)
{
int _weight = (b != 0) ? QFont::Bold : QFont::Normal;
QTextCursor _cursor = textCursor();
//int p = _cursor.position(); // this won't help
QTextCharFormat _format = _cursor.charFormat();
_format.setFontWeight(_weight);
_cursor.setCharFormat(_format);
//_cursor.setPosition(p, QTextCursor::KeepAnchor); // makes no difference on allowing me to type, but I can make the cursor move
//_cursor.movePosition(QTextCursor::NoMove, QTextCursor::KeepAnchor, 0); // makes no difference but I just thought some action might
setTextCursor(_cursor);
setFocus(Qt::MouseFocusReason);
// grabKeyboard(); // does nothing
}
main.cpp
#include <QApplication>
#include <QGraphicsView>
#include <QGridLayout>
#include <QtWidgets>
#include <QCheckBox>
#include "mytextitem.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene(-20, -20, 150, 100);
QGraphicsView view(&scene);
QWidget widget;
QGridLayout layout(&widget);
layout.addWidget(&view, 0, 0);
QCheckBox bold("Bold");
layout.addWidget(&bold, 0, 1);
MyTextItem* item = new MyTextItem();
scene.addItem(item);
QObject::connect(&bold, SIGNAL(stateChanged(int)), item, SLOT(setItemBold(int)));
view.ensureVisible(scene.sceneRect());
widget.show();
return a.exec();
}
Editing the item is possible only if clicking in the box.
Assuming that I am already in the box (editing), and I push the "Bold" checkbox, I expect to be able to continue editing - type in the box - but even though I try to
set focus (which places the blinking text cursor in the box),
set position for cursor (I can move it, or select things... that works but I want to keep current position and selection)
grab keyboard - seems to do nothing
nothing seems to return me to the box so I continue typing (with the new font setting).
How can I get the QTextCursor or anything else to allow me to keep editing the text ?
You need to focus on QGraphicsView after format change. You can't focus on QGraphicsTextItem because it isn't QWidget.

How to find global position of text cursor?

I would like to execute a QMenu object at the position of text cursor in a QPlainTextEdit. My problem is that QTextCursor is only define by its position in the Text (index of the character).
How can I find global position of the QTextCursor? Should I use an other object than QTextCursor in order to find the position of the text cursor where I want to open my QMenu?
Thank you by advance.
I've never tried myself, but doesn't QPlainTextEdit::cursorRect() work? It should give you position of the cursor in viewport coordinates. You can then get the viewport using viewport() and map the local position to global using viewport()->mapToGlobal().
I have found similar query to your in some online forum and here's someone suggested the output as
Note: Reference from http://www.unix.com/unix-linux-applications/81388-read-position-mouse-cursor.html, Author of below posting is daggilli, registered user of UNIX online forums. Credit of below posting in its complete form goes to daggilli.
This is the complete code for a Qt application I threw together in about ten minutes (called crosshair) which displays the current mouse coordinates in a window. You might be able to pull enough out of it to be useful. This is Qt 3.1, but Qt 4 is not a great deal different. You will need the Qt development libraries, not just the runtimes. The code comprises two files, crosshair.h and crosshair.cpp.
crosshair.h:
Code:
#ifndef CROSSHAIR_H
#define CROSSHAIR_H
#include <qwidget.h>
#include <qstring.h>
#include <qlabel.h>
#include <qevent.h>
class Crosshair : public QLabel
{
Q_OBJECT
public:
Crosshair(QWidget *parent=0);
protected:
void mousePressEvent(QMouseEvent *);
private:
QTimer *timer;
private slots:
void timerfire();
};
#endif
crosshair.cpp:
Code:
#include <qapplication.h>
#include <qpushbutton.h>
#include <qtimer.h>
#include <qcursor.h>
#include <iostream>
#include "crosshair.h"
using namespace std;
int main(int argc,char **argv)
{
QApplication a(argc,argv);
Crosshair mousepos;
a.setMainWidget(&mousepos);
mousepos.show();
return a.exec();
}
Crosshair::Crosshair(QWidget *parent) : QLabel(parent)
{
setIndent(20);
resize(100,30);
move(1200,200);
setText("0,0");
timer=new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(timerfire()));
timer->start(50,false);
}
void Crosshair::mousePressEvent(QMouseEvent *)
{
qApp->quit();
}
void Crosshair::timerfire()
{
QPoint p=QCursor::pos();
this->setText(QString().sprintf("%d,%d",p.x(),p.y()));
}
To build this, put both files in a directory called crosshair. cd to that directory and type
Code:
qmake -project
qmake
make
This does nothing more complex than inherit from a QLabel, set a timer to run 20x a second, grab the current cursor coordinates and write them into the label's text. Clicking in the window closes it. I use it for fixing up alignment bugs in JavaScript when I'm laying out objects.
You could open a file in the Crosshair class's constructor to store your data, and use gettimeofday(2) to get a timestamp. Nothing says Qt has to run in GUI mode (you can tell it explicitly not to in the QApplication constructor).
Qt from Trolltech: http://doc.trolltech.com

Resources