42 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
43 setCursor(Qt::PointingHandCursor);
65 m_envelopeDirty =
true;
73 m_firstFileSample = first;
74 m_envelopeDirty =
true;
82 m_lastFileSample = last;
83 m_envelopeDirty =
true;
99 m_scrollSample = scrollSample;
100 m_visibleSamples = visibleSamples;
116 m_annotations = annotations;
122float OverviewBarWidget::xToSample(
int x)
const
124 int totalSamples = m_lastFileSample - m_firstFileSample;
125 if (totalSamples <= 0 || width() <= 0)
126 return static_cast<float>(m_firstFileSample);
127 float frac =
static_cast<float>(x) /
static_cast<float>(width());
128 frac = qBound(0.f, frac, 1.f);
129 return static_cast<float>(m_firstFileSample) + frac *
static_cast<float>(totalSamples);
134void OverviewBarWidget::rebuildEnvelope()
136 const int pw = width();
138 if (pw <= 0 || !m_model || m_lastFileSample <= m_firstFileSample) {
139 m_envelopeImage = QImage();
140 m_envelopeDirty =
false;
144 m_envelopeImage = QImage(pw, ph, QImage::Format_RGB32);
145 m_envelopeImage.fill(QColor(38, 38, 42).rgb());
147 QPainter p(&m_envelopeImage);
148 p.setRenderHint(QPainter::Antialiasing,
false);
150 const int totalSamples = m_lastFileSample - m_firstFileSample;
151 const float samplesPerPx =
static_cast<float>(totalSamples) /
static_cast<float>(pw);
152 const int nChannels = m_model->channelCount();
153 if (nChannels <= 0 || totalSamples <= 0) {
154 m_envelopeDirty =
false;
159 struct TypeEnvelope {
162 QVector<float> envelope;
164 QVector<TypeEnvelope> typeEnvelopes;
165 QMap<QString, int> typeIndex;
167 for (
int ch = 0; ch < nChannels; ++ch) {
168 auto info = m_model->channelInfo(ch);
169 if (!typeIndex.contains(info.typeLabel)) {
170 int idx = typeEnvelopes.size();
171 typeIndex[info.typeLabel] = idx;
173 te.typeLabel = info.typeLabel;
174 te.color = info.color;
175 te.envelope.resize(pw, 0.f);
176 typeEnvelopes.append(te);
181 for (
int px_col = 0; px_col < pw; ++px_col) {
182 int sampleStart = m_firstFileSample +
static_cast<int>(px_col * samplesPerPx);
183 int sampleEnd = m_firstFileSample +
static_cast<int>((px_col + 1) * samplesPerPx);
184 sampleEnd = qMin(sampleEnd, m_lastFileSample);
185 if (sampleEnd <= sampleStart)
189 int step = qMax(1, (sampleEnd - sampleStart) / 4);
190 for (
int ch = 0; ch < nChannels; ++ch) {
191 auto info = m_model->channelInfo(ch);
192 int ti = typeIndex.value(info.typeLabel, -1);
193 if (ti < 0)
continue;
196 for (
int s = sampleStart; s < sampleEnd; s += step) {
197 float v = qAbs(m_model->sampleValueAt(ch, s));
198 if (v > maxAbs) maxAbs = v;
201 float norm = (info.amplitudeMax > 0.f) ? maxAbs / info.amplitudeMax : 0.f;
202 if (norm > typeEnvelopes[ti].envelope[px_col])
203 typeEnvelopes[ti].envelope[px_col] = norm;
208 const int nTypes = typeEnvelopes.size();
209 const float laneH =
static_cast<float>(ph) / qMax(nTypes, 1);
211 for (
int ti = 0; ti < nTypes; ++ti) {
212 const auto &te = typeEnvelopes[ti];
213 QColor fillColor = te.color;
214 fillColor.setAlpha(160);
217 float yBase = (ti + 1) * laneH;
218 for (
int x = 0; x < pw; ++x) {
219 float level = qBound(0.f, te.envelope[x], 1.f);
220 float barH = level * laneH * 0.9f;
221 if (barH < 0.5f)
continue;
222 p.fillRect(QRectF(x, yBase - barH, 1.f, barH), fillColor);
227 f.setPointSizeF(7.0);
229 p.setPen(QColor(200, 200, 200, 180));
230 p.drawText(QRectF(2,
static_cast<qreal
>(ti) * laneH, 60, laneH * 0.5f),
231 Qt::AlignLeft | Qt::AlignTop, te.typeLabel);
234 m_envelopeDirty =
false;
242 p.setRenderHint(QPainter::Antialiasing,
false);
244 const int pw = width();
245 const int ph = height();
247 if (m_envelopeDirty || m_envelopeImage.width() != pw)
251 if (!m_envelopeImage.isNull()) {
252 p.drawImage(0, 0, m_envelopeImage);
254 p.fillRect(rect(), QColor(38, 38, 42));
257 int totalSamples = m_lastFileSample - m_firstFileSample;
258 if (totalSamples <= 0) {
260 p.setPen(QColor(120, 120, 130));
261 p.drawText(rect(), Qt::AlignCenter, QStringLiteral(
"No data"));
265 float samplesPerPx =
static_cast<float>(totalSamples) /
static_cast<float>(pw);
268 for (
const auto &ann : m_annotations) {
269 float xStart =
static_cast<float>(ann.startSample - m_firstFileSample) / samplesPerPx;
270 float xEnd =
static_cast<float>(ann.endSample - m_firstFileSample) / samplesPerPx;
271 xStart = qBound(0.f, xStart,
static_cast<float>(pw));
272 xEnd = qBound(0.f, xEnd,
static_cast<float>(pw));
274 QColor c = ann.color;
276 p.fillRect(QRectF(xStart, 0, xEnd - xStart, ph), c);
281 for (
const auto &ev : m_events) {
282 float xF =
static_cast<float>(ev.sample - m_firstFileSample) / samplesPerPx;
283 if (xF < 0.f || xF > pw)
287 p.setPen(QPen(c, 1));
288 int ix =
static_cast<int>(xF);
289 p.drawLine(ix, ph - 6, ix, ph);
293 float vpX = (m_scrollSample -
static_cast<float>(m_firstFileSample)) / samplesPerPx;
294 float vpW = m_visibleSamples / samplesPerPx;
295 vpX = qBound(0.f, vpX,
static_cast<float>(pw));
296 vpW = qBound(2.f, vpW,
static_cast<float>(pw) - vpX);
299 QColor dimColor(0, 0, 0, 100);
301 p.fillRect(QRectF(0, 0, vpX, ph), dimColor);
303 p.fillRect(QRectF(vpX + vpW, 0, pw - vpX - vpW, ph), dimColor);
306 QPen vpPen(QColor(255, 255, 255, 200), 1.5);
308 p.setBrush(Qt::NoBrush);
309 p.drawRect(QRectF(vpX, 0.5f, vpW, ph - 1.f));
316 if (event->button() == Qt::LeftButton) {
318 float targetSample = xToSample(event->position().toPoint().x()) - m_visibleSamples * 0.5f;
329 float targetSample = xToSample(event->position().toPoint().x()) - m_visibleSamples * 0.5f;
339 if (event->button() == Qt::LeftButton) {
Minimap thumbnail of the entire recording shown above the raw browser.
Circular-buffer Qt model exposing a rolling window of the live FIFF stream as a table.
2-D display widgets and visualisation helpers (charts, topography, colour maps).
Circular-buffer Qt model exposing a rolling window of the live FIFF stream as a table.
void setFirstFileSample(int first)
void setLastFileSample(int last)
void scrollRequested(float targetSample)
void paintEvent(QPaintEvent *event) override
QSize sizeHint() const override
QSize minimumSizeHint() const override
void setAnnotations(const QVector< ChannelRhiView::AnnotationSpan > &annotations)
void setModel(ChannelDataModel *model)
void setSfreq(float sfreq)
void mouseMoveEvent(QMouseEvent *event) override
void setEvents(const QVector< ChannelRhiView::EventMarker > &events)
void mousePressEvent(QMouseEvent *event) override
void mouseReleaseEvent(QMouseEvent *event) override
OverviewBarWidget(QWidget *parent=nullptr)
void setViewport(float scrollSample, float visibleSamples)
static constexpr int kBarHeight