What I'm trying to do is pretty simple, when the mouse is over the qgraphicsitem I want it to change it's text value. Later on I want to use this to pop-up text when I click an image (i.e. the info of the image)
Here's my code so far:
#include <QtGui/QApplication>
#include <QtGui/QGraphicsItem>
#include <QtGui/QGraphicsTextItem>
#include <QtGui/QGraphicsScene>
#include <QtGui/QGraphicsView>
#include <QtGui/QPixmap>
int main( int argc, char * * argv )
{
QApplication app( argc, argv );
QGraphicsScene scene;
QGraphicsView view( &scene );
QGraphicsTextItem text( "this is my text");
scene.addItem(&text);
scene.setActivePanel(&text);
text.setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
text.setAcceptHoverEvents(true);
text.setAcceptTouchEvents(true);
if (text.isUnderMouse() || text.isSelected()){
text.setPlainText("test");
}
view.show();
return( app.exec() );
}
Some people use double-click events but I was hoping not to use them, but... if that's the only way to get the job done then it's ok.
This code block:
if (text.isUnderMouse() || text.isSelected()){
text.setPlainText("test");
}
is run exactly once, before your view is even shown; so this has absolutely no chance of doing what you expect it to.
You'll need to do a bit more work for that, namely create a custom subclass of QGraphicsTextItem and override the appropriate event handlers.
Here's how you could do that to handle changing text when hovering:
class MyTextItem: public QGraphicsTextItem
{
public:
MyTextItem(QString const& normal, QString const& hover,
QGraphicsItem *parent=0)
: QGraphicsTextItem(normal, parent), normal(normal), hover(hover)
{
}
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
setPlainText(hover);
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent *)
{
setPlainText(normal);
}
private:
QString normal, hover;
};
Add that to your code, and change the text declaration to:
MyTextItem text("this is my text", "test");
and it should do what you expect.
Related
I migrated some code from Qt 5.6.0 to 5.12.0. Suprisingly, I'm getting lots of warnings related to QWindowsWindow::setGeometry. Whenever a dialog is shown on top of another, I get this warning.
I could isolate the problem in a MCVE, it's very simple and minimal, all parenting look good, however, we get the warning when button is pressed:
QWindowsWindow::setGeometry: Unable to set geometry 132x30+682+303 on QWidgetWindow/'QDialogClassWindow'. Resulting geometry: 132x42+682+303 (frame: 4, 28, 4, 4, custom margin: 0, 0, 0, 0, minimum size: 116x42, maximum size: 16777215x16777215).
main.cpp:
#include <QApplication>
#include "mainframe.h"
#include <qDebug>
void MessageOutput( QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
qDebug() << msg;
}
int main( int argc, char* argv[] )
{
QApplication app(argc, argv);
qInstallMessageHandler(MessageOutput);
MainFrame wnd;
wnd.show();
return app.exec();
}
mainframe.h:
#include <QMainWindow>
class QPushButton;
class MainFrame : public QMainWindow
{
Q_OBJECT
public:
MainFrame();
public slots:
void showPopup();
private:
QPushButton* button;
};
mainframe.cpp:
#include "mainframe.h"
#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
MainFrame::MainFrame()
{
QWidget* widget = new QWidget( this );
widget->setLayout( new QVBoxLayout( widget ) );
QPushButton* pTextButton = new QPushButton( "Show popup", widget );
widget->layout()->addWidget( pTextButton );
connect( pTextButton, SIGNAL(clicked()), this, SLOT(showPopup()) );
setCentralWidget( widget );
}
void MainFrame::showPopup()
{
QDialog dlg( this );
dlg.setLayout( new QVBoxLayout() );
dlg.layout()->addWidget( new QLabel("popup message",&dlg) );
dlg.exec();
}
I see the issue under Windows 7 and 10. Am I doing anything wrong?
I know the warning can be removed by setting setMinimumSize (see https://stackoverflow.com/a/31231069/3336423), but why should we do this for every widget we create? Is there a way to fix that for good?
As you mentioned, this problem occurs only in Windows: the QWindowsWindow class is part of the windows platform plugin. Looking at Qt's source code (qwindowswindow.cpp#QWindowsWindow::setGeometry) there is no direct way to pause that specific message.
The only global solution I can think of right now is to filter the warning messages using a message handler:
void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
if (type != QtWarningMsg || !msg.startsWith("QWindowsWindow::setGeometry")) {
QByteArray localMsg = msg.toLocal8Bit();
fprintf(stdout, localMsg.constData());
}
}
int main(int argc, char* argv[])
{
qInstallMessageHandler(myMessageOutput);
QApplication a(argc, argv);
// ...
}
UPDATE
One of the problems is that Windows adds its own buttons to the frame. In your example the dialog adds three buttons: the system button (the icon, top-left corner), the help button and the close button. The help and close buttons have a fixed size, which happens to be larger than the QDialog's frame (which is computed as the maximum between the requested size and minimumSize). This then generates the warning: your requested size doesn't match the one created by Windows:
If you remove the help button, for example (dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowContextHelpButtonHint);), the warning disappears without setting a minimum size for the window. A manual action must be taken for each dialog displayed, but I think it is easier to automatize than the minimum size (through a factory maybe?):
The issue was reported to Qt:
https://bugreports.qt.io/browse/QTBUG-73258
To the code in OP is OK, it's just a Qt bug.
It's marked as "P2 Important", so hopefully it should be fixed in a next release.
Update: It's still not fixed in Qt 6.2.2...
In my program I am displaying a dialog window to provide information to the user. I am using the QT Label element to do this and I would like to make the text look more "organic" or carved in, like the title text of a window than simple black on gray. Does anyone have any suggestions? I am using QT Creator and coding in c++ for Linux.
here is an image of what I am trying to acomplish
I prepared an MCVE to demonstrate the options I suggested in my comments.
Changing style of QLabel is rather simple and straight forward.
Using the QStylePainter doesn't quite match what I actually expected. I left in the sample code what I got so far. (May be, somebody could leave a helpful hint.) I will investigate in this topic later and edit this answer as soon as I got some satisfying progress on this topic.
The sample code:
#include <QtWidgets>
class TitleBar: public QWidget {
private:
QStyleOptionTitleBar _option;
//QString _text;
public:
explicit TitleBar(
const QString &text = QString(), QWidget *pQParent = nullptr);
protected:
virtual QSize sizeHint() const;
virtual void resizeEvent(QResizeEvent *pQEvent);
virtual void paintEvent(QPaintEvent *pQEvent);
};
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// setup of GUI
QWidget qWin;
QVBoxLayout qBox;
// a boring label
QLabel qLbl1(QString::fromUtf8("Rather boring"));
qLbl1.setAlignment(Qt::AlignCenter);
qBox.addWidget(&qLbl1);
// a label with frame
QLabel qLbl2(QString::fromUtf8("Label with Frame"));
qLbl2.setFrameStyle(QLabel::Panel | QLabel::Raised);
qLbl2.setLineWidth(2);
qLbl2.setAlignment(Qt::AlignCenter);
qBox.addWidget(&qLbl2);
// a label with rich-text
QLabel qLbl3(
QString::fromUtf8(
"<body bgcolor='#28f'>" // doesn't have the effect
"<font size='+2' color='#f8e' face='Old English Text MT'>"
"Label <i>with</i> <b>Rich-Text</b>"
"</font>"
"</body>")); // background is set by style-sheet instead
qLbl3.setTextFormat(Qt::RichText); // might be auto-detected...
qLbl3.setStyleSheet("QLabel { background-color: #28f; }");
qLbl3.setAlignment(Qt::AlignCenter);
qLbl3.show();
qBox.addWidget(&qLbl3);
// a home-brew title bar
TitleBar qTitle("A Home-Brew Title Bar");
qBox.addWidget(&qTitle);
// finish setup of GUI
qWin.setLayout(&qBox);
qWin.show();
// run application
return qApp.exec();
}
TitleBar::TitleBar(const QString &text, QWidget *pQParent):
QWidget(pQParent)
{
_option.initFrom(this);
_option.titleBarFlags = Qt::Window;
_option.text = text;
}
QSize TitleBar::sizeHint() const
{
#if 0 // does not provide the expected result
return _option.rect.size();
#elif 0 // does not provide the expected result
return style()->proxy()->subControlRect(QStyle::CC_TitleBar,
&_option, QStyle::SC_TitleBarLabel).size();
#else
return QSize(0,
style()->proxy()->pixelMetric(QStyle::PM_TitleBarHeight,
&_option, this));
#endif // 0
}
void TitleBar::resizeEvent(QResizeEvent *pQEvent)
{
_option.rect = QRect(_option.rect.topLeft(), pQEvent->size());
}
void TitleBar::paintEvent(QPaintEvent *pQEvent)
{
QPainter qPainter(this);
style()->proxy()->drawComplexControl(QStyle::CC_TitleBar,
&_option, &qPainter, this);
}
I compiled and tested it in VS2013 on Windows 10 (64 bit). Below a snapshot:
I want to make a QGraphicsTextItem editable on double click, and make it movable when I click out.
#include <QApplication>
#include <QPainter>
#include <QGraphicsItem>
#include <QGraphicsView>
class TextItem: public QGraphicsTextItem
{
public:
TextItem()
{
setPlainText("hello world");
QFont f;
f.setPointSize(50);
f.setBold(true);
f.setFamily("Helvetica");
setFont(f);
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
setTextInteractionFlags(Qt::NoTextInteraction);
}
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL)
{
QGraphicsTextItem::paint(painter, option, widget);
}
protected:
virtual void focusOutEvent (QFocusEvent * event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::NoTextInteraction);
}
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::TextEditable); // TextEditorInteraction
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TextItem* t = new TextItem();
QGraphicsView view(new QGraphicsScene(-200, -150, 400, 300) );
view.scene()->addItem(t);
view.show();
return a.exec();
}
It does what I want - except I have to double-click twice
- first time I double click, I see a cursor but am unable to edit text (with either option, TextEditable or TextEditorInteraction (I probably want the latter). Then I double-click again and I can type to add or delete text.
It is a behavior that a user probably doesn't expect - and nothing I do seems to change it.
Am I doing something wrong, or is there anything I need to add ?
I expected a mouse action on a focusable item to give it focus automatically. I guess not...
In the mouseDoubleClickEvent, I added a call to setFocus()
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::TextEditorInteraction);
setFocus();
}
Under Windows, I've seen a nice feature: If I hover with the mouse over a short text field which contains overlong text not fitting completely into the field, a tooltip opens, displaying the complete contents of the text field.
Can someone point me to a code snippet which does this with QLineEdit?
I would create a custom class derived from QLineEdit like so:
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <QtGui>
class LineEdit : public QLineEdit
{
Q_OBJECT
public:
LineEdit();
public slots:
void changeTooltip(QString);
};
LineEdit::LineEdit()
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(changeTooltip(QString)));
}
void LineEdit::changeTooltip(QString tip)
{
QFont font = this->font();
QFontMetrics metrics(font);
int width = this->width();
if(metrics.width(tip) > width)
{
this->setToolTip(tip);
}
else
{
this->setToolTip("");
}
}
#include "moc_LineEdit.cpp"
#endif // LINEEDIT_H
Then just add it to whatever:
#include <QtGui>
#include "LineEdit.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
LineEdit edit;
edit.show();
return app.exec();
}
Here the improved function as mentioned in the comments above.
void LineEdit::changeTooltip(QString tip)
{
QFont font = this->font();
QFontMetrics metrics(font);
// get the (sum of the) left and right borders;
// note that Qt doesn't have methods
// to access those margin values directly
int lineMinWidth = minimumSizeHint().width();
int charMaxWidth = metrics.maxWidth();
int border = lineMinWidth - charMaxWidth;
int lineWidth = this->width();
int textWidth = metrics.width(tip);
if (textWidth > lineWidth - border)
this->setToolTip(tip);
else
this->setToolTip("");
}
You can try to change the tooltip each time the text is changed:
First, define a private slot to react the textChanged() signal from the QLineEdit:
(in the header file from the class where your QTextEdit belongs)
....
private slots:
void onTextChanged();
....
In the cpp file, then, connect the QLineEdit textChanged() signal to the slot you defined, and implement the behavior when the text changes:
// In constructor, or wherever you want to start tracking changes in the QLineEdit
connect(myLineEdit, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
Finally, this is how the slot would look like:
void MainWindow::onTextChanged() {
myLineEdit->setTooltip(myLineEdit->text());
}
I'm supposing a class called MainWindow contains the QLineEdit.
It seems like this would be a common thing to do, but I can't find how.
I have a QTextEdit or QPlainTextEdit widget with a bunch of text. Enough that scrolling is necessary.
I want another widget to give some information about the currently visible text. To do this, I need to know
when the visible text changes
what's the text?
I see that QPlainTextEdit has the method firstVisibleBlock, but it's protected. This tells me that it's not really something I should be using in my application. I wouldn't otherwise need to subclass from the edit window.
I also see that there's the signal updateRequest but it's not clear what I do with the QRect.
How do I do it or where do I find a hint?
I've written a minimal program that as two QTextEdit fields. In the left field you write and the text you are writing is shown in the second text edit too. You get the text of a QTextEdit by using toPlainText() and the signal is textChanged().
I've tested it and what you write in m_pEdit_0 is shown in "real-time" in m_pEdit_1.
main_window.hpp
#ifndef __MAIN_WINDOW_H__
#define __MAIN_WINDOW_H__
#include <QtGui/QtGui>
#include <QtGui/QMainWindow>
#include <QtGui/QApplication>
class main_window : public QMainWindow
{
Q_OBJECT
public:
main_window( QWidget* pParent = 0 );
~main_window();
public Q_SLOTS:
void on_edit_0_text_changed();
private:
QHBoxLayout* m_pLayout;
QTextEdit* m_pEdit_0;
QTextEdit* m_pEdit_1;
};
#endif // !__MAIN_WINDOW_H__
main_window.cpp
#include "main_window.hpp"
main_window::main_window( QWidget *pParent ) : QMainWindow( pParent )
{
m_pEdit_0 = new QTextEdit( this );
m_pEdit_1 = new QTextEdit( this );
connect( m_pEdit_0, SIGNAL( textChanged() ), this, SLOT( on_edit_0_text_changed() ) );
m_pLayout = new QHBoxLayout;
m_pLayout->addWidget( m_pEdit_0 );
m_pLayout->addWidget( m_pEdit_1 );
QWidget* central_widget = new QWidget( this );
central_widget->setLayout( m_pLayout );
setCentralWidget( central_widget );
}
main_window::~main_window()
{
}
void main_window::on_edit_0_text_changed()
{
m_pEdit_1->setText( m_pEdit_0->toPlainText() );
}
main.cpp
#include "main_window.hpp"
int main( int argc, char* argv[] )
{
QApplication a(argc, argv);
main_window mw;
mw.show();
return a.exec();
}
Edit:
This would work too, but would lack in performance for huge documents:
void main_window::on_edit_0_text_changed()
{
QStringList text_in_lines = m_pEdit_0->toPlainText().split( "\n" );
m_pEdit_1->clear();
for( int i = 0; i < text_in_lines.count(); i++ )
{
m_pEdit_1->append( text_in_lines.at( i ) );
}
}