1 // Copyright (c) 2016 The SigViewer Development Team
2 // Licensed under the GNU General Public License (GPL)
3 // https://www.gnu.org/licenses/gpl
4 
5 
6 #include "y_axis_widget_4.h"
7 #include "signal_graphics_item.h"
8 #include "gui/gui_action_factory.h"
9 #include "base/math_utils.h"
10 
11 #include <QPaintEvent>
12 #include <QPainter>
13 #include <QPixmap>
14 #include <QDebug>
15 #include <QContextMenuEvent>
16 
17 #include <cmath>
18 
19 namespace sigviewer
20 {
21 
22 //-----------------------------------------------------------------------------
YAxisWidget(QWidget * parent,QSharedPointer<const SignalViewSettings> signal_view_settings)23 YAxisWidget::YAxisWidget (QWidget* parent, QSharedPointer<const SignalViewSettings> signal_view_settings)
24   : QWidget (parent),
25     channel_height_ (0),
26     y_start_ (0),
27     signal_view_settings_ (signal_view_settings),
28     label_color_ (Qt::black)
29 {
30     // nothing to do here
31 }
32 
33 //-----------------------------------------------------------------------------
addChannel(ChannelID channel_nr,SignalGraphicsItem const * const signal_item)34 void YAxisWidget::addChannel(ChannelID channel_nr, SignalGraphicsItem const* const signal_item)
35 {
36     if (signal_item)
37         channel_nr2signal_graphics_item_[channel_nr] = signal_item;
38 }
39 
40 //-----------------------------------------------------------------------------
removeChannel(ChannelID channel_nr)41 void YAxisWidget::removeChannel(ChannelID channel_nr)
42 {
43     QMap<ChannelID, SignalGraphicsItem const*>::iterator it = channel_nr2signal_graphics_item_.find(channel_nr);
44 
45     if (it != channel_nr2signal_graphics_item_.end())
46     {
47         channel_nr2signal_graphics_item_.erase(it);
48         update ();
49     }
50 }
51 
52 //-----------------------------------------------------------------------------
changeSignalHeight(unsigned signal_height)53 void YAxisWidget::changeSignalHeight (unsigned signal_height)
54 {
55     channel_height_ = signal_height;
56     update ();
57 }
58 
59 //-----------------------------------------------------------------------------
changeYStart(int32 y_start)60 void YAxisWidget::changeYStart(int32 y_start)
61 {
62     y_start_ = y_start;
63     update ();
64 }
65 
66 //-----------------------------------------------------------------------------
updateChannel(ChannelID)67 void YAxisWidget::updateChannel (ChannelID)
68 {
69     update ();
70 }
71 
72 //-----------------------------------------------------------------------------
enableSeparator(bool enable)73 void YAxisWidget::enableSeparator(bool enable)
74 {
75     enable_separator = enable;
76     update();
77 }
78 
79 //-----------------------------------------------------------------------------
changeLabelColor(QColor labelColor)80 void YAxisWidget::changeLabelColor(QColor labelColor)
81 {
82     label_color_ = labelColor;
83     update();
84 }
85 
86 //!this section is set to be consistent with label_widget.cpp----------------
paintEvent(QPaintEvent *)87 void YAxisWidget::paintEvent(QPaintEvent*)
88 {
89     bool channel_overlapping = signal_view_settings_->getChannelOverlapping();
90     float64 signal_height = signal_view_settings_->getChannelHeight();
91     if (channel_overlapping)
92         signal_height = (signal_height +
93                  (signal_height * (channel_nr2signal_graphics_item_.size() - 1)
94                 * (1.0 - signal_view_settings_->getChannelOverlapping())))
95                 / channel_nr2signal_graphics_item_.size();
96 
97     int32 y_end = y_start_ + height();
98 
99     if (signal_height < 1)
100         return;
101 
102     QPainter painter (this);
103     painter.translate(0, -y_start_);
104     painter.setPen(label_color_);
105 
106 //    painter.setPen(QColor(0, 43, 130));
107 //    painter.drawLine(width() - 1, y_start_, width() - 1, y_end);
108 //    painter.setPen(Qt::black);
109 
110     float64 float_y_end = y_end;
111     auto iter = channel_nr2signal_graphics_item_.begin();
112 
113     for (float32 float_y = signal_height / 2;
114          float_y < float_y_end && iter != channel_nr2signal_graphics_item_.end();
115          float_y += signal_height, iter++)
116     {
117         paintYAxisLabels (&painter, iter.value()->getYOffset(),
118                           iter.value()->getYGridPixelIntervall(),
119                           iter.value()->getValueRangeFragment(),
120                           iter.value()->getPhysicalDimensionString(),
121                           signal_height);
122         painter.translate (0, signal_height);
123     }
124 
125 //    this is the bottom line
126     if (!channel_overlapping && enable_separator)
127         painter.drawLine (0, 0, width() - 1, 0);
128 
129 //    if (channel_overlapping)
130 //        return;
131 
132 //    for (float32 float_y = 0;
133 //         float_y <= signal_height * channel_nr2signal_graphics_item_.size();
134 //         float_y += signal_height)
135 //    {
136 //        painter.drawLine(0, float_y, width() - 1, float_y);
137 //    }
138 }
139 
140 //-----------------------------------------------------------------------------
contextMenuEvent(QContextMenuEvent * event)141 void YAxisWidget::contextMenuEvent (QContextMenuEvent* event)
142 {
143     QMenu menu;
144     menu.addAction(GuiActionFactory::getInstance()->getQAction("Channels per Page..."));
145     menu.addSeparator();
146     QAction* visibility_action = menu.addAction("Y Axis");
147     visibility_action->setCheckable (true);
148     visibility_action->setChecked (true);
149     connect (visibility_action, SIGNAL(triggered(bool)), SLOT(setVisible(bool)));
150     menu.exec (event->globalPos());
151 }
152 
153 //-------------------------------------------------------------------
paintYAxisLabels(QPainter * painter,float64 offset,float64 y_grid_pixel_intervall,double value_range_fragment,QString const & unit_string,float64 channelHeight)154 void YAxisWidget::paintYAxisLabels (QPainter* painter, float64 offset,
155                                     float64 y_grid_pixel_intervall,
156                                     double value_range_fragment,
157                                     QString const& unit_string, float64 channelHeight)
158 {
159     //Below, 0 is the relative upper border of each channel.
160     //despite being an absolute value, because the painter coordinates are readjusted
161     //automatically every time it moves to a new channel, hence 0 is the
162     //relative position of the upper border of each channel
163 #define UPPER_BORDER 0
164 
165     bool channel_overlapping = signal_view_settings_->getChannelOverlapping();
166 
167     if (!channel_overlapping && enable_separator)
168     {
169         painter->drawLine (0, UPPER_BORDER, width() - 1, UPPER_BORDER);
170     }
171 
172     paintYUnits (painter, unit_string, channelHeight);
173 
174     if (y_grid_pixel_intervall < 1)
175         return;
176 
177     while (y_grid_pixel_intervall < 11)
178     {
179         y_grid_pixel_intervall *= 2;
180         value_range_fragment *= 2;
181     }
182 
183     double value = 0;
184 
185     //offset + channelHeight / 2 is the position where 0 is for each channel
186     //However, 0 doesn't necessarily appear in the channel, if we are in
187     //"zero line fitted" mode and if the mean is far from 0.
188 
189     //In the following for loop, we are still going to calculate from  the position of 0
190     //even if 0 doesn't appear in the channel. Because in "view option" mode, the channel
191     //can be scrolled up and down arbitrarily.
192     for (float64 value_y = offset + channelHeight / 2; value_y < channelHeight;
193          value_y += y_grid_pixel_intervall)
194     {
195         //But we only paint the calibration when they are within the channel height.
196         //This can be dynamic if in the "view option" mode, since channels can be
197         //scolled up and down.
198 
199         //Here, value starts with 0 means painter will draw 0 first, and subtract
200         //value_range_fragment from value thereafter, so long as they are within channel
201         //height from UPPER_BORDER
202         if (value_y > UPPER_BORDER)
203         {
204 
205             painter->drawLine (width () - 5, value_y, width () - 1, value_y);
206             painter->drawText(0, value_y - 20, width () - 10, 40,
207                               Qt::AlignRight | Qt::AlignVCenter,
208                               QString::number (value));
209         }
210         value -= value_range_fragment;
211     }
212 
213     //We finished painting 0 and all negative numbers, now reset value to 0
214     value = 0;
215     //Now paint the positive numbers
216     for (float64 value_y = offset + channelHeight / 2 - y_grid_pixel_intervall;
217          value_y > UPPER_BORDER; value_y -= y_grid_pixel_intervall)
218     {
219         value += value_range_fragment;
220         //paint only if they position is within the range from UPPER_BORDER to
221         //the bottom (UPPER_BORDER + channelHeight). This can be dynamic when
222         //scrolling the channel.
223         if (value_y < channelHeight)
224         {
225             painter->drawLine (width () - 5, value_y, width () - 1, value_y);
226             painter->drawText(0, value_y - 20, width () - 10, 40,
227                               Qt::AlignRight | Qt::AlignVCenter,
228                               QString::number (value));
229         }
230     }
231 }
232 
233 //-------------------------------------------------------------------
paintYUnits(QPainter * painter,QString const & unit_string,float64 channelHeight)234 void YAxisWidget::paintYUnits (QPainter* painter, QString const& unit_string, float64 channelHeight)
235 {
236     if (unit_string.size() > 4)
237     {
238         painter->rotate(-90);
239         painter->drawText (5,  0, width(), channelHeight,
240                            Qt::AlignLeft | Qt::AlignVCenter,
241                            unit_string);
242         painter->rotate(90);
243     }
244     else
245         painter->drawText (5,  0, width() - 10, channelHeight,
246                            Qt::AlignLeft | Qt::AlignVCenter,
247                            unit_string);
248 }
249 
250 }
251