Hi I am interested in eliding text in a QLabel in the middle of the text in C++. I found the text elide mode property but it looks it only applies to QAbstractItemViews. Also here it says QLabel can elide text that doesn't fit within it, but only in one line but then doesn't say how to do it. Also here it is super easy but it's in QML/Qt Quick. Is there an easy way to do this? Do I have to override the paint event and use QFontMetrics or something like that? I'd imagine this would be as simple as setting a text elide middle property but perhaps I am mistaken. Thanks!
Here's the code thanks to Scheff's Cat.
//elidedlabel.h
#ifndef ELIDEDLABEL_H
#define ELIDEDLABEL_H
#include <QLabel>
#include <QPainter>
class ElidedLabel : public QLabel
{
Q_OBJECT
public:
explicit ElidedLabel(QWidget *parent = nullptr);
void setText(const QString &text);
const QString & text() const { return content; }
protected:
void paintEvent(QPaintEvent *event) override;
private:
QString content;
};
#endif // ELIDEDLABEL_H
//elidedlabel.cpp
#include "elidedlabel.h"
ElidedLabel::ElidedLabel(QWidget *parent)
: QLabel(parent)
{
}
void ElidedLabel::setText(const QString &newText)
{
content = newText;
update();
}
void ElidedLabel::paintEvent(QPaintEvent *event)
{
QLabel::paintEvent(event);
QPainter painter(this);
QFontMetrics fontMetrics = painter.fontMetrics();
QString elidedLine = fontMetrics.elidedText(content, Qt::ElideMiddle, width());
painter.drawText(QPoint(0, fontMetrics.ascent()), elidedLine);
}
Related
Using Qt to create an application that accepts a file drop. I have an area on my UI that I want to drop the file into, using a Qlabel. I have the function of dragging and dropping the file into the UI working, however I can drop it anywhere on the window, and not just into the Qlabel area.
I thought using
ui->label_drag->setAcceptDrops(true);
would work, however this just removed the functionality all together. What is the best way of handling this? if possible at all.
Thanks
The best way to do this is to override the QLabel class. In the dragEnterEvent be sure to call acceptProposedAction to process the move and leave events. If you don't do that, only the dragEnter event will fire.
Sample code follows. To use this in your project, add the source to your project and then right click on the label on the form and promote the item to QLabelDragDrop.
#ifndef QLABELDRAGDROP_H
#define QLABELDRAGDROP_H
#include <QLabel>
class QLabelDragDrop : public QLabel
{
Q_OBJECT
public:
explicit QLabelDragDrop(QWidget *parent = nullptr);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
signals:
public slots:
};
#endif // QLABELDRAGDROP_H
#include "qlabeldragdrop.h"
#include <QDebug>
#include <QDragEnterEvent>
#include <QDropEvent>
QLabelDragDrop::QLabelDragDrop(QWidget *parent) : QLabel(parent)
{
setAcceptDrops(true);
setMouseTracking(true);
}
void QLabelDragDrop::dragEnterEvent(QDragEnterEvent *event)
{
qDebug() << "dragEnterEvent";
event->acceptProposedAction();
}
void QLabelDragDrop::dragLeaveEvent(QDragLeaveEvent *event)
{
qDebug() << "dragLeaveEvent";
releaseMouse();
}
void QLabelDragDrop::dragMoveEvent(QDragMoveEvent *event)
{
qDebug() << "dragMoveEvent";
}
void QLabelDragDrop::dropEvent(QDropEvent *event)
{
qDebug() << "dropEvent";
}
I have a slot that receives a QString, and I want to assign the value for this parameter in another variable, or use it directly in another method of the same class.
The signal is emitted from some other class.
MyWidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include"myclass.h"
#include <QWidget>
#include <QtWidgets>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
private:
Ui::MyWidget *ui;
signals:
void redirectData(QString data);
public slots:
void sendData();
private:
MyClass *myClass; // the object to receive and output the data
};
#endif // MYWIDGET_H
MyWidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
myClass = new MyClass();
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(sendData()));
connect(this, SIGNAL(redirectData(QString)), myClass, SLOT(outputData(QString)));
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::sendData()
{
emit redirectData(ui->lineEdit->text());
myClass->show();
}
MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QWidget>
namespace Ui {
class MyClass;
}
class MyClass : public QWidget
{
Q_OBJECT
public:
explicit MyClass(QWidget *parent = 0);
~MyClass();
QString x;
private:
Ui::MyClass *ui;
signals:
void send2(QString);
public slots:
void outputData(QString data);
};
#endif // MYCLASS_H
MyClass.cpp
#include "myclass.h"
#include "ui_myclass.h"
MyClass::MyClass(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyClass)
{
ui->setupUi(this);
ui->plainTextEdit->insertPlainText(x); // show x in plainTextEdit
}
MyClass::~MyClass()
{
delete ui;
}
void MyClass::outputData(QString data){
x=data; // affect data a x
}
I want to display the value of x in the QPlainTextEdit.
This method is correct but does not fit with the rest of my code. I would like to preserve the value in x.
MyClass.cpp
#include "myclass.h"
#include "ui_myclass.h"
MyClass::MyClass(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyClass)
{
ui->setupUi(this);
}
MyClass::~MyClass()
{
delete ui;
}
void MyClass::outputData(QString data){
ui->plainTextEdit->insertPlainText(data);
}
This now an OK question, if a tad wordy and basic. It is clearly mostly complete (the .ui files are missing - but don't include them!) and reproducible.
Thus, there are two things that you can do:
Simply set the value of the plain text edit in the slot:
void MyClass::outputData(QString data){
x = data;
ui->plainTextEdit->insertPlainText(x);
}
You don't need to store the value in x at all, if you don't need it anywhere else in MyClass.
If MyClass should expose the x property to interact with other classes, you should make it so - and then use the notification signal to modify the widget:
class MyClass : public QWidget {
Q_OBJECT
Q_PROPERTY(QString x READ x WRITE setX NOTIFY xChanged)
QString m_x;
Ui::MyClass *ui;
public:
explicit MyClass(QWidget *parent = {}) {
ui->setupUi(this);
connect(this, &MyClass::xChanged, ui->plainTextEdit, &QPlainTextEdit::insertPlainText);
~MyClass {}
Q_SIGNAL void xChanged(const QString &);
Q_SLOT void setX(const QString &x) {
if (m_x != x) {
m_x = x;
emit xChanged(m_x);
}
}
QString x() const { return m_x; }
};
Other notes
The MyClass::x member should not be public.
The containers passed as method arguments should be almost always of a const reference type - to avoid the premature pessimization of making copies of objects passed. Thus you should modify the signatures as follows - in both declarations (header files) and definitions (implementation - .cpp files):
void MyClass::send2(const QString&);
void MyClass::outputData(const QString &data)
void MyWidget::redirectData(const QString &data)
The old-style connect syntax you use demands normalized signatures and doesn't change - the const and reference must be dropped.
There's no need to store the ui by pointer - simply store it by value. The C++ language then automatically takes care of memory management for you, and the default compiler-generated destructor does the job.
In self-contained examples, there's no need to use the .ui files. Creating the widgets manually and laying them out is quite trivial, as you can see below.
A reworked example
Taking all this into account, this would be a self-contained demo - with no Ui files needed. Your question should have had something like this included - it's self contained, single file. The use of the properties is not undesirable to indicate intent: those are the changeable properties of the classes.
#include <QtWidgets>
class Source : public QWidget {
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
QHBoxLayout m_layout{this};
QLineEdit m_edit;
QPushButton m_send{tr("Send")};
QString m_text;
public:
Source(QWidget *parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_edit);
m_layout.addWidget(&m_send);
connect(&m_send, &QPushButton::clicked, [this]{
setText(m_edit.text());
});
}
Q_SLOT void setText(const QString &text) {
if (text == m_text) return;
m_text = text;
emit textChanged(m_text);
}
QString text() const { return m_text; }
Q_SIGNAL void textChanged(const QString &);
};
class Destination : public QWidget {
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
QHBoxLayout m_layout{this};
QPlainTextEdit m_edit;
QString m_text;
public:
Destination(QWidget *parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_edit);
connect(this, &Destination::textChanged, &m_edit, &QPlainTextEdit::setPlainText);
}
Q_SLOT void setText(const QString &text) {
if (text == m_text) return;
m_text = text;
emit textChanged(m_text);
}
QString text() const { return m_text; }
Q_SIGNAL void textChanged(const QString &);
};
int main(int argc, char **argv) {
QApplication app{argc, argv};
Source src;
Destination dst;
QObject::connect(&src, &Source::textChanged, &dst, [&](const QString &text){
if (!dst.isVisible()) {
dst.move(src.frameGeometry().topRight()); // align the windows
dst.show();
}
dst.setText(text);
});
src.show();
return app.exec();
}
#include "main.moc"
I've been searching online for days and I cant find anything to help out with my specific problem. I'm trying to set up this dialog to accept files to be dropped into the QTreeWidget, named filesTreeWidget, but everything I've been searching online doesn't seem to make a difference. I'm pretty new to QT and C++ as well, so I'm sure that doesn't help.
Thanks for any help
Header
class FileIQ : public QDialog
{
Q_OBJECT
protected:
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
}
Cpp
FileIQ::FileIQ(QWidget *parent, DR::EnginePtr engine)
: QDialog(parent)
, ui(new Ui::FileIQ)
, engine_(engine)
{
ui->filesTreeWidget->setAcceptDrops(true);
ui->filesTreeWidget->setDropIndicatorShown(true);
setAcceptDrops(true);
}
void FileIQ::dropEvent(QDropEvent *event)
{
foreach(const QUrl &url, event->mimeData()->urls()) {
QString filename = url.toLocalFile();
qDebug() << "Dropped file:" << filename;
QTreeWidgetItem *item = new QTreeWidgetItem(ui->filesTreeWidget);
item->setText(0, filename);
}
}
void FileIQ::dragEnterEvent(QDragEnterEvent *event)
{
event->accept();
}
void FileIQ::dragMoveEvent(QDragMoveEvent * event)
{
event->accept();
}
void FileIQ::dragLeaveEvent(QDragLeaveEvent * event)
{
event->accept();
}
First, the right thing is to implement drag and drop within QTreeWidget, not inside QDialog. To do this we must create a class that inherits from QTreeWidget and we must implement the following protected methods:
bool QTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index,
const QMimeData *data, Qt::DropAction action)
Handles the data supplied by a drag and drop operation that ended with
the given action in the index in the given parent item.
The default implementation returns true if the drop was successfully
handled by decoding the mime data and inserting it into the model;
otherwise it returns false.
QStringList QTreeWidget::mimeTypes() const
Returns a list of MIME types that can be used to describe a list of
treewidget items.
Qt::DropActions QTreeWidget::supportedDropActions() const
Returns the drop actions supported by this view.
From the above we implemented this class:
#ifndef TREEWIDGET_H
#define TREEWIDGET_H
#include <QDropEvent>
#include <QTreeWidget>
#include <QMimeData>
#include <QFileInfo>
class FilesTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
FilesTreeWidget(QWidget *parent= Q_NULLPTR):
QTreeWidget(parent)
{
setAcceptDrops(true);
setDropIndicatorShown(true);
setColumnCount(2);
}
protected:
bool dropMimeData(QTreeWidgetItem *parent, int /*index*/, const QMimeData *data, Qt::DropAction /*action*/)
{
for(const QUrl url: data->urls()) {
const QFileInfo info( url.toLocalFile());
if(info.isFile()){
QTreeWidgetItem *item;
if (parent){
item = new QTreeWidgetItem(parent);
parent->setExpanded(true);
}
else
item = new QTreeWidgetItem(this);
item->setText(0, info.fileName());
item->setText(1, info.filePath());
}
}
return true;
}
QStringList mimeTypes () const
{
return QStringList()<<"text/uri-list";
}
Qt::DropActions supportedDropActions () const
{
return Qt::CopyAction;
}
};
#endif // TREEWIDGET_H
The complete example can be found in the following link. If you already have a QTreeWidget assigned by Qt Designer the simplest solution is to promote the Qt Designer QTreeWidget to use the new class.
Output:
I was able to simulate the Right-Click event by subclassing the QTableWidget:
header file:
#ifndef QRIGHCLICKTABLE_H
#define QRIGHCLICKTABLE_H
#include <QTableWidget>
#include <QMouseEvent>
class QRightClickTable : public QTableWidget
{
Q_OBJECT
public:
explicit QRightClickTable(QWidget *parent = 0);
private slots:
void mousePressEvent(QMouseEvent *e);
signals:
void rightClicked();
public slots:
};
#endif // QRIGHCLICKTABLE_H
cpp file
QRightClickTable::QRightClickTable(QWidget *parent) :
QPushButton(parent)
{
}
void QRightClickTable::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::RightButton)
emit rightClicked();
}
QRightClickTable *button = new QRightClickTable(this);
ui->gridLayout->addWidget(button);
connect(button, SIGNAL(rightClicked()), this, SLOT(onRightClicked()));
void MainWindow::onRightClicked()
{
qDebug() << "User right clicked me";
}
Now, right-click works correctly, but there are other problems with QTableWidget: all other mouse events, such as the left click to select a cell, no longer work.
Can you help me? I know I need to call the base class implementation in my override of mousePressEvent, you could show me how with a little piece of code?
Change your event handler like this :
void QRightClickTable::mousePressEvent(QMouseEvent *e) {
if(e->button()==Qt::RightButton) {
emit rightClicked();
} else {
QTableWidget::mousePressEvent(e);
}
}
Situation:
I have a Dialog class in Qt on which I draw a raster of squares. The squares are implemented in the MySquare class (MySquare: QGraphicsItem).
Question:
I want to signal the Dialog slot setTarget() that a square was clicked (and obviously I want to give it some information about that square like for example it's x and y coordinates with it later). However I get an 'undefined reference to 'MySquare::targetChanged()' error. I have looked for a solution but have not found any; anybody an idea?
edit: I have added a Q_OBJECT macro inside the MySquare however the error does not dissapear and I get an additional "'undefined reference to 'vtable for MySquare()' error
dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
public slots:
void setTarget();
private:
Ui::Dialog *ui;
QGraphicsScene *scene;
};
dialog.cpp
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
MySquare *item;
item = new MySquare(30,30,30,30);
QObject::connect(item,SIGNAL(targetChanged()),this,SLOT(setTarget()));
}
void Dialog::setTarget(){
qDebug() << "signal recieved" << endl;
}
mysquare.h
#include <QGraphicsItem>
#include <QPainter>
#include <QDebug>
#include <QKeyEvent>
#include <QObject>
class MySquare : public QGraphicsItem, public QObject
{
Q_OBJECT
public:
MySquare(int x,int y,int h, int w);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
int x,y,h,w;
signals:
void targetChanged();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
mysquare.cpp
#include "mysquare.h"
#include <QGraphicsSceneDragDropEvent>
#include <QWidget>
MySquare::MySquare(int _x,int _y, int _w, int _h)
{
setAcceptDrops(true);
color=Qt::red;
color_pressed = Qt::green;
x = _x;
y = _y;
w = _w;
h = _h;
}
QRectF MySquare::boundingRect() const
{
return QRectF(x,y,w,h);
}
void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rec = boundingRect();
QBrush brush(color);
if (Pressed){
brush.setColor(color);
} else {
brush.setColor(color_pressed);
}
painter->fillRect(rec,brush);
painter->drawRect(rec);
}
void MySquare::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=true;
update();
QGraphicsItem::mousePressEvent(event);
emit targetChanged();
}
void MySquare::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=false;
update();
QGraphicsItem::mouseReleaseEvent(event);
qDebug() << "mouse Released";
}
void MySquare::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
qDebug() << "mouse Moved";
QDrag *drag = new QDrag(event->widget());
QMimeData *mime = new QMimeData;
drag->setMimeData(mime);
drag->exec();
}
void MySquare::keyPressEvent(QKeyEvent *event){
//out of bounds check?
int x = pos().x();
int y = pos().y();
QGraphicsItem::keyPressEvent(event);
}
Edit: If you have an undefined reference to a vtable, then it's probably because you did not implement certain virtual functions. Have you implemented the mouse event handlers of the MySquare class somewhere? Have you also implemented the boundingRect() and paint() functions of the MySquare class?
Old answer:
You need to write the Q_OBJECT macro after the opening '{' in the MySquare class. Also Qt will complain about multiple inheritance at some point. Therefore, instead of inheriting from QGraphicsItem and QObject, inherit from QGraphicsObject instead.
The actual reason, the linker complains about a missing function definition is the following: When you put a Q_OBJECT macro into the right place, then the Qt Meta Object Compiler (MOC) will generate a new cpp file for the project which contains the definition of the signal functions of the respective class. So Qt implements a function for every signal for you. If you don't insert the Q_OBJECT macro, then MOC won't do anything for you and the function definition for the signal will be missing to the linker.
Try to do the following steps:
Build-clean
Build-run qmake
Build-build.
Maybe the moc Option is not Set in the Makefile after you added the Q_Object Makro. Therefore you have to refresh your Makefile.
Hope this helps.