qt - QSlider mouse direct jump
(11)
Aquí hay una implementación simple en python usando QStyle.sliderValueFromPosition ():
class JumpSlider(QtGui.QSlider):
def mousePressEvent(self, ev):
""" Jump to click position """
self.setValue(QtGui.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), ev.x(), self.width()))
def mouseMoveEvent(self, ev):
""" Jump to pointer position while moving """
self.setValue(QtGui.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), ev.x(), self.width()))
En lugar de avanzar cuando el usuario hace clic en alguna parte del qslider, quiero hacer que el control deslizante salte a esa posición. ¿Cómo se puede implementar esto?
Bueno, dudo que Qt tenga una función directa para este propósito.
Intenta usar widgets personalizados. ¡Esto debería funcionar!
Prueba la siguiente lógica
class MySlider : public QSlider
{
protected:
void mousePressEvent ( QMouseEvent * event )
{
if (event->button() == Qt::LeftButton)
{
if (orientation() == Qt::Vertical)
setValue(minimum() + ((maximum()-minimum()) * (height()-event->y())) / height() ) ;
else
setValue(minimum() + ((maximum()-minimum()) * event->x()) / width() ) ;
event->accept();
}
QSlider::mousePressEvent(event);
}
};
Creo,
la función QStyle :: sliderValueFromPosition () se puede usar.
http://qt-project.org/doc/qt-5/qstyle.html#sliderValueFromPosition
El siguiente código es en realidad un truco, pero funciona bien sin subclasificar QSlider. Lo único que debe hacer es conectar la señal QSlider valueChanged a su contenedor.
Nota 1: debe establecer una página Paso> 0 en su control deslizante
Nota 2: Funciona solo para un deslizador horizontal de izquierda a derecha (debe cambiar el cálculo de "sliderPosUnderMouse" para trabajar con orientación vertical o apariencia invertida)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// ...
connect(ui->mySlider, SIGNAL(valueChanged(int)),
this, SLOT(mySliderValueChanged(int)));
// ...
}
void MainWindow::mySliderValueChanged(int newPos)
{
// Make slider to follow the mouse directly and not by pageStep steps
Qt::MouseButtons btns = QApplication::mouseButtons();
QPoint localMousePos = ui->mySlider->mapFromGlobal(QCursor::pos());
bool clickOnSlider = (btns & Qt::LeftButton) &&
(localMousePos.x() >= 0 && localMousePos.y() >= 0 &&
localMousePos.x() < ui->mySlider->size().width() &&
localMousePos.y() < ui->mySlider->size().height());
if (clickOnSlider)
{
// Attention! The following works only for Horizontal, Left-to-right sliders
float posRatio = localMousePos.x() / (float )ui->mySlider->size().width();
int sliderRange = ui->mySlider->maximum() - ui->mySlider->minimum();
int sliderPosUnderMouse = ui->mySlider->minimum() + sliderRange * posRatio;
if (sliderPosUnderMouse != newPos)
{
ui->mySlider->setValue(sliderPosUnderMouse);
return;
}
}
// ...
}
He intentado y buscado esto en la red y esperaba Qt por una forma más inteligente de hacerlo, desafortunadamente no hubo gran ayuda (puede que no estuviera buscando correctamente)
Bueno, yo he hecho esto en el creador de Qt:
- Agregue un eventFilter en el encabezado (toma QObject y QEvent como argumento) (bool return type)
- Inicializa en constructor ... por ejemplo ... si tu barra deslizante es HSlider entonces ui> HSlider-> installEventFilter (this);
En la definición:
a. compruebe si el objeto es su control deslizante escriba algo como:
ui->HSlider == Object
segundo. Compruebe si el mouse hace clic en el evento algo así como:
QEvent::MouseButtonPress == event->type
do. si todo lo anterior significa que tienes evento de mouse en el control deslizante, haz algo como: en la definición:
ui->HSlider->setValue( Qcursor::pos().x() - firstval ); return QMainWindow::eventFilter(object, event);
ui->HSlider->setValue( Qcursor::pos().x() - firstval ); return QMainWindow::eventFilter(object, event);
Nota: fistVal: se puede sacar imprimiendo la posición cursur en 0 = posición inicial del control deslizante (con la ayuda de QCursor::pos().x()
)
espero que esto ayude
La respuesta de Massimo Callegari es casi correcta, pero el cálculo de newVal ignora el ancho del control deslizante. Este problema surge cuando intenta hacer clic cerca del final del control deslizante.
El siguiente código corrige esto para los controles deslizantes horizontales
double halfHandleWidth = (0.5 * sr.width()) + 0.5; // Correct rounding
int adaptedPosX = event->x();
if ( adaptedPosX < halfHandleWidth )
adaptedPosX = halfHandleWidth;
if ( adaptedPosX > width() - halfHandleWidth )
adaptedPosX = width() - halfHandleWidth;
// get new dimensions accounting for slider handle width
double newWidth = (width() - halfHandleWidth) - halfHandleWidth;
double normalizedPosition = (adaptedPosX - halfHandleWidth) / newWidth ;
newVal = minimum() + ((maximum()-minimum()) * normalizedPosition);
Lo necesitaba también y probé la solución de spyke, pero le faltan dos cosas:
- apariencia invertida
- manejar el picking (cuando el mouse está sobre el mango, el salto directo no es necesario)
Entonces, aquí está el código revisado:
void MySlider::mousePressEvent ( QMouseEvent * event )
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (event->button() == Qt::LeftButton &&
sr.contains(event->pos()) == false)
{
int newVal;
if (orientation() == Qt::Vertical)
newVal = minimum() + ((maximum()-minimum()) * (height()-event->y())) / height();
else
newVal = minimum() + ((maximum()-minimum()) * event->x()) / width();
if (invertedAppearance() == true)
setValue( maximum() - newVal );
else
setValue(newVal);
event->accept();
}
QSlider::mousePressEvent(event);
}
Mi implementación final basada en los comentarios que rodean:
class ClickableSlider : public QSlider {
public:
ClickableSlider(QWidget *parent = 0) : QSlider(parent) {}
protected:
void ClickableSlider::mousePressEvent(QMouseEvent *event) {
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (event->button() == Qt::LeftButton &&
!sr.contains(event->pos())) {
int newVal;
if (orientation() == Qt::Vertical) {
double halfHandleHeight = (0.5 * sr.height()) + 0.5;
int adaptedPosY = height() - event->y();
if ( adaptedPosY < halfHandleHeight )
adaptedPosY = halfHandleHeight;
if ( adaptedPosY > height() - halfHandleHeight )
adaptedPosY = height() - halfHandleHeight;
double newHeight = (height() - halfHandleHeight) - halfHandleHeight;
double normalizedPosition = (adaptedPosY - halfHandleHeight) / newHeight ;
newVal = minimum() + (maximum()-minimum()) * normalizedPosition;
} else {
double halfHandleWidth = (0.5 * sr.width()) + 0.5;
int adaptedPosX = event->x();
if ( adaptedPosX < halfHandleWidth )
adaptedPosX = halfHandleWidth;
if ( adaptedPosX > width() - halfHandleWidth )
adaptedPosX = width() - halfHandleWidth;
double newWidth = (width() - halfHandleWidth) - halfHandleWidth;
double normalizedPosition = (adaptedPosX - halfHandleWidth) / newWidth ;
newVal = minimum() + ((maximum()-minimum()) * normalizedPosition);
}
if (invertedAppearance())
setValue( maximum() - newVal );
else
setValue(newVal);
event->accept();
} else {
QSlider::mousePressEvent(event);
}
}
};
También conocí este problema. Mi solución se muestra a continuación.
slider->installEventFilter(this);
---
bool MyDialog::eventFilter(QObject *object, QEvent *event)
{
if (object == slider && slider->isEnabled())
{
if (event->type() == QEvent::MouseButtonPress)
{
auto mevent = static_cast<QMouseEvent *>(event);
qreal value = slider->minimum() + (slider->maximum() - slider->minimum()) * mevent->localPos().x() / slider->width();
if (mevent->button() == Qt::LeftButton)
{
slider->setValue(qRound(value));
}
event->accept();
return true;
}
if (event->type() == QEvent::MouseMove)
{
auto mevent = static_cast<QMouseEvent *>(event);
qreal value = slider->minimum() + (slider->maximum() - slider->minimum()) * mevent->localPos().x() / slider->width();
if (mevent->buttons() & Qt::LeftButton)
{
slider->setValue(qRound(value));
}
event->accept();
return true;
}
if (event->type() == QEvent::MouseButtonDblClick)
{
event->accept();
return true;
}
}
return QDialog::eventFilter(object, event);
}
También puede anular estos controladores de eventos de QSlider.
-
QSlider::mousePressedEvent
-
QSlider::mouseMoveEvent
-
QSlider::mouseDoubleClickEvent
Un método simple sería derivar de QSlider
y reimplementar mousePressEvent(....)
para establecer la posición del marcador usando setSliderPosition(int)
.
después de tener problemas con todas las versiones de @spyke @Massimo Callegari y @Ben (la posición del deslizador no era correcta para toda el área) encontré algunas funcionalidades de Qt Style dentro del código fuente de QStyle::SH_Slider_AbsoluteSetButtons
: QStyle::SH_Slider_AbsoluteSetButtons
.
Tienes que crear un nuevo QStyle que puede ser muy molesto, o usar ProxyStyle
como lo muestra el usuario jpn en http://www.qtcentre.org/threads/9208-QSlider-step-customize?p=49035#post49035
He agregado otro constructor y he corregido un error tipográfico, pero usé el resto del código fuente original.
#include <QProxyStyle>
class MyStyle : public QProxyStyle
{
public:
using QProxyStyle::QProxyStyle;
int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0, const QWidget* widget = 0, QStyleHintReturn* returnData = 0) const
{
if (hint == QStyle::SH_Slider_AbsoluteSetButtons)
return (Qt::LeftButton | Qt::MidButton | Qt::RightButton);
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
ahora puede establecer el estilo de su control deslizante en el constructor de controles deslizantes (si su control deslizante se deriva de QSlider):
setStyle(new MyStyle(this->style()));
o debería funcionar de esta manera si se trata de un QSlider estándar:
standardSlider.setStyle(new MyStyle(standardSlider->style()));
entonces usted usa el estilo original de ese elemento, pero si se QStyle::SH_Slider_AbsoluteSetButtons
"propiedad" QStyle::SH_Slider_AbsoluteSetButtons
usted regresa como lo desee;)
quizás deba destruir estos estilos de proxy en la eliminación del control deslizante, aún no probados.