2016-12-06 4 views
0

Ich implementierte eine Einheit Textfeld, die Konvertierungen vornehmen kann, wenn eine andere Einheit ausgewählt ist. Dies wurde getan, indem QLineEdit und QLabel mit einem QHBoxLayout zusammengefügt wurden.Qt Delegate Anzeige Text gerendert, während Editor aktiv ist

enter image description here

class LengthTextBox::Impl 
{ 
    public: 
     Impl() : 
      lblUnit(new QLabel()), 
      txtValue(new QLineEdit()), 
      menuLength(new LengthMenu(lblUnit)), 
      precision(2), 
      unit(units::Length::Meters) 
     { 
     } 

     QLabel* lblUnit; 
     QLineEdit* txtValue; 
     LengthMenu* menuLength; 

     int precision; 

     units::Length unit; 
}; 

LengthTextBox::LengthTextBox(QWidget* parent) : QWidget(parent), 
    pimpl() 
{ 
    const auto hLayout = new QHBoxLayout(this); 

    // Adjust spacing for better fit within tables. 
    const auto spacing = 2; 
    auto margins = hLayout->contentsMargins(); 
    margins.setTop(0); 
    margins.setLeft(spacing); 
    margins.setRight(spacing); 
    margins.setBottom(0); 
    hLayout->setContentsMargins(margins); 
    hLayout->setSpacing(spacing); 

    this->pimpl->txtValue->setValidator(new QDoubleValidator()); 

    // Install an event filter to capture focus out events. 
    this->pimpl->txtValue->installEventFilter(this); 

    hLayout->addWidget(this->pimpl->txtValue); 
    hLayout->addWidget(this->pimpl->lblUnit); 

    // Since we're building on top of a QWidget, set up the focus policies to still focus on the text box. 
    this->setFocusProxy(this->pimpl->txtValue); 
    this->pimpl->txtValue->setFocusPolicy(Qt::StrongFocus); 
    this->pimpl->lblUnit->setFocusPolicy(Qt::ClickFocus); 

    // Connect signals for switching between units. 
    this->connect(this->pimpl->lblUnit, &QLabel::linkActivated, this, &LengthTextBox::unitSelectorClicked); 
    this->connect(this->pimpl->menuLength, &LengthMenu::unitChanged, this, &LengthTextBox::setUnit); 

    // Forward the line edit signals. 
    this->connect(this->pimpl->txtValue, &QLineEdit::editingFinished, this, &LengthTextBox::editingFinished); 
    this->connect(this->pimpl->txtValue, &QLineEdit::returnPressed, this, &LengthTextBox::returnPressed); 
    this->connect(this->pimpl->txtValue, &QLineEdit::textChanged, this, &LengthTextBox::textChanged); 
    this->connect(this->pimpl->txtValue, &QLineEdit::textEdited, this, &LengthTextBox::textEdited); 

    this->setUnit(this->pimpl->unit); 
} 

LengthTextBox::~LengthTextBox() 
{ 
} 

void LengthTextBox::setPrecision(int x) 
{ 
    this->pimpl->precision = x; 
} 

int LengthTextBox::getPrecision() const 
{ 
    return this->pimpl->precision; 
} 

void LengthTextBox::setUnit(const units::Length& x) 
{ 
    const auto value = this->getValue(); 
    this->setValue(units::Convert(value, this->pimpl->unit, x)); 

    // Use HTML to render the text as a link. This will lead to the linkActivated signal when clicked. 
    const QString link = "<b><a href='#'>" + QString::fromStdString(units::GetSymbol(x)) + "</a></b>"; 
    this->pimpl->lblUnit->setText(link); 

    this->pimpl->txtValue->setFocus(Qt::FocusReason::OtherFocusReason); 
    this->pimpl->txtValue->selectAll(); 

    this->pimpl->unit = x; 
    this->unitChanged(this->pimpl->unit); 
} 

units::Length LengthTextBox::getUnit() const 
{ 
    return this->pimpl->unit; 
} 

void LengthTextBox::setValue(double x, const units::Length& fromUnit) 
{ 
    if(fromUnit != units::Length::Unknown) 
    { 
     x = units::Convert(x, fromUnit, this->pimpl->unit); 
    } 

    this->pimpl->txtValue->setText(QString::number(x, 'f', this->pimpl->precision)); 
} 

double LengthTextBox::getValue(const units::Length& toUnit) const 
{ 
    auto value = this->pimpl->txtValue->text().toDouble(); 

    if(toUnit != units::Length::Unknown) 
    { 
     value = units::Convert(value, this->pimpl->unit, toUnit); 
    } 

    return value; 
} 

std::string LengthTextBox::getFormattedText() const 
{ 
    return this->pimpl->txtValue->text().toStdString() + " " + units::GetSymbol(this->pimpl->unit); 
} 

void LengthTextBox::unitSelectorClicked() 
{ 
    this->pimpl->menuLength->popup(QCursor::pos()); 
} 

bool LengthTextBox::eventFilter(QObject* obj, QEvent* x) 
{ 
    // Watch for a focus out event on the text box inorder to send a lost focus signal. 
    if(this->pimpl->txtValue == obj) 
    { 
     if(x->type() == QEvent::FocusOut && this->pimpl->lblUnit->hasFocus() == false) 
     { 
      this->lostFocus(); 
      return true; 
     } 
    } 

    return QWidget::eventFilter(obj, x); 
} 

Als nächstes abgeleitet ich eine QStyledItemDelegate so, dass ich in dieses Textfeld in meinem eigenen QTableView nutzen könnte.

Wenn Sie sehr genau hinsehen, werden Sie sehen, dass der Buchstabe "m" hinter dem Hyperlink "m" angezeigt wird, wenn der Editor für diesen Delegaten aktiv ist.

class LengthTextBoxDelegate::Impl 
{ 
    public: 
     Impl(int precision) : 
      txtLength(std::make_unique<LengthTextBox>()) 
     { 
      this->txtLength->setPrecision(precision); 
     } 

     // By using a length text box to manage current selected unit, all conversion logic remains in one place. 
     std::unique_ptr<LengthTextBox> txtLength; 
}; 

LengthTextBoxDelegate::LengthTextBoxDelegate(int precision, QWidget* parent) : QStyledItemDelegate(parent), 
    pimpl(precision) 
{ 
} 

LengthTextBoxDelegate::~LengthTextBoxDelegate() 
{ 
} 

QString LengthTextBoxDelegate::displayText(const QVariant& value, const QLocale&) const 
{ 
    this->pimpl->txtLength->setValue(value.toDouble(), Length::Meters); 
    return QString::fromStdString(this->pimpl->txtLength->getText()) + " " + QString::fromStdString(GetSymbol(this->pimpl->txtLength->getSelectedUnit())); 
} 

QWidget* LengthTextBoxDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const 
{ 
    const auto txtLength = new LengthTextBox(parent); 
    txtLength->setValue(index.data().toDouble()); 
    txtLength->setSelectedUnit(this->pimpl->txtLength->getSelectedUnit()); 
    txtLength->setPrecision(this->pimpl->txtLength->getPrecision()); 

    // AMS // This is kind of a hack. For some reason, the ::displayText() that is rendered when no editor is created, was getting rendered 
    // behind the editor widget and was appearing through as if the text box was transparent. 
    // By setting the background color white, we get a nice aesthetic that matches the table views that use this delegate. 
    // In addition, it covers up the text that was appearing behind the widget. 
    auto p = txtLength->palette(); 
    p.setColor(QPalette::Base, Qt::white); 
    txtLength->setPalette(p); 
    //txtLength->setAutoFillBackground(true); 

    // Track the current unit so that its symbol can be rendered when ::displayText() is called. 
    this->connect(txtLength, &LengthTextBox::unitChanged, this, &LengthTextBoxDelegate::unitChanged); 
    this->connect(txtLength, &LengthTextBox::returnPressed, this, &LengthTextBoxDelegate::commitAndCloseEditor); 
    this->connect(txtLength, &LengthTextBox::lostFocus, this, &LengthTextBoxDelegate::commitAndCloseEditor); 

    return txtLength; 
} 

void LengthTextBoxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const 
{ 
    const auto txtLength = qobject_cast<LengthTextBox*>(editor); 
    assert(txtLength != nullptr); 

    txtLength->setValue(index.data().toDouble(), Length::Meters); 
} 

void LengthTextBoxDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const 
{ 
    const auto txtLength = qobject_cast<LengthTextBox*>(editor); 
    assert(txtLength != nullptr); 

    model->setData(index, txtLength->getValue(Length::Meters)); 
} 

void LengthTextBoxDelegate::unitChanged(const hive::math::units::Length& x) 
{ 
    this->pimpl->txtLength->setSelectedUnit(x); 
} 

void LengthTextBoxDelegate::commitAndCloseEditor() 
{ 
    const auto txtLength = qobject_cast<LengthTextBox*>(this->sender()); 
    this->commitData(txtLength); 
    this->closeEditor(txtLength); 
} 

Bis jetzt ist die einzige Lösung, die ich gefunden habe, die Hintergrundfarbe auf meinem Editor Widget zu setzen. Das fühlt sich hackisch an, da muss etwas anderes passieren, was ich falsch mache.

enter image description here

Gibt es eine Möglichkeit, die Qt :: DisplayRole Text, um zu verhindern gemacht wird, während mein Editor aktiv ist?

+0

Welche Klasse ist eigentlich 'LengthTextBox' abgeleitet aus? – Tomas

+0

Ich habe meinen Beitrag bearbeitet, er stammt von QWidget – ASxa86

Antwort

2

Sie müssen den Hintergrund füllen, bevor Sie Ihr Editor-Widget malen. QWidget 's autoFillBackground Eigenschaft steuert dieses Verhalten, da es standardmäßig auf false festgelegt ist, wird Ihr Editor Widget über die QTableView gemalt.

Sie müssen nur in createEditor() die Eigenschaft auf den Editor setzen, bevor es zurückkehrt:

txtLength->setAutoFillBackground(true); 

Die Farbe des Hintergrundes angegeben setBackgroundRole() verwenden, die der Eltern Hintergrund standardmäßig erbt (dh QPalette::Base, wenn die Eltern ist ein QTableView). Also, werden Sie weißen Hintergrund für den Editor erhalten, können Sie möchten die QPalette::Window Farbe Rolle angeben statt:

txtLength->setBackgroundRole(QPalette::Window); 
Verwandte Themen