Skip to content

Commit 08b89a4

Browse files
committed
Add line numbers and highlighting
1 parent 1a75a30 commit 08b89a4

File tree

5 files changed

+178
-19
lines changed

5 files changed

+178
-19
lines changed

src/Interface/Modules/Base/ModuleDialogGeneric.cc

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,38 @@ void ModuleDialogGeneric::addTextEditManager(QTextEdit* textEdit, const Algorith
404404
addWidgetSlotManager(boost::make_shared<TextEditSlotManager>(state_, *this, stateKey, textEdit));
405405
}
406406

407+
class PlainTextEditSlotManager : public WidgetSlotManager
408+
{
409+
public:
410+
PlainTextEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QPlainTextEdit* textEdit) :
411+
WidgetSlotManager(state, dialog, textEdit, stateKey), stateKey_(stateKey), textEdit_(textEdit)
412+
{
413+
connect(textEdit, SIGNAL(textChanged()), this, SLOT(push()));
414+
}
415+
virtual void pull() override
416+
{
417+
auto newValue = QString::fromStdString(state_->getValue(stateKey_).toString());
418+
if (newValue != textEdit_->toPlainText())
419+
{
420+
textEdit_->setPlainText(newValue);
421+
LOG_DEBUG("In new version of pull code for PlainTextEdit: " << newValue.toStdString());
422+
}
423+
}
424+
virtual void pushImpl() override
425+
{
426+
LOG_DEBUG("In new version of push code for PlainTextEdit: " << textEdit_->toPlainText().toStdString());
427+
state_->setValue(stateKey_, textEdit_->toPlainText().toStdString());
428+
}
429+
private:
430+
AlgorithmParameterName stateKey_;
431+
QPlainTextEdit* textEdit_;
432+
};
433+
434+
void ModuleDialogGeneric::addPlainTextEditManager(QPlainTextEdit* plainTextEdit, const AlgorithmParameterName& stateKey)
435+
{
436+
addWidgetSlotManager(boost::make_shared<PlainTextEditSlotManager>(state_, *this, stateKey, plainTextEdit));
437+
}
438+
407439
class LineEditSlotManager : public WidgetSlotManager
408440
{
409441
public:
@@ -786,7 +818,7 @@ void ModuleDialogGeneric::syncTableRowsWithDynamicPort(const std::string& portId
786818
if (portId.find(type) != std::string::npos)
787819
{
788820
//qDebug() << "adjust input table: " << portId.c_str() << portChangeType;
789-
821+
790822
if (portChangeType == DynamicPortChange::USER_ADDED_PORT || portChangeType == DynamicPortChange::USER_ADDED_PORT_DURING_FILE_LOAD)
791823
{
792824
//qDebug() << "trying to add row via port added, id: " << portId.c_str();

src/Interface/Modules/Base/ModuleDialogGeneric.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ namespace Gui {
131131
void addComboBoxManager(QComboBox* comboBox, const Core::Algorithms::AlgorithmParameterName& stateKey);
132132
void addComboBoxManager(QComboBox* comboBox, const Core::Algorithms::AlgorithmParameterName& stateKey, const GuiStringTranslationMap& stringMap);
133133
void addTextEditManager(QTextEdit* textEdit, const Core::Algorithms::AlgorithmParameterName& stateKey);
134+
void addPlainTextEditManager(QPlainTextEdit* plainTextEdit, const Core::Algorithms::AlgorithmParameterName& stateKey);
134135
void addLineEditManager(QLineEdit* lineEdit, const Core::Algorithms::AlgorithmParameterName& stateKey);
135136
void addDoubleLineEditManager(QLineEdit* lineEdit, const Core::Algorithms::AlgorithmParameterName& stateKey);
136137
void addSpinBoxManager(QSpinBox* spinBox, const Core::Algorithms::AlgorithmParameterName& stateKey);

src/Interface/Modules/Python/InterfaceWithPython.ui

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,7 @@
6161
<attribute name="title">
6262
<string>Code</string>
6363
</attribute>
64-
<layout class="QGridLayout" name="gridLayout_5">
65-
<item row="0" column="0">
66-
<widget class="QTextEdit" name="pythonCodeTextEdit_">
67-
<property name="font">
68-
<font>
69-
<family>Courier</family>
70-
</font>
71-
</property>
72-
<property name="tabStopWidth">
73-
<number>30</number>
74-
</property>
75-
</widget>
76-
</item>
77-
</layout>
64+
<layout class="QGridLayout" name="gridLayout_5"/>
7865
</widget>
7966
<widget class="QWidget" name="tab">
8067
<attribute name="title">

src/Interface/Modules/Python/InterfaceWithPythonDialog.cc

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ InterfaceWithPythonDialog::InterfaceWithPythonDialog(const std::string& name, Mo
4242
setWindowTitle(QString::fromStdString(name));
4343
fixSize();
4444

45-
addTextEditManager(pythonCodeTextEdit_, Parameters::PythonCode);
45+
{
46+
pythonCodePlainTextEdit_ = new CodeEditor(this);
47+
tabWidget->widget(0)->layout()->addWidget(pythonCodePlainTextEdit_);
48+
}
49+
50+
addPlainTextEditManager(pythonCodePlainTextEdit_, Parameters::PythonCode);
4651

4752
addSpinBoxManager(retryAttemptsSpinBox_, Parameters::NumberOfRetries);
4853
addSpinBoxManager(pollingIntervalSpinBox_, Parameters::PollingIntervalMilliseconds);
@@ -91,13 +96,12 @@ void InterfaceWithPythonDialog::setupOutputTableCells()
9196

9297
void InterfaceWithPythonDialog::updateFromPortChange(int numPorts, const std::string& portId, DynamicPortChange type)
9398
{
94-
//qDebug() << "InterfaceWithPythonDialog::updateFromPortChange" << numPorts << portId.c_str() << type;
9599
if (type == DynamicPortChange::INITIAL_PORT_CONSTRUCTION)
96100
return;
97101

98102
if (type == DynamicPortChange::USER_REMOVED_PORT)
99103
{
100-
QMessageBox::warning(this, "Warning: possible Python code update required", windowTitle() +
104+
QMessageBox::warning(this, "Warning: possible Python code update required", windowTitle() +
101105
": The connection to port " + QString::fromStdString(portId) + " was deleted. The variable name \"" +
102106
QString::fromStdString(state_->getValue(SCIRun::Core::Algorithms::Name(portId)).toString()) + "\" is no longer valid."
103107
+ " Please update your Python code or input variable table to reflect this.");
@@ -116,11 +120,103 @@ void InterfaceWithPythonDialog::updateFromPortChange(int numPorts, const std::st
116120
void InterfaceWithPythonDialog::handleInputTableWidgetRowChange(const std::string& portId, const std::string& type, DynamicPortChange portChangeType)
117121
{
118122
const int lineEditColumn = 2;
119-
syncTableRowsWithDynamicPort(portId, type, inputVariableNamesTableWidget_, lineEditColumn, portChangeType,
123+
syncTableRowsWithDynamicPort(portId, type, inputVariableNamesTableWidget_, lineEditColumn, portChangeType,
120124
{ { 1, [&](){ return new QTableWidgetItem(QString::fromStdString(type)); } } });
121125
}
122126

123127
void InterfaceWithPythonDialog::loadAPIDocumentation()
124128
{
125129
openPythonAPIDoc();
126130
}
131+
132+
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
133+
{
134+
lineNumberArea_ = new LineNumberArea(this);
135+
136+
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
137+
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
138+
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
139+
140+
updateLineNumberAreaWidth(0);
141+
highlightCurrentLine();
142+
}
143+
144+
int CodeEditor::lineNumberAreaWidth()
145+
{
146+
int digits = 1;
147+
int max = qMax(1, blockCount());
148+
while (max >= 10) {
149+
max /= 10;
150+
++digits;
151+
}
152+
153+
int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
154+
155+
return space;
156+
}
157+
158+
void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
159+
{
160+
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
161+
}
162+
163+
void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
164+
{
165+
if (dy)
166+
lineNumberArea_->scroll(0, dy);
167+
else
168+
lineNumberArea_->update(0, rect.y(), lineNumberArea_->width(), rect.height());
169+
170+
if (rect.contains(viewport()->rect()))
171+
updateLineNumberAreaWidth(0);
172+
}
173+
174+
void CodeEditor::resizeEvent(QResizeEvent *e)
175+
{
176+
QPlainTextEdit::resizeEvent(e);
177+
178+
QRect cr = contentsRect();
179+
lineNumberArea_->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
180+
}
181+
182+
void CodeEditor::highlightCurrentLine()
183+
{
184+
QList<QTextEdit::ExtraSelection> extraSelections;
185+
186+
if (!isReadOnly()) {
187+
QTextEdit::ExtraSelection selection;
188+
189+
QColor lineColor = QColor(Qt::darkGray);
190+
191+
selection.format.setBackground(lineColor);
192+
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
193+
selection.cursor = textCursor();
194+
selection.cursor.clearSelection();
195+
extraSelections.append(selection);
196+
}
197+
198+
setExtraSelections(extraSelections);
199+
}
200+
201+
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
202+
{
203+
QPainter painter(lineNumberArea_);
204+
painter.fillRect(event->rect(), Qt::lightGray);
205+
QTextBlock block = firstVisibleBlock();
206+
int blockNumber = block.blockNumber();
207+
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
208+
int bottom = top + (int) blockBoundingRect(block).height();
209+
while (block.isValid() && top <= event->rect().bottom()) {
210+
if (block.isVisible() && bottom >= event->rect().top()) {
211+
QString number = QString::number(blockNumber + 1);
212+
painter.setPen(Qt::black);
213+
painter.drawText(0, top, lineNumberArea_->width(), fontMetrics().height(),
214+
Qt::AlignRight, number);
215+
}
216+
217+
block = block.next();
218+
top = bottom;
219+
bottom = top + (int) blockBoundingRect(block).height();
220+
++blockNumber;
221+
}
222+
}

src/Interface/Modules/Python/InterfaceWithPythonDialog.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,49 @@ private Q_SLOTS:
5151
private:
5252
void handleInputTableWidgetRowChange(const std::string& portId, const std::string& type, DynamicPortChange portChangeType);
5353
void setupOutputTableCells();
54+
class CodeEditor* pythonCodePlainTextEdit_;
55+
};
56+
57+
class CodeEditor : public QPlainTextEdit
58+
{
59+
Q_OBJECT
60+
61+
public:
62+
CodeEditor(QWidget *parent = nullptr);
63+
64+
void lineNumberAreaPaintEvent(QPaintEvent *event);
65+
int lineNumberAreaWidth();
66+
67+
protected:
68+
void resizeEvent(QResizeEvent *event) override;
69+
70+
private Q_SLOTS:
71+
void updateLineNumberAreaWidth(int newBlockCount);
72+
void highlightCurrentLine();
73+
void updateLineNumberArea(const QRect &, int);
74+
75+
private:
76+
QWidget* lineNumberArea_;
77+
};
78+
79+
class LineNumberArea : public QWidget
80+
{
81+
public:
82+
LineNumberArea(CodeEditor *editor) : QWidget(editor) {
83+
codeEditor = editor;
84+
}
85+
86+
QSize sizeHint() const override {
87+
return QSize(codeEditor->lineNumberAreaWidth(), 0);
88+
}
89+
90+
protected:
91+
void paintEvent(QPaintEvent *event) override {
92+
codeEditor->lineNumberAreaPaintEvent(event);
93+
}
94+
95+
private:
96+
CodeEditor *codeEditor;
5497
};
5598

5699
}

0 commit comments

Comments
 (0)