How to add a tick mark to a slider if it cannot inherit QSlider - qt

I have a Qt dialog and there is a slider in it, when the dialog is initialized the slider will be set a value. In order to remind the user what is the default value, I want to add a mark to the slider, just draw a line or a triangle above the handle. Here, the slider should be of QSlider type, that means I can't implement a customized control derived from QSlider. Is there any way to realize it ?

I'm not clear why you can't derive a control from QSlider. You can still treat it like a QSlider, just override the paintEvent method. The example below is pretty cheesy, visually speaking, but you could use the methods from QStyle to make it look more natural:
#include <QtGui>
class DefaultValueSlider : public QSlider {
Q_OBJECT
public:
DefaultValueSlider(Qt::Orientation orientation, QWidget *parent = NULL)
: QSlider(orientation, parent),
default_value_(-1) {
connect(this, SIGNAL(valueChanged(int)), SLOT(VerifyDefaultValue(int)));
}
protected:
void paintEvent(QPaintEvent *ev) {
int position = QStyle::sliderPositionFromValue(minimum(),
maximum(),
default_value_,
width());
QPainter painter(this);
painter.drawLine(position, 0, position, height());
QSlider::paintEvent(ev);
}
private slots:
void VerifyDefaultValue(int value){
if (default_value_ == -1) {
default_value_ = value;
update();
}
}
private:
int default_value_;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
DefaultValueSlider *slider = new DefaultValueSlider(Qt::Horizontal);
slider->setValue(30);
QWidget *w = new QWidget;
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(slider);
layout->addStretch(1);
w->setLayout(layout);
QMainWindow window;
window.setCentralWidget(w);
window.show();
return app.exec();
}
#include "main.moc"

Easiest way I can think off is:
Add QSlider to QSlider (like you do it with layouts and QFrames). Slider above will be your current slider (clickable one). Slider below will be your "default tick position" value.
#include <QApplication>
#include <QSlider>
#include <QVBoxLayout>
int main(int argc, char * argv[])
{
QApplication app(argc, argv);
QSlider * defaultValueSlider = new QSlider();
QSlider * valueSlider = new QSlider(defaultValueSlider);
QVBoxLayout * lay = new QVBoxLayout(defaultValueSlider);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
lay->addWidget(valueSlider);
defaultValueSlider->setRange(0, 100);
valueSlider->setRange(0, 100);
defaultValueSlider->setValue(30);
defaultValueSlider->show();
return app.exec();
}

Why do you need to inherit a QSlider to access its public methods?
http://doc.trolltech.com/4.7/qslider.html
You can just call its setTickPosition() in your app.

Related

Qt making a QPushButton fill layout cell

I need a pushbutton to either fill or not fill the entire space provided by a QGridLayout cell upon the creation of the button (the alignment value is loaded from file). I've simplified my situation with the following code. During run-time, users can set the alignment of the button - either making it fill the entire layout cell or nicely centered. It works so long as the button didn't start off with NULL alignment specified. Yet, I need the ability to start off with a NULL alignment (i.e. the button fills the space of the layout cell). When initially aligning with NULL, what is getting set to make the button lock into a AlignVCenter setting and how can I get the button to return to acting like it was initialized with something other than null alignment?
I'm using Qt 4.8 on Ubuntu 12.04 LTS
#include <QPushButton>
#include <QGridLayout>
#include <QMainWindow>
#include <QApplication>
class MyWidget : public QWidget {
Q_OBJECT
QPushButton* m_pb;
QGridLayout* m_gl;
protected slots:
void pbClicked();
public:
MyWidget(QWidget* parent = 0);
};
MyWidget::MyWidget(QWidget* parent): QWidget(parent)
{
m_pb = new QPushButton(tr("push me"));
connect(m_pb, SIGNAL(clicked()), this, SLOT(pbClicked()));
m_gl = new QGridLayout();
//use (1) to see button expand when button is pressed
//use (2) to show that I can't start off expanded
/*1*/ //m_gl->addWidget(m_pb, 0, 0, Qt::AlignCenter); // creates desired effect
/*2*/ //m_gl->addWidget(m_pb, 0, 0, 0); //does not create desired effect
setLayout(m_gl);
}
void MyWidget::pbClicked(){
//will expand button so long as initial alignment is not NULL
m_gl->setAlignment(m_pb, 0);
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyWidget* widget = new MyWidget();
QMainWindow window;
window.setCentralWidget(widget);
window.show();
return app.exec();
}
#include "main.moc"
The "desired" behavior that you see is in fact an error, and I will file a bug report for it. Thanks for spotting it - nice corner case.
You need to set the size policy of the button to expanding in both directions. Buttons normally don't want to expand vertically, so if you tried a variant that toggles the alignment, you'd see that it works only horizontally, and that's correct.
This is a simple demonstration that shows the correct behavior that also fulfills your needs.
#include <QPushButton>
#include <QGridLayout>
#include <QApplication>
class AlignButton : public QPushButton {
Q_OBJECT
Qt::Alignment m_alignment;
Q_SLOT void clicked() {
m_alignment ^= Qt::AlignCenter;
parentWidget()->layout()->setAlignment(this, m_alignment);
label();
}
void label() {
setText(QString("Alignment = %1").arg(m_alignment));
}
public:
AlignButton(Qt::Alignment alignment, QWidget * parent = 0) :
QPushButton(parent),
m_alignment(alignment)
{
connect(this, SIGNAL(clicked()), SLOT(clicked()));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
label();
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
QGridLayout layout(&window);
layout.addWidget(new AlignButton(0), 0, 0, 0);
layout.addWidget(new AlignButton(Qt::AlignCenter), 1, 0, Qt::AlignCenter);
window.setMinimumSize(500, 200);
window.show();
return app.exec();
}
#include "main.moc"

Reparenting a Qt widget

I have a WidgetA widget, which is an owner-drawn widget. It's currently placed in QMainWindow's QVBoxLayout. After clicking a button, I'd like to "detach" WidgetA from this QVBoxLayout, insert QSplitter into this QVBoxLayout and "readd" WidgetA to this QSplitter. All this without destroying WidgetA, so it will preserve its drawing context, etc.
So, currently I have this (only one widget in a window):
I'd like to put a QSplitter between WidgetA and QMainWindow, and create a new widget, WidgetB, so I'd end up with:
Later I'd like it to split even further, so both WidgetA and WidgetB would still allow themselves to be detached and placed in a new QSplitter, so it would be possible to create f.e. this hierarchy:
And, to be complete, one more step:
I'm not very experienced in Qt, so what I'm trying to do may seem pretty obvious, but I couldn't find how to "reparent" widgets. Is this possible in Qt?
Please, see reparent example, may be it helps you:
//MyMainWindow.h
#include <QWidget>
#include <QPainter>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSplitter>
class MyWidget: public QWidget
{
public:
MyWidget(QWidget* parent, int number)
: QWidget(parent),
m_number(number)
{
}
private:
virtual void paintEvent(QPaintEvent* e)
{
QWidget::paintEvent(e);
QPainter p(this);
p.fillRect( rect(), Qt::red);
p.drawText( rect(), Qt::AlignCenter, QString::number(m_number) );
}
private:
int m_number;
};
class MyMainWindow: public QWidget
{
Q_OBJECT
public:
MyMainWindow()
{
setFixedSize(300, 200);
m_mainLayout = new QVBoxLayout(this);
QHBoxLayout* buttonLayout = new QHBoxLayout;
m_mainLayout->addLayout(buttonLayout);
m_button = new QPushButton("Button", this);
buttonLayout->addWidget(m_button);
connect(m_button, SIGNAL(clicked()), this, SLOT(onButtonClickedOnce()));
m_initWidget = new MyWidget(this, 1);
m_mainLayout->addWidget(m_initWidget);
}
private slots:
void onButtonClickedOnce()
{
m_button->disconnect(this);
m_mainLayout->removeWidget(m_initWidget);
QSplitter* splitter = new QSplitter(Qt::Horizontal, this);
m_mainLayout->addWidget(splitter);
splitter->addWidget(m_initWidget);
MyWidget* newWidget = new MyWidget(splitter, 2);
splitter->addWidget(newWidget);
}
private:
QVBoxLayout* m_mainLayout;
QWidget* m_initWidget;
QPushButton* m_button;
};
//main.cpp
#include <QtWidgets/QApplication>
#include "MyMainWindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyMainWindow mainWindow;
mainWindow.show();
return a.exec();
}
When you operate with widget which is part of layout, then you need to use appropriate methods of QLayout (parent of QVBoxLayout) to detach the item from layout:
QLayout::removeWidget (removeItem if it is not widget, but spacer item or another layout)
QLayout::addWidget (addItem --/--)
Btw: even when widget moves between layouts, its parent may even stay same. I guess you have no need to call QWidget::setParent() as the calls of addWidget/removeWidget will do all work for you.

How to make a QSlider change with double values

My problem is that I just can't seem to make my QSlider work with double values instead of integer, because I need to make it return double values to a QLineEdit and also set it's own value when I put some value in the edit.
When I was a Qt beginner I started with this tutorial. It is a little bit old (it refers to Qt4.1), but it was good enough to get me started!
I have put together a simple example application that can show you where to start... Maybe you can find it helpful!
#include <QApplication>
#include <QtGui>
#include <QVBoxLayout>
#include <QSlider>
#include <QLabel>
class DoubleSlider : public QSlider {
Q_OBJECT
public:
DoubleSlider(QWidget *parent = 0) : QSlider(parent) {
connect(this, SIGNAL(valueChanged(int)),
this, SLOT(notifyValueChanged(int)));
}
signals:
void doubleValueChanged(double value);
public slots:
void notifyValueChanged(int value) {
double doubleValue = value / 10.0;
emit doubleValueChanged(doubleValue);
}
};
class Test : public QWidget {
Q_OBJECT
public:
Test(QWidget *parent = 0) : QWidget(parent),
m_slider(new DoubleSlider()),
m_label(new QLabel())
{
m_slider->setOrientation(Qt::Horizontal);
m_slider->setRange(0, 100);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(m_slider);
layout->addWidget(m_label);
connect(m_slider, SIGNAL(doubleValueChanged(double)),
this, SLOT(updateLabelValue(double)));
updateLabelValue(m_slider->value());
}
public slots:
void updateLabelValue(double value) {
m_label->setText(QString::number(value, 'f', 2));
}
private:
QSlider *m_slider;
QLabel *m_label;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Test *wid = new Test();
wid->show();
return a.exec();
}
#include "main.moc"
You can simply divide slider value on some constant. For example:
const int dpi = 10; // Any constant 10^n
int slideVal = 57; // Integer value from slider
double realVal = double( slideVal / dpi ); // float value

Qt: How to draw a dummy line edit control

I have a QPainter, and a rectangle.
i'd like to draw a QLineEdit control, empty. Just to draw it, not to have a live control. How do I do that? I have tried QStyle::drawPrimitive to no avail. nothing gets drawn.
QStyleOption option1;
option1.init(contactsView); // contactView is the parent QListView
option1.rect = option.rect; // option.rect is the rectangle to be drawn on.
contactsView->style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option1, painter, contactsView);
Naturally, i'd like the drawn dummy to look native in Windows and OSX.
Your code is pretty close, but you would have to initialize the style from a fake QLineEdit. The following is based on QLineEdit::paintEvent and QLineEdit::initStyleOption.
#include <QtGui>
class FakeLineEditWidget : public QWidget {
public:
explicit FakeLineEditWidget(QWidget *parent = NULL) : QWidget(parent) {}
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
QLineEdit dummy;
QStyleOptionFrameV2 panel;
panel.initFrom(&dummy);
panel.rect = QRect(10, 10, 100, 30); // QFontMetric could provide height.
panel.lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,
&panel,
&dummy);
panel.midLineWidth = 0;
panel.state |= QStyle::State_Sunken;
panel.features = QStyleOptionFrameV2::None;
style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &painter, this);
}
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
FakeLineEditWidget w;
w.setFixedSize(300, 100);
w.show();
return app.exec();
}

Add QRadioButtons on runtime

How i can add QRadioButtons in a QFrame on runtime?
Thanks.
Add the widget to the appropriate place by calling the addWidget() method, such as:
ui->someLayout->addWidget(widgetToAdd);
Just make sure you do this in your main (UI) thread.
you can add a QRadioButtons on runtime normally in the same way you do before runtime.
you create the QRadioButton dynamically and call the addWidget method of QFrame layout.
if you are not ableto do it, post the code and let me show you.
mainwindow.h
#include <QtGui/QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
#include <QRadioButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
QHBoxLayout * layout;
QPushButton * button;
public:
MainWindow(QWidget *parent = 0);
public slots:
void radioAdd();
};
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
layout = new QHBoxLayout(this);
QWidget * w = new QWidget(this);
w->setLayout(layout);
this->setCentralWidget(w);
button = new QPushButton(QString("push"),this);
layout->addWidget(button);
connect(button,SIGNAL(clicked()), this, SLOT(radioAdd()));
}
void MainWindow::radioAdd() {
QRadioButton * radio = new QRadioButton("Search from the &cursor", this);
layout->addWidget(radio);
}
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
in this code the radioButton get created in the runtime (in the slot function radioAdd). and in your case, instead of adding QRadioButton into the wigdet layout you add them into QFrame.

Resources