I am new to QT and GUI related programming and looking to do a 2 tier selection menu in a project. I appreciate the time taken to help.
Example: Combo box 1 has options like: 1. Screen size - Medium 2. Screen size - large and depending on that I would like to display different options for screen resolution in combo box 2.
The user can change the combo box 1 selection any number of times and box 2 should show the appropriate options.
I have tried using QComboBox.setEnabled(False) and True as was suggested in Disabling QComboBox in pyqt
but it has not worked for me and I am certainly missing something.
Snippet of my code:
void interface::changeFunctionx(int index)
{
delete f;
switch(index)
{
case 0:
version = 1;
functionSely->setVisible(1);
break;
case 1:
version = 1;
//some other function call still seeing how gui works
break;
}
}
The Data of the QComboBox can be re-filled using below logic
QComboBox::clear()
QComboBox::insertItems(0,QStringList);
//Declare the variable needed in the class
class myclass : public QMainWindow {
QList<QString> lst;
QStringList ql1,ql2;
}
Two combo box used in this example. i.e, cbo1 (2 items) (user selection) & cbo2 (re-fill data)
//Combo box fill Data Preparation
//In this example, the below function called only one time (called from the end line of constructor) (to initialize)
void fnPrepareStaticData(){
lst.push_back("Option-1");
lst.push_back("Option-2");
lst.push_back("Option-3");
ql1 = (QStringList(lst));
lst.clear();
lst.push_back("New-1");
lst.push_back("New-2");
lst.push_back("New-3");
ql2 = (QStringList(lst));
}
//slot added to the QComboBox (currentIndexChanged(int index))
void MainWindow::on_cbo1_currentIndexChanged(int index)
{
ui->cbo2->clear();
switch (index) {
case 0:
ui->cbo2->insertItems(0,ql1);
break;
case 1:
ui->cbo2->insertItems(0,ql2);
break;
default:
break;
}
}
Related
I have a QTableWidget, some columns are filled with text, some with numbers, these columns are fine to sort. But I also have a column with custom widgets, how should I enable sorting for these?
My first thought was of course to overload the '<' method on the QTableWidgetItem, but there are no QTableWidgetItem. So what would be the best way to solve this?
QTableWidget is not ideal for this case, consider using QTableView. Anyway, I will show you how to sort a QProgressBar widget in a QTableWidget.
As you already said, you can overload the <() operator for this case. Let's say you have a QProgressBar in column number 4. You have to overload the <() operator.
You have to subclass QProgressBar and QTableWidgetItem.
class CustomTableWidgetItem : public QProgressBar, public QTableWidgetItem
{
Q_OBJECT
public:
CustomTableWidgetItem( QWidget* parent ) : QProgressBar( parent )
{
}
CustomTableWidgetItem(const QString txt = QString("0"))
:QTableWidgetItem(txt)
{
}
bool operator <(const QTableWidgetItem& other) const
{
if(other.column() == 0 /* numeric cell */) {
return QTableWidgetItem::text().toInt() < other.text().toInt();
}
else if(other.column() == 4 /* progress bar */) {
const QProgressBar *p = dynamic_cast<const QProgressBar *>(&other);
if(p != 0) {
if(this->value() < p->value())
return true;
}
}
return false;
}
};
And then you can insert your QProgressBar like this in cell number 4.
Insert QProgressBar like this
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
CustomTableWidgetItem *pgbar = new CustomTableWidgetItem(this);
ui->tableWidget->setCellWidget(ui->tableWidget->rowCount()-1, 4, pgbar);
ui->tableWidget->setItem(ui->tableWidget->rowCount()-1, 4, pgbar);
Let's say you want to have simple text at cell 1 use QTableWidgetItem.
ui->tableWidget->setItem(ui->tableWidget->rowCount()-1, 1, new QTableWidgetItem("Hello"));
If you want to sort numbers as well, for example in cell 0 use CustomTableWidgetItem, since we have implemented an if-statement as you can see above for sorting numbers and progressbar.
ui->tableWidget->setItem(ui->tableWidget->rowCount()-1, 0, new CustomTableWidgetItem(QString::number(ui->tableWidget->rowCount())));
You can use this approach with other widgets as well, just subclass the correct widget, but in general it's better to use QTableView (MVC approach).
Here is a screenshot with QTableWidget and QProgressBar sorting.
I would like to write a custom QLabel subclass with some more features for responsive design. In thisexample, I want to write a QLabel which scales the text based on the useable space. This is quite easy but also has some problems because of Qt-intern stuff. (I have to scale the text to 0.9 of the useable space, otherwise resizing the window / widget gets buggy)
Now I wan't to add a way to hide the label completely when the font size is bellow a specific threshold. However, this seems to be quite a complex task.
Here is what I have sofar in the classes resizeEvent(QResizeEvent *event) function.
Right now, my function only sets the text to "" when the size would be bellow the threshold.
void CustomLabel::resizeEvent (QResizeEvent * event ) {
if(autoFontResize) {
this->setSilentText(labelText); // just the normal setText function, I overwrote it for the subclass
QFont f = this->font();
int flags = Qt::TextDontClip|Qt::TextWordWrap;
QRect fontBoundRect = this->fontMetrics().boundingRect(this->rect(), flags, this->text());
float xFactor = (float)event->size().width() / (float)fontBoundRect.width();
float yFactor = (float)event->size().height() / (float)fontBoundRect.height();
float factor = xFactor < yFactor ? xFactor : yFactor;
f.setPointSizeF(f.pointSize()*factor*0.9); //
if(minimumFontSize != 0) { // 0 = no minimum Size for the font
if(f.pointSize() < minimumFontSize) {
if(hideFontOnMinimum) { // either Hide or set to the limit size
this->setSilentText(""); //replace text
} else {
f.setPointSizeF(minimumFontSize);
}
}
}
this->setFont(f);
}
QLabel::resizeEvent(event);
}
By the way, some parts of the code are found on stackoverflow, not mine. ;)
What I would like to do is to completely hide() the label. However the label doesn't know when It can show() again since the resizeEvent doesn't seem to be called after that.
Any ideas?
Thanks!
As you've noticed, if you call hide() on the widget it fails to receive a resize event. Since you're customising the class anyway, rather than calling hide(), you could just set a class variable to note that it's hidden and overload the paintEvent function, not to draw the widget if the variable is set: -
void CustomLabel::paintEvent(QPaintEvent * event)
{
if(m_hideOnMinimum)
return;
QLabel::paintEvent(event);
}
Note that by not painting the label, it will be hidden, but the user may still be able to interact with it, so you will need to disable it or overload keyboard / mouse events too.
I'm experiencing a problem with hover functionality on QLabel.
I've implemented this as event filter:
HoverLabel::HoverLabel(QWidget *parent) : QWidget(parent)
{
installEventFilter(this);
label = (QLabel *)parent;
show();
}
bool HoverLabel::eventFilter(QObject *object, QEvent *event)
{
if (object == this)
{
if (event->type() == QEvent::Enter)
{
label->setText("Howering");
return true;
}
else if (event->type() == QEvent::Leave)
{
label->setText("Not howering");
return true;
}
}
return false;
}
Calling while main constructor is running as:
hoverLabels[0] = new HoverLabel(ui->hoverLabel_1);
Now everything works fine except that the area where I ger QEvent::Enter is just too small and what's more, constant - you can double the size of label and area that generates event stays the same.
I've marked the hover area on picture with blue rectangle, as the mouse wasn't captured for whatever reason. Beyond that, it say's "Not howering". I've tried various text content, various text sizes, checking all the boxes around, setting different size policies but the area is still the same.
In short, the class had to override eventFilter. In my case class is called PlayerLabel.
See these 2 files for how to:
header file:
https://github.com/metthal/ICP-Projekt/blob/1f0a0f6e919d132bbec6bacd1cff91a3a596460d/inc/gui/mainwindow.h#L40
source file:
https://github.com/metthal/ICP-Projekt/blob/1f0a0f6e919d132bbec6bacd1cff91a3a596460d/src/gui/mainwindow.cpp#L908
Also see how to create this object afterwards:
https://github.com/metthal/ICP-Projekt/blob/1f0a0f6e919d132bbec6bacd1cff91a3a596460d/src/gui/mainwindow.cpp#L358
In my application I have a QToolButton related to the presence of an USB Pen Drive. When the Pen drive is inserted I would like to show the QToolButton and create a context menu associated to the content of the pen drive.I have a different menu dynamically created to be assigned to the Button.
My code works well for the first time, but when I create a new menu it doesn't appear.
In this last version of code, when I show the button for the second time I get the the previous menu (Dismount is the only item present) and when i click on the item it doesn't do anything.
EDIT: If I use the QAction instead of the QWidgetAction the code works fine. So it seems something related to the QWidgetAction of QLabel used inside of it.
The following is a simplified version of my code:
/* member variables */
QMenu *m_pqmConMenUSB;
QLabel m_MenuItem;
/* costructor */
ui->tbDriveUSB->setContextMenuPolicy(Qt::CustomContextMenu);
m_pqmConMenUSB = NULL;
QObject::connect(ui->tbDriveUSB, SIGNAL(customContextMenuRequested(const QPoint&)),this, SLOT(showUSBCM(const QPoint&)));
m_MenuItem.setStyleSheet("QLabel { background-color : black; color : white; }");
m_MenuItem.setText("Dismount");
QFont fonte = m_MenuItem.font();
fonte.setPixelSize(16);
m_MenuItem.setFont(fonte);
QPalette ChePalette = m_MenuItem.palette();
m_MenuItem.setMinimumSize(0,32);
ChePalette.setColor(m_MenuItem.backgroundRole(), Qt::black);
ChePalette.setColor(m_MenuItem.foregroundRole(), Qt::white);
m_MenuItem.setPalette(ChePalette);
/*member functions*/
void MainWindow::showUSBCM(const QPoint& pos)
{
// copied from an example
if (pos != QPoint(0,0)) {
// Execute context menu
if (m_pqmConMenUSB!=NULL) m_pqmConMenUSB->exec(pos);
}
}
void MainWindow::OnUSBMounted()
{
/* this static boolean is used to simulate a change in the menu content */
static bool tryToChange = false;
ui->tbDriveUSB->show();
m_pqmConMenUSB = new QMenu(this);
QWidgetAction *menuItemW = new QWidgetAction(this);
menuItemW->setDefaultWidget(&m_MenuItem);
menuItemW->setText("Dismount");
connect(menuItemW,SIGNAL(triggered()), this, SLOT(DoDismount()));
m_pqmConMenUSB->addAction(menuItemW);
if (tryToChange)
{
menuItemW = new QWidgetAction(this);
menuItemW->setDefaultWidget(&m_MenuItem);
menuItemW->setText("Update");
connect(menuItemW,SIGNAL(triggered()), this, SLOT(Update()));
m_pqmConMenUSB->addAction(menuItemW);
}
tryToChange = !tryToChange;
ui->tbDriveUSB->setMenu(m_pqmConMenUSB);
}
void MainWindow::OnUSBDismounted()
{
ui->tbDriveUSB->hide();
/* the first version of the code tries to destroy the menu with the following code, but it doesn't work
/*ui->tbDriveUSB->setMenu(NULL);
QAction *pAction;
foreach (pAction, m_pqmConMenUSB->actions())
pAction->disconnect(this);
delete(m_pqmConMenUSB);
m_pqmConMenUSB = NULL;*/
}
A useful pattern to dynamically populate a menu associated with a QToolButton is to first create the menu and attach it to the button, but do not populate it. Then connect a slot to the QMenu::aboutToShow() signal. In the slot implementation, clear the contents of the menu and populate it as needed for the current state of the application.
As I mentioned yesterday, the problem was related to QLabels. In my code I used two member variables of QLabel type. The QLabels weren't pointers.
When I delete the action, the QLabels weren't able to show them again. I suppose it was related to the removeAction(d->menuAction); function which destroys QWidget associated to the QWidgetAction. That function was called when the ui->tbDriveUSB->setMenu(NULL); was called.
I choose to use the QLabel just for stylesheet and size, but it's possibile to set that properties in the menu. This is enough for me.
I think that, making a new QLabel when the QWidgetAction is created and delete it when the QWidgetAction is deleted, could make works the previous code. I haven't tested it.
In order to complete the answer, the following is my current code that works well
/* member variable */
QMenu *m_pqmConMenUSB;
/* constructor */
ui->tbDriveUSB->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->tbDriveUSB, SIGNAL(customContextMenuRequested(const QPoint&)),this, SLOT(showUSBCM(const QPoint&)));
m_pqmConMenUSB = new QMenu(this);
QFont fonte = m_pqmConMenUSB->font();
fonte.setPixelSize(16);
m_pqmConMenUSB->setFont(fonte);
m_pqmConMenUSB->setStyleSheet("QMenu { background-color : black; color : white; }");
m_pqmConMenUSB->setMinimumSize(0,32);
/*member functions*/
void MainWindow::showUSBCM(const QPoint& pos)
{
if (pos != QPoint(0,0))
{
// Execute context menu
if (m_pqmConMenUSB!=NULL) m_pqmConMenUSB->exec(pos);
}
}
void MainWindow::OnUSBMounted()
{
static bool tryToChange = true;
ui->tbDriveUSB->show();
QAction *menuItem = new QAction("Dismount",this);
connect(menuItem,SIGNAL(triggered()), this, SLOT(DoDismount()));
m_pqmConMenUSB->addAction(menuItem);
if (tryToChange)
{
QAction *menuItem2 = new QAction("upDate",this);
connect(menuItem2,SIGNAL(triggered()), this, SLOT(Update()));
m_pqmConMenUSB->addAction(menuItem2);
}
tryToChange = !tryToChange;
ui->tbDriveUSB->setMenu(m_pqmConMenUSB);
}
void MainWindow::OnUSBDismounted()
{
printf("SEI UNO SMONTATO\n\r");
ui->tbDriveUSB->setMenu(NULL);
QAction *pAction;
foreach (pAction, m_pqmConMenUSB->actions())
{
pAction->disconnect(this); // receiver
delete pAction;
}
ui->tbDriveUSB->hide();
m_pqmConMenUSB->clear();
}
I have a tab which contains a QtableView and few buttons like "MOVE UP" and "MOVE DOWN". So on "MOVE UP" button press I have to move the entire row of QTableView one step up and the bring the adjacent one, one step down. I want to achieve this without creating complete model again since it may take time to construct the whole model again. Instead I just want to MOVE UP the selected row in the view.
Please let me know the simplest way to achieve this.
Thanks in advance
You don't need any knowledge of the model to do any of this, and most of the commenters are quite mistaken with any mention of the model at all.
You simply need to obtain the view's verticalHeader, and do a section swap of the item you want with the one above it. You might need to tell the verticalHeader to allow for reordering setMovable(true)
One of the main tenants QT and ModelView programming is to understand and appreciate the separation of manipulating a model from manipulating a view of the model.
I made the move up and down functions and this is what my .cpp contains
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
model = new QStandardItemModel(4,2,this);
for(int row = 0; row < 4; row++)
{
for(int col = 0; col < 2; col++)
{
QModelIndex index
= model->index(row,col,QModelIndex());
// 0 for all data
model->setData(index,row);
}
}
ui->tableView->setModel(model);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_upPushButton_clicked()
{
if(ui->tableView->currentIndex().row()<=0)
{
return;
}
QList<QStandardItem *> list = model->takeRow(ui->tableView->currentIndex().row());
model->insertRow(ui->tableView->currentIndex().row(),list);
}
void Widget::on_downPushButton_clicked()
{
int selectedRow = ui->tableView->currentIndex().row() ;
if(selectedRow == ui->tableView->model()->rowCount()-1)
{
return;
}
QList<QStandardItem *> list = model->takeRow(ui->tableView->currentIndex().row());
model->insertRow(selectedRow+1,list);
}
This is what the ui looks like