Skip to content

Commit

Permalink
multiple chromosome display in QtSLiM
Browse files Browse the repository at this point in the history
  • Loading branch information
bhaller committed Sep 27, 2024
1 parent d87c82c commit 79217ef
Show file tree
Hide file tree
Showing 17 changed files with 903 additions and 493 deletions.
751 changes: 628 additions & 123 deletions QtSLiM/QtSLiMChromosomeWidget.cpp

Large diffs are not rendered by default.

97 changes: 77 additions & 20 deletions QtSLiM/QtSLiMChromosomeWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#define GL_SILENCE_DEPRECATION

#include <QWidget>
#include <QPointer>

#ifndef SLIM_NO_OPENGL
#include <QOpenGLWidget>
Expand All @@ -39,16 +40,63 @@ class QtSLiMWindow;
class QtSLiMHaplotypeManager;
class QPainter;
class QContextMenuEvent;
class QButton;


struct QtSLiMRange
{
int64_t location, length;

explicit QtSLiMRange() : location(0), length(0) {}
explicit QtSLiMRange(int64_t p_location, int64_t p_length) : location(p_location), length(p_length) {}
};


// This is a little controller class that governs a chromosome view or views, and an associated action button
// It is used by QtSLiMWindow for the main chromosome views (overview and zoomed), and by the chromosome display
class QtSLiMChromosomeWidgetController : public QObject
{
Q_OBJECT

QtSLiMWindow *slimWindow_ = nullptr;

// state used only in the chromosome display case
QPointer<QWidget> displayWindow_ = nullptr;
std::string focalSpeciesName_; // we keep the name of our focal species, since a pointer would be unsafe
std::string focalSpeciesAvatar_; // cached so we can display it even when the simulation is invalid
bool needsRebuild_ = false; // true immediately after recycling

public:
bool useScaledWidths_ = true; // used only by the chromosome display
bool shouldDrawMutations_ = true;
bool shouldDrawFixedSubstitutions_ = false;
bool shouldDrawGenomicElements_ = false;
bool shouldDrawRateMaps_ = false;

bool displayHaplotypes_ = false; // if false, displaying frequencies; if true, displaying haplotypes
std::vector<slim_objectid_t> displayMuttypes_; // if empty, display all mutation types; otherwise, display only the muttypes chosen

QtSLiMChromosomeWidgetController(QtSLiMWindow *slimWindow, QWidget *displayWindow, Species *focalSpecies);

void buildChromosomeDisplay(bool resetWindowSize);
void updateFromController(void);

void runChromosomeContextMenuAtPoint(QPoint p_globalPoint);
void actionButtonRunMenu(QtSLiMPushButton *p_actionButton);

// forwards from slimWindow_; this is everything QtSLiMChromosomeWidget needs from the outside world
bool invalidSimulation(void) { return slimWindow_->invalidSimulation(); }
Community *community(void) { return slimWindow_->community; }
Species *focalDisplaySpecies(void);
void colorForGenomicElementType(GenomicElementType *elementType, slim_objectid_t elementTypeID, float *p_red, float *p_green, float *p_blue, float *p_alpha)
{ slimWindow_->colorForGenomicElementType(elementType, elementTypeID, p_red, p_green, p_blue, p_alpha); }
QtSLiMWindow *slimWindow(void) { return slimWindow_; }

signals:
void needsRedisplay(void);
};


// This is a fast macro for when all we need is the offset of a base from the left edge of interiorRect; interiorRect.origin.x is not added here!
// This is based on the same math as rectEncompassingBase:toBase:interiorRect:displayedRange:, and must be kept in synch with that method.
#define LEFT_OFFSET_OF_BASE(startBase, interiorRect, displayedRange) (static_cast<int>(floor(((startBase - static_cast<slim_position_t>(displayedRange.location)) / static_cast<double>(displayedRange.length)) * interiorRect.width())))
Expand All @@ -60,16 +108,21 @@ class QtSLiMChromosomeWidget : public QOpenGLWidget, protected QOpenGLFunctions
class QtSLiMChromosomeWidget : public QWidget
#endif
{
Q_OBJECT
Q_OBJECT

QtSLiMWindow *controller_ = nullptr;
QtSLiMChromosomeWidgetController *controller_ = nullptr;
std::string focalSpeciesName_; // we keep the name of our focal species, since a pointer would be unsafe
std::string focalChromosomeSymbol_; // we keep the name of our focal species, since a pointer would be unsafe
std::string focalChromosomeSymbol_; // we keep the symbol of our focal chromosome, since a pointer would be unsafe

bool isOverview_ = true;
QtSLiMChromosomeWidget *referenceChromosomeView_ = nullptr;
bool isOverview_ = false;
QtSLiMChromosomeWidget *dependentChromosomeView_ = nullptr;

// Selection
bool showsTicks_ = true;

// Displayed range (only in a regular chromosome view)
QtSLiMRange displayedRange_;

// Selection (only in the overview)
bool hasSelection_ = false;
slim_position_t selectionFirstBase_ = 0, selectionLastBase_ = 0;

Expand All @@ -82,7 +135,7 @@ class QtSLiMChromosomeWidget : public QWidget
int mouseInsideCounter_ = 0;
bool showChromosomeNumbers_ = false; // set true after a delay when the mouse is inside

// Tracking
// Tracking (only in the overview)
bool isTracking_ = false;
QRect contentRectForTrackedChromosome_;
slim_position_t trackingStartBase_ = 0, trackingLastBase_ = 0;
Expand All @@ -94,28 +147,32 @@ class QtSLiMChromosomeWidget : public QWidget
QtSLiMHaplotypeManager *haplotype_mgr_ = nullptr; // the haplotype manager constructed for the current display; cached

public:
explicit QtSLiMChromosomeWidget(QWidget *p_parent = nullptr, QtSLiMWindow *controller = nullptr, Species *displaySpecies = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
explicit QtSLiMChromosomeWidget(QWidget *p_parent = nullptr, QtSLiMChromosomeWidgetController *controller = nullptr, Species *displaySpecies = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
virtual ~QtSLiMChromosomeWidget() override;

void setController(QtSLiMWindow *controller);
void setController(QtSLiMChromosomeWidgetController *controller);
Chromosome *resetToDefaultChromosome(void);
void setFocalDisplaySpecies(Species *displaySpecies);
Species *focalDisplaySpecies(void);
void setFocalChromosome(Chromosome *chromosome);
Chromosome *focalChromosome(void);

void setReferenceChromosomeView(QtSLiMChromosomeWidget *p_ref_widget);
void setDependentChromosomeView(QtSLiMChromosomeWidget *p_dependent_widget);

bool hasSelection(void) { return hasSelection_; }
QtSLiMRange getSelectedRange(Chromosome *chromosome);
void setSelectedRange(QtSLiMRange p_selectionRange);
void restoreLastSelection(void);
void updateDependentView(void);

QtSLiMRange getDisplayedRange(Chromosome *chromosome);
void setDisplayedRange(QtSLiMRange p_displayedRange);

void stateChanged(void); // update when the SLiM model state changes; tosses any cached display info
bool showsTicks(void) { return showsTicks_; }
void setShowsTicks(bool p_showTicks);

signals:
void selectedRangeChanged(void);
void stateChanged(void); // update when the SLiM model state changes; tosses any cached display info
void updateAfterTick(void);

protected:
#ifndef SLIM_NO_OPENGL
Expand Down Expand Up @@ -158,7 +215,7 @@ class QtSLiMChromosomeWidget : public QWidget
void qtDrawMutationIntervals(QRect &interiorRect, Species *displaySpecies, QtSLiMRange displayedRange, QPainter &painter);
void qtDrawRateMaps(QRect &interiorRect, Species *displaySpecies, QtSLiMRange displayedRange, QPainter &painter);

Chromosome *_setFocalChromosomeForTracking(QMouseEvent *p_event);
Chromosome *_findFocalChromosomeForTracking(QMouseEvent *p_event);
virtual void mousePressEvent(QMouseEvent *p_event) override;
void _mouseTrackEvent(QMouseEvent *p_event);
virtual void mouseMoveEvent(QMouseEvent *p_event) override;
Expand All @@ -175,12 +232,12 @@ class QtSLiMChromosomeWidget : public QWidget

// Our configuration is kept by the controller, since it is shared by all chromosome views for multispecies models
// However, "overview" chromosome views are always configured the same, hard-coded here
inline bool shouldDrawMutations(void) const { return isOverview_ ? false : controller_->chromosome_shouldDrawMutations_; }
inline bool shouldDrawFixedSubstitutions(void) const { return isOverview_ ? false : controller_->chromosome_shouldDrawFixedSubstitutions_; }
inline bool shouldDrawGenomicElements(void) const { return isOverview_ ? true : controller_->chromosome_shouldDrawGenomicElements_; }
inline bool shouldDrawRateMaps(void) const { return isOverview_ ? false : controller_->chromosome_shouldDrawRateMaps_; }
inline bool displayHaplotypes(void) const { return isOverview_ ? false : controller_->chromosome_display_haplotypes_; }
inline std::vector<slim_objectid_t> &displayMuttypes(void) const { return controller_->chromosome_display_muttypes_; }
inline bool shouldDrawMutations(void) const { return isOverview_ ? false : controller_->shouldDrawMutations_; }
inline bool shouldDrawFixedSubstitutions(void) const { return isOverview_ ? false : controller_->shouldDrawFixedSubstitutions_; }
inline bool shouldDrawGenomicElements(void) const { return isOverview_ ? true : controller_->shouldDrawGenomicElements_; }
inline bool shouldDrawRateMaps(void) const { return isOverview_ ? false : controller_->shouldDrawRateMaps_; }
inline bool displayHaplotypes(void) const { return isOverview_ ? false : controller_->displayHaplotypes_; }
inline std::vector<slim_objectid_t> &displayMuttypes(void) const { return controller_->displayMuttypes_; }
};

#endif // QTSLIMCHROMOSOMEWIDGET_H
Expand Down
4 changes: 2 additions & 2 deletions QtSLiM/QtSLiMChromosomeWidget_GL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void QtSLiMChromosomeWidget::glDrawRect(Species *displaySpecies)

// if the simulation is at tick 0, it is not ready
if (ready)
if (controller_->community->Tick() == 0)
if (controller_->community()->Tick() == 0)
ready = false;

if (ready)
Expand Down Expand Up @@ -91,7 +91,7 @@ void QtSLiMChromosomeWidget::glDrawRect(Species *displaySpecies)
if (!haplotype_mgr_)
{
size_t interiorHeight = static_cast<size_t>(interiorRect.height()); // one sample per available pixel line, for simplicity and speed; 47, in the current UI layout
haplotype_mgr_ = new QtSLiMHaplotypeManager(nullptr, QtSLiMHaplotypeManager::ClusterNearestNeighbor, QtSLiMHaplotypeManager::ClusterNoOptimization, controller_, displaySpecies, interiorHeight, false);
haplotype_mgr_ = new QtSLiMHaplotypeManager(nullptr, QtSLiMHaplotypeManager::ClusterNearestNeighbor, QtSLiMHaplotypeManager::ClusterNoOptimization, controller_, displaySpecies, displayedRange, interiorHeight, false);
}

if (haplotype_mgr_)
Expand Down
4 changes: 2 additions & 2 deletions QtSLiM/QtSLiMChromosomeWidget_QT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void QtSLiMChromosomeWidget::qtDrawRect(Species *displaySpecies, QPainter &paint

// if the simulation is at tick 0, it is not ready
if (ready)
if (controller_->community->Tick() == 0)
if (controller_->community()->Tick() == 0)
ready = false;

if (ready)
Expand Down Expand Up @@ -88,7 +88,7 @@ void QtSLiMChromosomeWidget::qtDrawRect(Species *displaySpecies, QPainter &paint
if (!haplotype_mgr_)
{
size_t interiorHeight = static_cast<size_t>(interiorRect.height()); // one sample per available pixel line, for simplicity and speed; 47, in the current UI layout
haplotype_mgr_ = new QtSLiMHaplotypeManager(nullptr, QtSLiMHaplotypeManager::ClusterNearestNeighbor, QtSLiMHaplotypeManager::ClusterNoOptimization, controller_, displaySpecies, interiorHeight, false);
haplotype_mgr_ = new QtSLiMHaplotypeManager(nullptr, QtSLiMHaplotypeManager::ClusterNearestNeighbor, QtSLiMHaplotypeManager::ClusterNoOptimization, controller_, displaySpecies, displayedRange, interiorHeight, false);
}

if (haplotype_mgr_)
Expand Down
7 changes: 1 addition & 6 deletions QtSLiM/QtSLiMGraphView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ QtSLiMGraphView::QtSLiMGraphView(QWidget *p_parent, QtSLiMWindow *controller) :
controller_ = controller;
setFocalDisplaySpecies(controller_->focalDisplaySpecies());

connect(controller, &QtSLiMWindow::controllerUpdatedAfterTick, this, &QtSLiMGraphView::updateAfterTick);
connect(controller, &QtSLiMWindow::controllerChromosomeSelectionChanged, this, &QtSLiMGraphView::controllerChromosomeSelectionChanged);
connect(controller, &QtSLiMWindow::controllerFullUpdateAfterTick, this, &QtSLiMGraphView::updateAfterTick);
connect(controller, &QtSLiMWindow::controllerTickFinished, this, &QtSLiMGraphView::controllerTickFinished);
connect(controller, &QtSLiMWindow::controllerRecycled, this, &QtSLiMGraphView::controllerRecycled);

Expand Down Expand Up @@ -1279,10 +1278,6 @@ void QtSLiMGraphView::controllerRecycled(void)
if (action) action->setEnabled(!controller_->invalidSimulation() && !missingFocalDisplaySpecies());
}

void QtSLiMGraphView::controllerChromosomeSelectionChanged(void)
{
}

void QtSLiMGraphView::controllerTickFinished(void)
{
}
Expand Down
3 changes: 1 addition & 2 deletions QtSLiM/QtSLiMGraphView.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ public slots:
virtual void invalidateDrawingCache(void); // subclasses must call this themselves in their destructor - super cannot do it!
virtual void graphWindowResized(void);
virtual void controllerRecycled(void); // subclasses must call super: QtSLiMGraphView::controllerRecycled()
virtual void controllerChromosomeSelectionChanged(void);
virtual void controllerTickFinished(void);
virtual void updateAfterTick(void); // subclasses must call super: QtSLiMGraphView::updateAfterTick()
virtual void updateSpeciesBadge(void);
void actionButtonRunMenu(QtSLiMPushButton *actionButton);

protected:
Expand All @@ -122,7 +122,6 @@ public slots:
void setFocalDisplaySpecies(Species *species);
Species *focalDisplaySpecies(void);
bool missingFocalDisplaySpecies(void); // true if the graph has a focal display species but can't find it
void updateSpeciesBadge(void);

// Base graphing functionality
QRect interiorRectForBounds(QRect bounds);
Expand Down
52 changes: 0 additions & 52 deletions QtSLiM/QtSLiMGraphView_1DPopulationSFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

#include "QtSLiMGraphView_1DPopulationSFS.h"

#include <string>

#include "QtSLiMWindow.h"


Expand Down Expand Up @@ -84,10 +82,6 @@ double *QtSLiMGraphView_1DPopulationSFS::populationSFS(int mutationTypeCount)

// get the selected chromosome range
Species *graphSpecies = focalDisplaySpecies();
bool hasSelection;
slim_position_t selectionFirstBase;
slim_position_t selectionLastBase;
controller_->chromosomeSelection(graphSpecies, &hasSelection, &selectionFirstBase, &selectionLastBase);

// tally into our bins
Population &pop = graphSpecies->population_;
Expand All @@ -104,15 +98,6 @@ double *QtSLiMGraphView_1DPopulationSFS::populationSFS(int mutationTypeCount)
{
const Mutation *mutation = mut_block_ptr + registry[registry_index];

// if the user has selected a subrange of the chromosome, we will work from that
if (hasSelection)
{
slim_position_t mutationPosition = mutation->position_;

if ((mutationPosition < selectionFirstBase) || (mutationPosition > selectionLastBase))
continue;
}

slim_refcount_t mutationRefCount = *(refcount_block_ptr + mutation->BlockIndex());
double mutationFrequency = mutationRefCount / totalHaplosomeCount;
int mutationBin = static_cast<int>(floor(mutationFrequency * binCount));
Expand Down Expand Up @@ -157,42 +142,13 @@ void QtSLiMGraphView_1DPopulationSFS::drawGraph(QPainter &painter, QRect interio

// plot our histogram bars
drawGroupedBarplot(painter, interiorRect, spectrum, mutationTypeCount, binCount, 0.0, (1.0 / binCount));

// if we have a limited selection range, overdraw a note about that
bool hasSelection;
slim_position_t selectionFirstBase;
slim_position_t selectionLastBase;
controller_->chromosomeSelection(graphSpecies, &hasSelection, &selectionFirstBase, &selectionLastBase);

if (hasSelection)
{
painter.setFont(QtSLiMGraphView::fontForTickLabels());
painter.setBrush(Qt::darkGray);

QString labelText = QString("%1 – %2").arg(selectionFirstBase).arg(selectionLastBase);
QRect labelBoundingRect = painter.boundingRect(QRect(), Qt::TextDontClip | Qt::TextSingleLine, labelText);
double labelX = interiorRect.x() + (interiorRect.width() - labelBoundingRect.width()) / 2.0;
double labelY = interiorRect.y() + interiorRect.height() - (labelBoundingRect.height() + 4);

labelY = painter.transform().map(QPointF(labelX, labelY)).y();

painter.setWorldMatrixEnabled(false);
painter.drawText(QPointF(labelX, labelY), labelText);
painter.setWorldMatrixEnabled(true);
}
}

QtSLiMLegendSpec QtSLiMGraphView_1DPopulationSFS::legendKey(void)
{
return mutationTypeLegendKey(); // we use the prefab mutation type legend
}

void QtSLiMGraphView_1DPopulationSFS::controllerChromosomeSelectionChanged(void)
{
invalidateCachedData();
update();
}

bool QtSLiMGraphView_1DPopulationSFS::providesStringForData(void)
{
return true;
Expand All @@ -202,14 +158,6 @@ void QtSLiMGraphView_1DPopulationSFS::appendStringForData(QString &string)
{
// get the selected chromosome range
Species *graphSpecies = focalDisplaySpecies();
bool hasSelection;
slim_position_t selectionFirstBase;
slim_position_t selectionLastBase;
controller_->chromosomeSelection(graphSpecies, &hasSelection, &selectionFirstBase, &selectionLastBase);

if (hasSelection)
string.append(QString("# Selected chromosome range: %1 – %2\n").arg(selectionFirstBase).arg(selectionLastBase));

int binCount = histogramBinCount_;
int mutationTypeCount = static_cast<int>(graphSpecies->mutation_types_.size());
double *plotData = populationSFS(mutationTypeCount);
Expand Down
3 changes: 0 additions & 3 deletions QtSLiM/QtSLiMGraphView_1DPopulationSFS.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ class QtSLiMGraphView_1DPopulationSFS : public QtSLiMGraphView
virtual bool providesStringForData(void) override;
virtual void appendStringForData(QString &string) override;

public slots:
virtual void controllerChromosomeSelectionChanged(void) override;

private:
double *populationSFS(int mutationTypeCount);
};
Expand Down
Loading

0 comments on commit 79217ef

Please sign in to comment.