I use a QPlainTextEdit for a code editor that also shows line numbers.
But when I press shift+return a the editor makes a break, but the line number don't increases.
I think in html it would just be a <br/> instead of a new <p> tag...
Have a look at the screenshot...
You should probably be using QTextEdit since this is rich text we're talking about.
Override virtual void keyPressEvent ( QKeyEvent * e ). You can call QTextEdit::keyPressEvent in the implementation to delegate non-special cases.
You can, actually, use object with eventFilter and installEventFilter function.
#ifndef SHIFTENTERFILTER_H
#define SHIFTENTERFILTER_H
#include <QObject>
#include <QEvent>
#include <QKeyEvent>
class ShiftEnterFilter : public QObject
{
protected:
virtual bool eventFilter(QObject *, QEvent *event) {
if(event -> type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast <QKeyEvent> (event);
if((keyEvent -> modifiers() & Qt::ShiftModifier) && ((keyEvent -> key() == Qt::Key_Enter) || (keyEvent -> key() == Qt::Key_Return)))
return true;
}
return false;
}
public:
ShiftEnterFilter(QObject *parent = 0) : QObject(parent) {}
};
#endif
Just install this filter to your QPlainTextEdit
// code
ui -> plainTextEdit -> installEventFilter(new ShiftEnterFilter(this));
// code
Try this (CodeEdit inherits QPlainTextEdit):
/**
* override keyPressEvent, change behaviour of shift + enter(return)
* #brief CodeEdit::keyPressEvent
* #param event
*/
void CodeEdit::keyPressEvent(QKeyEvent *event)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
// disable shift + enter(return)
if ((keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)) {
event->ignore();
return;
}
QPlainTextEdit::keyPressEvent(event);
}
Related
I am promoting the QDoubleSpinBox class as i want to catch the mouseDoubleClick Event.
This is the Promoted class.
class SumDoubleBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SumDoubleBox(QWidget* parent = nullptr);
void setSingleStep(double val);
double singleStep() const;
void stepBy(int steps) override;
protected:
virtual void focusInEvent(QFocusEvent *e) override;
public slots:
void setZero();
void setOne();
signals:
int signalUndoRedo();
private:
double m_defaultStep = 1.0;
double m_CurrentStep;
bool m_stepUp;
};
SumDoubleBox::SumDoubleBox(QWidget* parent) : QDoubleSpinBox(parent)
{
SumLineEdit* lineEdit = new SumLineEdit(this);
setLineEdit(lineEdit);
setMinimum(0.0);
setMaximum(99999.0);
}
Since i am creating a pointer in the Constructor of the SumDoubleBox Class.
SumLineEdit* lineEdit = new SumLineEdit(this);
Do i need to Explicitly delete this in the Destructor ?
/////////////////////////////////////////////////////////////////
The Defination of the SumLineEdit class.
class SumLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit SumLineEdit(QWidget* parent = nullptr) { };
protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
};
void SumLineEdit::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
selectAll();
event->accept();
return;
}
QLineEdit::mouseDoubleClickEvent(event);
}
Since you've parented the new lineedit to the SumDoubleBox, it should get deleted with its parent.
There is, however, a more Qt-centric way to handle this case that I would strongly recommend you use or at least look into: Installing an event filter on the built-in line edit and handling the events for it. I've made many spinbox variants, and this approach usually works out best:
SumDoubleBox::SumDoubleBox( QWidget *parent ) :
QDoubleSpinBox( parent )
{
lineEdit()->installEventFilter( this );
}
bool SumDoubleBox::eventFilter( QObject *object, QEvent *event )
{
if( object == lineEdit() && event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
if( mouseEvent->button() == Qt::LeftButton )
{
selectAll();
event->accept();
return true; //swallow the event
}
}
return false; //let the event through to the lineedit
}
This makes the custom class entirely redundant. It's nice because inheritance is a big hammer that easily gets out of control as a codebase scales up, and if there are other simpler ways to achieve your goal, it's often worth considering.
I know how to apply a keyboard shortcut to an action. And in some software such as Visual Studio there are shortcuts that do the job in more than one step (such as Ctrl+K,Ctrl+C to comment the code).
Another example of that in Sublime Text:
I wonder whether or not it is possible to implement in Qt.
You can create it by using the multiple arguments constructor for QKeySequence.
like this:
auto ac = new QAction(this);
ac->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K, Qt::CTRL + Qt::Key_C));
Try this:
action->setShortcut("Ctrl+K,Ctrl+C");
QKeySequence may be implicitly created from QString.
Due to documentation:
Up to four key codes may be entered by separating them with commas, e.g. "Alt+X,Ctrl+S,Q".
MOC generates almost same code when you create shortcut for a QAction via Qt Designer. But it makes it slightly different:
action->setShortcut(QApplication::translate("MainWindow", "Ctrl+K, Ctrl+C", 0));
but it's actually same thing.
You can use eventFilter to get mouse & keyboard events.
I use boolean to get first and second key, Ctrl + K then C.
I made you a sample code it's working.
.cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
firstKey = false;
secondKey = false;
this->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if (object == this &&event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if ((keyEvent->key() == Qt::Key_Control))
{
firstKey = true;
return true;
}
else if ((keyEvent->key() == Qt::Key_K))
{
secondKey = true;
return true;
}
else if ((keyEvent->key() == Qt::Key_C))
{
if(firstKey && secondKey)
{
firstKey = false;
secondKey = false;
QMessageBox::information(this, "", "Ctrl + k + c");
}
return true;
}
else
return false;
}
else
return false;
}
void MainWindow::keyReleaseEvent(QKeyEvent *e)
{
if (e->type() == QEvent::KeyRelease)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if ((keyEvent->key() == Qt::Key_Control))
{
firstKey = false;
}
}
}
.h file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QMessageBox>
#include <QKeyEvent>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
bool firstKey;
bool secondKey;
bool eventFilter(QObject *object, QEvent *event);
void keyReleaseEvent(QKeyEvent *e);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
I want to draw a line using QGraphicsLineItem. What exactly I want is that on clicking at GraphicsView, after second click Line must be drawn. I am confused with the syntax of QGraphicsLineItem and also how to use it. I am new to Qt. Please help me out to solve this problem.
You can use this code snippet.
*h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QStack>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
public slots:
private:
QStack<QPoint> stack;
};
#endif // GRAPHICSSCENE_H
*.cpp
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPoint pos = mouseEvent->scenePos().toPoint();
if(stack.isEmpty())
stack.append(pos);
else if(stack.count() == 1)
{
stack.append(pos);
addLine(QLine(stack.pop(),stack.pop()),QPen(Qt::green));
}
}
}
Usage:
GraphicsScene *scene = new GraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
Edit: more beautiful solution which works as you need.
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPoint pos = mouseEvent->scenePos().toPoint();
if(stack.isEmpty())
stack.append(pos);
else
addLine(QLine(pos,stack.pop()),QPen(Qt::green));
}
}
You can derive the graphics view/scene and override the mousePressEvent
Below is example using derived QGraphicsScene and overridden mousePressEvent
Class Definition :
class MyScene : public QGraphicsScene
Data Members :
QList<QPointF> m_clickPositions;
int m_mode;
Code :
void MyScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(false == sceneRect().contains(event->scenePos()))
{
QGraphicsScene::mousePressEvent(event);
}
else if(Qt::LeftButton == event->button() && m_mode == ConstructMode)
{
m_clickPositions.append(event->scenePos());
if(m_clickPositions.size() == 2)
{
QLineF lineF(m_clickPositions[0], m_clickPositions[1]);
QGraphicsLineItem* item = this->addLine(lineF);
m_clickPositions.clear();
m_mode = ScrollMode;
}
}
}
I had used something similar in my project and extracted the code. Hope this helps.
Please comment is this is not working.
Edit ::
ConstructMode and Scroll mode are used in the above program so that I can distinguish whether I want to Draw/Construct or just scroll the scene. You can remove them and the declaration of m_mode if not required by you.
If you want to use the modes you can define some public constants and add a method setMode(). Please see the code below.
MyScene.h or some Constant file if you have one
#define ConstructMode 100
#define ScrollMode 101
And add the following function
void MyScene::setMode(int mode)
{
m_mode = mode;
}
After this if you want to enter the construction mode you will need to call myScene->setMode(ConstructMode) everytime, as after the item is constructed the mode is reset to ScrollMode.
I am trying to handle mult-itouch events in this simple QWidget based program but not able to receive any touch events.
"MyWidget.h"
#include <QWidget>
class QPaintEvent;
class QEvent;
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *);
bool event ( QEvent * event );
};
"MyWidget.cpp"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
setAttribute(Qt::WA_AcceptTouchEvents);
}
void MyWidget::paintEvent(QPaintEvent *evt) {
QPainter painter(this);
painter.fillRect(rect(),QColor(0,255,0));
// painter.drawText(QPoint(rect().left(),rect().top()),"Hello world");
}
bool MyWidget::event(QEvent *event){
if(event->type() == QEvent::TouchBegin ||
event->type() == QEvent::TouchEnd ||
event->type() == QEvent::TouchUpdate ){
qDebug() <<"Touch events";
}
else if(event->type() == QEvent::MouseButtonDblClick) {
qDebug() <<"double click";
}
return QWidget::event(event);
}
Am I missing anything here ?
To make touch events work add the following to your MainWindow:
MyWidget *myWidget = ...;
setCentralWidget(myWidget);
In the MyWidget constructor add:
setAttribute(Qt::WA_AcceptTouchEvents);
//grabGesture(Qt::PinchGesture);
//setAttribute(Qt::WA_InputMethodEnabled);
//setFocusPolicy(Qt::WheelFocus);
setAttribute(Qt::WA_StaticContents);
I'm using QFileDialog to select a directory. I'm having an issue that I'm unable to resolve. I've spent a lot of time googling for this but have come up with zilch.
I specify the starting directory (say /home/dhoti/downloads) and I want to disable navigation above this directory. For example the user should not be allowed to go to /home/dhoti or /tmp etc. How do I achieve this?
Here is my code:
QFileDialog dlg(this, "Select Firmware Version");
dlg.setDirectory("/home/dhoti/downloads");
dlg.setFileMode(QFileDialog::DirectoryOnly);
dlg.setOption(QFileDialog::ReadOnly, true);
dlg.setOption(QFileDialog::HideNameFilterDetails, true);
dlg.setViewMode(QFileDialog::List);
dlg.setAcceptMode(QFileDialog::AcceptOpen);
dlg.exec();
qDebug() << "selected files: " << dlg.selectedFiles();
thanks for any help
Dhoti
You can detect when the current directory changes and if it is beyond your limit, set the directory back to the limit directory.
You can do this by executing the dialog non-blocking, and connecting the QFileDialog::directoryEntered(const QString& directory) signal to a slot of your own where you can do the checking. If it fails your check, set the current directory to the limit directory by QFileDialog::setDirectory(const QString& directory).
Disclaimer I have not tried this, but I'll be surprised if it does not work.
Try the following:
filedialog.h
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
class QEvent;
#include <QFileDialog>
#include <QString>
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
explicit FileDialog(QWidget *parent = 0);
public:
bool eventFilter(QObject *o, QEvent *e);
void setTopDir(const QString &path);
QString topDir() const;
private:
bool pathFits(const QString &path) const;
private slots:
void checkHistory();
void checkGoToParent();
void checkLineEdit(const QString &text);
private:
QString mtopDir;
};
#endif // FILEDIALOG_H
filedialog.cpp
#include "filedialog.h"
#include <QString>
#include <QStringList>
#include <QFileDialog>
#include <QList>
#include <QToolButton>
#include <QDir>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QEvent>
#include <QKeyEvent>
#include <QAbstractButton>
#include <QCompleter>
#include <QAbstractItemView>
#include <QFileInfo>
FileDialog::FileDialog(QWidget *parent) :
QFileDialog(parent)
{
connect(this, SIGNAL(directoryEntered(QString)), this, SLOT(checkHistory()));
connect(this, SIGNAL(directoryEntered(QString)), this, SLOT(checkGoToParent()));
connect(findChild<QToolButton *>("backButton"), SIGNAL(clicked()), this, SLOT(checkGoToParent()));
connect(findChild<QToolButton *>("forwardButton"), SIGNAL(clicked()), this, SLOT(checkGoToParent()));
connect(findChild<QLineEdit *>("fileNameEdit"), SIGNAL(textChanged(QString)), this, SLOT(checkLineEdit(QString)));
findChild<QLineEdit *>("fileNameEdit")->installEventFilter(this);
findChild<QWidget *>("listView")->installEventFilter(this);
findChild<QWidget *>("treeView")->installEventFilter(this);
findChild<QLineEdit *>("fileNameEdit")->completer()->popup()->installEventFilter(this);
setOption(DontUseNativeDialog, true);
}
bool FileDialog::eventFilter(QObject *o, QEvent *e)
{
if (e->type() != QEvent::KeyPress)
return false;
int key = static_cast<QKeyEvent *>(e)->key();
if (o->objectName() == "listView" || o->objectName() == "treeView")
{
return (Qt::Key_Backspace == key && !pathFits(directory().absolutePath()));
}
else
{
if (Qt::Key_Return != key && Qt::Key_Enter != key)
return false;
QString text = findChild<QLineEdit *>("fileNameEdit")->text();
QString path = QDir::cleanPath(directory().absolutePath() + (text.startsWith("/") ? "" : "/") + text);
bool a = QDir(text).isAbsolute();
return !((!a && pathFits(path)) || (a && pathFits(text)));
}
}
void FileDialog::setTopDir(const QString &path)
{
if (path == mtopDir)
return;
mtopDir = (!path.isEmpty() && QFileInfo(path).isDir()) ? path : QString();
if (!pathFits(path))
{
setDirectory(mtopDir);
checkHistory();
checkLineEdit(findChild<QLineEdit *>("fileNameEdit")->text());
}
else
{
QLineEdit *ledt = findChild<QLineEdit *>("fileNameEdit");
ledt->setText(ledt->text());
}
findChild<QWidget *>("lookInCombo")->setEnabled(mtopDir.isEmpty());
findChild<QWidget *>("sidebar")->setEnabled(mtopDir.isEmpty());
checkGoToParent();
}
QString FileDialog::topDir() const
{
return mtopDir;
}
bool FileDialog::pathFits(const QString &path) const
{
return mtopDir.isEmpty() || (path.startsWith(mtopDir) && path.length() > mtopDir.length());
}
void FileDialog::checkHistory()
{
QStringList list = history();
for (int i = list.size() - 1; i >= 0; --i)
if (!pathFits(list.at(i)))
list.removeAt(i);
setHistory(list);
}
void FileDialog::checkGoToParent()
{
findChild<QToolButton *>("toParentButton")->setEnabled(pathFits(directory().absolutePath()));
}
void FileDialog::checkLineEdit(const QString &text)
{
QAbstractButton *btn = findChild<QDialogButtonBox *>("buttonBox")->buttons().first();
QString path = QDir::cleanPath(directory().absolutePath() + (text.startsWith("/") ? "" : "/") + text);
bool a = QDir(text).isAbsolute();
btn->setEnabled(btn->isEnabled() && ((!a && pathFits(path)) || (a && pathFits(text))));
}
This code may look like some magic, and it's not perfect, but it works. I searched for QFileDialog child objects names in Qt sources and used
findChild()
method to access them. All you need is just use the
setTopDir()
method to specify a directory above which users are not allowed to go.
Here's an example project using this class: https://docs.google.com/file/d/0B3P3dwuDIZ1-Q19FbkFMY2puUE0/edit?usp=sharing
You can use the solution of #ololoepepe. And clean unwanted entries in the comboBox on the top with this:
connect(findChild<QComboBox *>("lookInCombo"), static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &FileDialog::checkComboBox);
void FileDialog::checkComboBox(int index) {
int i;
QComboBox *cb = findChild<QComboBox *>("lookInCombo");
if (index == 0 && cb->model()->rowCount() > 1) {
for (i = 0; i < cb->model()->rowCount(); ++i) {
if (!pathFits(cb->model()->index(i, 0).data().toString() + "/")) {
cb->model()->removeRow(i);
--i;
}
}
}
}
Here is the simplest solution, with the minimum steps required to limit a directory traversal.
Idea: use public signal directoryEntered(const QString &) of QFileDialog to get notification when directory might be changed, implement slot for it in one of your classes and place there a logic for making sure that directory is the one you need.
QFileDialog dialog(this);
connect(&dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(onFileDialogDirectoryChanged(const QString &)));