I have a qplaintextedit that is loaded dynamically with text contents, with QString.
I want to add an option to the menu that appears when we right click on the qplaintextedit. How can I do it programmatically so that I am able to add my own menu item to the default menu item? Any ideas would be appreciated.
You can sublcass QPlainTextEdit and reimplement the method contextMenuEvent(QContextMenuEvent *event). Alternatively you can add an event filter to a QPlainTextEdit and catch the QContextMenuEvent.
In your implementation, you can call QMenu *QPlainTextEdit::createStandardContextMenu(const QPoint &position) to create the default menu of the text edit and than add your custom items to it.
Example (subclass):
void MyTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu* menu = createStandardContextMenu(event->pos());
QAction* clearAction = menu->addAction("Clear");
QAction* choosedAction = menu->exec(event->globalPos());
//...
delete menu;
}
See:
Event handlers
Event filters
QPlainTextEdit::contextMenuEvent
QPlainTextEdit::createStandardContextMenu
You have to reimplement the function : void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
More details in the doc here : http://doc.qt.io/qt-5/qplaintextedit.html#contextMenuEvent
It is not necessary to subclass QPlainTextEdit, nor to use an event filter. You can do it all in the main widget of your application. The following worked for me using Qt versions 4.7.4 and 4.8.5.
In the Designer:
You add a QPlainTextEdit widget whose name is "textbox".
In MainWindow.h:
// Auto generated from MainWindow.ui
#include "ui_MainWindow.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
/// The auto generated user interface.
Ui::MainWindowClass ui;
...
}
In MainWindow.cpp:
MainWindow::MainWindow( QWidget * pParent, Qt::WFlags flags )
: QMainWindow( pParent, flags )
, ui( )
{
ui.textbox->setContextMenuPolicy( Qt::CustomContextMenu );
connect( ui.textbox, SIGNAL( customContextMenuRequested( QPoint ) ),
this, SLOT( onTextboxCustomContextMenuRequested( QPoint ) ) );
}
void MainWindow::onTextboxCustomContextMenuRequested( QPoint p )
{
// Start with the standard menu.
QMenu * pMenu = ui.textbox->createStandardContextMenu();
QAction * pAction;
// Clear.
// Because QPlainTextEdit::clear() is a slot method, I can connect directly to it.
pAction = new QAction( "Clear", this );
connect( pAction, SIGNAL( triggered() ), ui.textbox, SLOT( clear() ) );
pMenu->addAction( pAction );
// Show the menu.
QPoint q = ui.textbox->mapToGlobal( p );
pMenu->exec( q );
// The menu's ownership is transferred to me, so I must destroy it.
delete pMenu;
}
Related
I'm pretty new to c++ and qt. I'm not sure if i use the right terminology describe what I want to achieve. But here it goes.
My application spawns and removes widgets in a gridlayout when the user pushes buttons. Managed to do this successfully. However when the user uses the spawned widgets I want the widgets to interact with each other.
QList<QLineEdit*> m_ptrLEPathList;
QList<QPushButton*> m_ptrPBList;
qint8 m_noFields;
void MainWindow::on_pbIncFields_clicked()
{
//create widgets and place on a new row in a gridLayout
QLineEdit *lineEditPath = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
//storing pointers in lists to be able to delete them later.
m_ptrLEPathList.append(lineEditPath);
m_ptrPBList.append(pushButton);
ui->gridLayout->addWidget(m_ptrLEPathList.last(),m_noFields,0);
ui->gridLayout->addWidget(m_ptrPBList.last(),m_noFields,1);
connect(m_ptrPBList.last(), SIGNAL(clicked(bool), this, SLOT(on_addPath()));
m_noFields++;
}
void MainWindow::on_pbDecFields()
{
//delete last spawned widgets
}
void MainWindow::on_addPath()
{
QFileDialog getPath();
getPath.exec();
//somehow set the text of the line edit spawned on the same row as the pushbutton
}
So my slot is executed when I push any spawned button but I have no idea how to store the data from the file dialog in the related lineEdit.
Is the basic idea of what I'm trying to do ok or is there any other solution to achieve the fuctionality I'm looking for?
In on_addPath slot you can use QObject::sender method to get the clicked button, and, assuming m_ptrLEPathList and m_ptrPBList lists are equal, you can easily get the corresponding QLineEdit:
void MainWindow::on_addPath()
{
QFileDialog dialog;
if (!dialog.exec())
{
return;
}
QStringList fileNames = dialog.selectedFiles();
if (fileNames.isEmpty())
{
return;
}
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn)
{
return;
}
Q_ASSERT(m_ptrPBList.size() == m_ptrLEPathList.size());
int index = m_ptrPBList.indexOf(btn);
if (index == -1)
{
return;
}
QLineEdit *edit = m_ptrLEPathList.at(index);
edit->setText(fileNames.first());
}
You are including 'on_addPath' function out of the scope of the 'MainWindow' class, so when the slot is called you have not access to member elements in the class.
Try to include the slot function into the class and check if you have direct access to the member elements. Also, the 'lineEditPath' element must be a member object, so it must be included into the class definition.
Something like this:
void MainWindow::on_addPath()
{
QFileDialog getPath();
getPath.exec();
QStringList fileNames = dialog.selectedFiles();
if (fileNames.isEmpty())
{
return;
}
m_lineEditPath->setText(fileNames.first());
}
First off, void on_addPath() must be void MainWindow::on_addPath()
As for linking the data from QFileDialog it is simple. Try this:
void MainWindow::on_addPath() {
/* Get the push button you clicked */
QPushButon *btn = qobject_cast<QPushButton*>( sender() );
/* Make sure both the lists have the same size */
Q_ASSERT(m_ptrPBList.size() == m_ptrLEPathList.size());
/* If the sender is a button in your list */
if ( m_ptrPBList.contains( btn ) ) {
/* Get the index of your push button */
int idx = m_ptrPBList.indexOf( btn );
/* Get the corresponding line edit */
QLineEdit *le = m_ptrLEPathList.at( idx );
/* Get your path */
QString path = QFileDialog::getOpenFileName( this, "Caption", "Default Location" );
/* If you path is not empty, store it */
if ( not path.isEmpty() )
le->setText( path );
}
}
Add a map to private section
QMap<QPushButton*, QLineEdit*> map;
Then
QLineEdit *lineEditPath = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
map.insert(pushButton, lineEditPath);
You can use sender() method like follow:
void on_addPath()
{
QFileDialog getPath();
getPath.exec();
QObject* obj = sender();
QPushButton *pb = 0;
if((pb = qobject_cast<QPushButton *>(obj)) != 0) {
QLineEdit* lineEdit = map->value(pb, 0);
if( lineEdit != 0 )
lineEdit->setText( getPath.<some function to get selected file name> );
}
}
I think the cleanest solution would be to contain the QLineEdit and QPushButton in a custom widget class, if it suits your project. That way you could use the file dialog inside this class, and you won't have to store the widgets in lists. It is hard to give you all the information, as you didn't really provide any details what your application is supposed to do. But in any case, the custom widget class would look something like this (you should define all the functions inside a .cpp file):
#ifndef WIDGETCONTAINER_H
#define WIDGETCONTAINER_H
#include <QWidget>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QFileDialog>
class WidgetContainer : public QWidget
{
Q_OBJECT
public:
WidgetContainer(QWidget *parent = 0) : QWidget(parent)
{
setLayout(new QHBoxLayout);
button.setText("BUTTON");
layout()->addWidget(&lineEdit);
layout()->addWidget(&button);
connect(&button, SIGNAL(clicked()), this, SLOT(buttonPressed()));
}
private:
QLineEdit lineEdit;
QPushButton button;
private slots:
void buttonPressed()
{
QString filename = QFileDialog::getSaveFileName();
lineEdit.setText(filename);
}
};
#endif // WIDGETCONTAINER_H
I'm making the board game Puerto Rico in C++ and Qt. One of the features will be that after they've chosen the Major Role (button 1) each player (3 players in total) kan build a building. So i have a button for each building, is there a way that i get the information (like the name of the button) when the player clicked on it?
Kind regards
If you connect a signal of QPushButton to a slot, you can call sender() inside the slot to get the object which sent the signal.
Example slot code:
void MainWindow::onButtonPress()
{
QObject* senderObj = sender();
if (senderObj->isWidgetType())
{
QPushButton* button = qobject_cast<QPushButton*>(senderObj);
if (button)
{
// button is the QPushButton who emmited this signal
}
}
}
If you created building1Button, building2Button, building3Button buttons in the designer, you can try the followings.
In your class constructor you need to connect the clicked() SIGNAL from all building buttons to the same SLOT.
Game::Game()
{
// ...
connect( ui->building1Button, SIGNAL( clicked() ), this, SLOT( onBuildingClicked() ) );
connect( ui->building2Button, SIGNAL( clicked() ), this, SLOT( onBuildingClicked() ) );
connect( ui->building3Button, SIGNAL( clicked() ), this, SLOT( onBuildingClicked() ) );
// ...
}
And in that SLOT you can use sender() function to check which button was clicked.
void Game::onBuildingClicked()
{
QPushButton* clickedButton = qobject_cast< QPushButton* >( sender() );
if ( clickedButton )
{
const QString clickedBuilding = clickedButton->text();
const QString clickedButtonName = clickedButton->objectName();
}
}
I do this in Qt Creator. I want to change my QStackedLayout with only QPushButton, not QComboBox. Is this possible? Anybody has implemented this? I got many example from Qt Documentation but all examples use QComboBox (now QPushButton like I need). This is my code:
#mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
Dialog::Dialog()
{
QVBoxLayout *mainlayout = new QVBoxLayout;
QVBoxLayout *layouta = new QVBoxLayout;
QVBoxLayout *layoutb = new QVBoxLayout;
QPushButton *tombola = new QPushButton("A");
QPushButton *tombolb = new QPushButton("B");
QPushButton *tombolc = new QPushButton("C");
QFrame *framea = new QFrame;
QFrame *frameb = new QFrame;
QStackedLayout *stackia = new QStackedLayout;
layouta->addWidget(tombola);
layoutb->addWidget(tombolb);
framea->setLayout(layouta);
frameb->setLayout(layoutb);
framea->setMinimumSize(88,88);
frameb->setMinimumSize(88,88);
//building frame
framea->setFrameShape(QFrame::StyledPanel);
framea->setFrameShadow(QFrame::Raised);
frameb->setFrameShape(QFrame::StyledPanel);
frameb->setFrameShadow(QFrame::Raised);
//get c button smaller
tombolc->setMaximumWidth(33);
stackia->addWidget(framea);
stackia->addWidget(frameb);
stackia->addWidget(tombolc);
mainlayout->addLayout(stackia);
QPushButton *tombold = new QPushButton("D");
mainlayout->addWidget(tombold);
setLayout(mainlayout);
connect(tombold, SIGNAL(clicked()), stackia, SLOT(setCurrentIndex(1))); //CONNECTOR
}
RESULT
Qt Creator says:
Object::connect: No such slot QStackedLayout::setCurrentIndex(1)
What is my mistake?
At second chance after searching and asking for 4 days, I have changed my connect() and function code into:
CONNECTOR:
connect(tombold, SIGNAL(clicked()), stackia, SLOT(change_stack()));
FUNCTION:
void Dialog::change_stack()
{
stackia->setCurrentIndex(1);
}
RESULT
But Qt Creator says:
Object::connect: No such slot QStackedLayout::change_stack()
and immediately the window closed.
At my sight, my code has error. But I don't know what error so I can't change QStackLayout content/page into another page. What is my mistake? I believe this is actually quite simple but I just don't know where is the error.
Any suggestion?
You should add change_stack function to your Dialog class and connect to it like this:
class Dialog : public QWidget
{
...
private slots:
void change_stack();
private:
QStackedLayout *stackia;
}
Dialog::Dialog
{
...
connect(tombold, SIGNAL(clicked()), this, SLOT(change_stack()));
...
}
void Dialog::change_stack()
{
stackia->setCurrentIndex(1);
}
Concerning
connect(tombold, SIGNAL(clicked()), stackia, SLOT(setCurrentIndex(1)));
A slot can only have as many parameters or less parameters than a signal.
E.g. you can do
connect( sender , SIGNAL( somethingHappened( const QString& ) ),
receiver, SLOT ( doSomething ( const QString& ) ) );
and you can do
// connect signal and ignore the parameter
connect( sender , SIGNAL( somethingHappened( const QString& ) ),
receiver, SLOT ( doSomethingElse ( ) ) );
but you cannot do
// this will not work
connect( sender , SIGNAL( somethingElseHappened( ) ),
receiver, SLOT ( doSomething ( const QString& ) ) );
What you probably want is something like this:
Dialog::Dialog
{
...
// This must be a member variable
_stackia = new QStackedLayout();
tombola->setProperty( "tabpage", 1 );
tombolb->setProperty( "tabpage", 2 );
tombolc->setProperty( "tabpage", 3 );
connect( tombola, SIGNAL( clicked () ),
this , SLOT ( tombol_clicked() ) );
connect( tombolb, SIGNAL( clicked () ),
this , SLOT ( tombol_clicked() ) );
connect( tombolc, SIGNAL( clicked () ),
this , SLOT ( tombol_clicked() ) );
}
// This must be defined as slot in the header
void Dialog::tombol_clicked()
{
int index = sender()->property( "tabpage" ).toInt();
_stackia->setCurrentIndex( index );
}
I've made some QPushbuttons like QPushButton **btn and I want to know when the user clicks on one of them using QMouseEvent here is the code but this idea does not work at all any ideas??
void Game::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
btn[ev->x()][ev->y()].setStyleSheet("background-color : black;");
}
else
{
btn[ev->x()][ev->y()].setStyleSheet("background-color : red;");
}
that else part is for right click
and here is the code that generates the buttons
void Game::MakeButton()
{
btn = new ApButton*[column];
hrztl = new QHBoxLayout[column];
hrztl->setSpacing(0);
for(int i=0; i<column;i++)
{
btn[i] = new ApButton[row];
for(int j=0; j<row; j++)
{
btn[i][j].setRowCol(i,j);
btn[i][j].setFixedSize(50,50);
hrztl[i].addWidget(&btn[i][j]);
}
ui->MainLayout->addLayout(&hrztl[i]);
}
ui->MainLayout->setSpacing(0);
}
ApButton is a class that inherits QPushButton
This is a good example of use for a QSignalMapper, as seen there: http://qt-project.org/doc/qt-5.0/qtcore/qsignalmapper.html#details
ButtonWidget::ButtonWidget(QStringList texts, QWidget *parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i) {
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}
connect(signalMapper, SIGNAL(mapped(QString)),
this, SIGNAL(clicked(QString)));
setLayout(gridLayout);
}
In that example, every button is identified by its title, as a QString. The mapper allows you to retrieve the corresponding button's title when one of them is clicked.
Switch your
Game::mousePressEvent(QMouseEvent *e)
to
ApButton::mousePressEvent(QMouseEvent *e)
since you are trying to implement the Press Event of the Button. If you only want to have the moment of the button being pressed and not changing Button behaviour with this, use a SIGNAL/SLOT connection instead of reimplementing the event (add to your creation):
connect(btn[i][j], SIGNAL(pressed()), this, SLOT(slotButtonPressed());
void Game::slotButtonPressed(){
//doSomething
}
use a QButtonGroup or the QSignalMapper if you need to identify more then one Button in a single method or use QObject::sender(), but this can be tricky sometimes.
I had a similar situations some times ago.. I had a QDialog and I had to dinamically add some QPushButton.. then I need to know on which button the user pressed.. so I needed something like:
connect( btn, SIGNAL( clicked(int) ),
this, SLOT( handle(int) ));
for instance a signal-slot connection with the id of the clicked button and a function for handle the click. The function is the same for each buttons and can handle the specific button because of the id..
Implementing this is quite simple subclassing QPushButton adding an id and a new signal..
Hope it's some help!
If Apbutton inherits QPushButton, why don't connect to clicked()?
then you can call QObject::sender()
On slot:
ApButton *but = dynamic_cast<ApButton*>QObject::sender()
if(but)
{
but->setStyleSheet("background-color : black;");
}
to get the clicked buttonbutton and set its stylesheet
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 ) );
}
}