How to center align text horizontally? - imgui

I am creating a text in ImGui. It automatically aligns right, how do I make just that one text align in the center?
ImGui::Text("Example Text");
I don't believe there is a function to do this. I know you can do it for a box or widget, but how would I for a simple text?

void TextCentered(std::string text) {
auto windowWidth = ImGui::GetWindowSize().x;
auto textWidth = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
ImGui::Text(text.c_str());
}

Just want to add a solution for multi-line text to save 5 minutes for someone.
void TextCentered(std::string text) {
float win_width = ImGui::GetWindowSize().x;
float text_width = ImGui::CalcTextSize(text.c_str()).x;
// calculate the indentation that centers the text on one line, relative
// to window left, regardless of the `ImGuiStyleVar_WindowPadding` value
float text_indentation = (win_width - text_width) * 0.5f;
// if text is too long to be drawn on one line, `text_indentation` can
// become too small or even negative, so we check a minimum indentation
float min_indentation = 20.0f;
if (text_indentation <= min_indentation) {
text_indentation = min_indentation;
}
ImGui::SameLine(text_indentation);
ImGui::PushTextWrapPos(win_width - text_indentation);
ImGui::TextWrapped(text.c_str());
ImGui::PopTextWrapPos();
}

This comment on a similar GitHub issue could help, haven't tried it myself though:
void TextCenter(std::string text) {
float font_size = ImGui::GetFontSize() * text.size() / 2;
ImGui::SameLine(
ImGui::GetWindowSize().x / 2 -
font_size + (font_size / 2)
);
ImGui::Text(text.c_str());
}

Well more performant way without std::string
void TextCenter(const char* text, ...) {
va_list vaList = nullptr;
va_start(vaList, &text, );
float font_size = ImGui::GetFontSize() * strlen(text) / 2;
ImGui::SameLine(
ImGui::GetWindowSize().x / 2 -
font_size + (font_size / 2)
);
ImGui::TextV(text, vaList);
va_end(vaList);
}

Although I've gone with the method others have listed here, there is another method using Selectable:
static bool test = false;
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.5f));
ImGui::Selectable("Test", &test, 0, ImVec2(80,10));
ImGui::PopStyleVar();
The bad:
Items have hover effects that cannot be disabled without making text gray.
may have extra overhead
The Good:
Easy
NB: Unlike the other solutions, this one works in Table cells when table column auto-width is used. (double click on dividers)

In short:
void DrawTextCentered(const char* text)
{
ImGui::SetCursorPosX( (ImGui::GetWindowWidth() - ImGui::CalcTextSize(text).x) / 2.f);
ImGui::Text(text);
}

Related

When I resize an image on Qt, how can I focus to middle point of widget?

I can change the resolution of an image with a slider. I use QGraphicsScene for displaying image.But the problem is when I resize image, I couldn't focus to a point.You can see my code here.
This is first part, so its for resize an image.It's working.
void MainWindow::on_sld_scale_valueChanged(int value) {
//for resize an image with slider
int w = m_image->width();
int h = m_image->height();
int new_w = (w * value)/100;
int new_h = (h * value)/100;
m_pixmap = QPixmap::fromImage(
m_image->scaled(new_w, new_h, Qt::KeepAspectRatio, Qt::FastTransformation));
This second part of my function.It is my algorithm for focus to middle point of widget but it doesn't work.
//for focus to middle point of widget
auto views = m_scene->views();
Q_ASSERT(views.size() == 1);
auto view = views.first();
int pos_x = view->horizontalScrollBar()->value();
int pos_y = view->verticalScrollBar()->value();
show_pixmap();
qDebug() << "x" << pos_x << "w" << view->width()
<< "sbw" << view->verticalScrollBar()->width()
<< "y" << pos_y << "h" << view->height()
<< "sbh" << view->horizontalScrollBar()->height();
int a = pos_x + (view->width()) + (view->verticalScrollBar()->width());
int b = pos_x + (view->height()) + (view->horizontalScrollBar()->height());
a = a * (value/100.0);
b = b * (value/100.0);
m_pixmap = QPixmap::fromImage(*m_image);
view->horizontalScrollBar()->setValue(a);
view->verticalScrollBar()->setValue(b);
}
This is my source code.I can resize the image but I can't focus to middle point.Can you help me to solve this problem?
Don't you mix QScrollBar::width to QScrollBar::maximum? They have difficult relation.
You better don't touch view's scroll bars. Try QGraphicsView::centerOn or even QGraphicsView::fitInView (did you mean "focus" to be "place to center of the view"?). Like this:
const qreal centerX = pixmapItem->x() + pixmapItem->width() / 2;
const qreal centerY = pixmapItem->y() + pixmapItem->height() / 2;
viewer->centerOn(centerX , centerY);
where pixmapItem is a QGraphicsPixmapItem.
As #mvidelgauz supposed, why don't you scale a QGraphicsPixmapItem in the scene? May this be a better solution to rescale it?
Don't recreate pixmap, use QGraphicsView's methods setResizeAnchor() and scale()

Fixed Aspect Ratio For Qt Windows

I have read many questions about this topic but all I have found are either unanswered, do not do what I want, or simply do not work.
My problem is that of maintaining a height to width ratio while resizing my main window. So far I have seen propositions to override 'QLayout' and use 'heightForWidth' to perform the deed, to use the 'resizeEvent' call back to change the size hint, and to use the 'resizeEvent' call back to resize the window.
The first two options I mentioned only work in the examples for dragging width; I want a user to be able to drag width or height. The second of the two I attempted to adapt for my purposes but it causes my code to crash (the first relies on the 'heightForWidth' function and I cannot find a 'widthForHeight' function {The comments in this thread support my conclusion: How to maintain widgets aspect ratio in Qt?}). The third option was what I originally attempted but has a lot of bugs: most of the time the window snaps back to its original size, having rapidly transitioned between that and where it should be while dragging the border with the mouse.
The attempt which causes a crash:
void window::resizeEvent(QResizeEvent *event)
{
//window::resizeEvent(event); causes crash
if ((this->width() * 5 / 8) > (this->height() + 1)
|| (this->width() * 5 / 8) < (this->height() - 1))
{
updateGeometry();
}
}
QSize window::sizeHint() const
{
QSize s = size();
if (lastWidth != s.width())
{
s.setHeight((s.width()*5)/8);
//s.setWidth(window::sizeHint().width());
}
else if (lastHeight != s.height())
{
s.setWidth((s.height()*8)/5);
//s.setHeight(window::sizeHint().height());
}
return s;
}
The attempt which has bugs:
void window::resizeEvent(QResizeEvent *)
{
//If the window does not have the correct aspect ratio
//This should be false if this function caused the resize event,
// preventing a loop
if ((this->width() * 5 / 8) > (this->height() + 1)
|| (this->width() * 5 / 8) < (this->height() - 1))
{
int currentHeight = this->height();
int currentWidth = this->width();
//Change the dimension the user did not to match the user's change.
if (lastWidth != currentWidth)
{
lastWidth = currentWidth;
currentHeight = currentWidth * 5/8;
lastHeight = currentHeight;
}
else
{
lastHeight = currentHeight;
currentWidth = currentHeight * 8/5;
lastWidth = currentWidth;
}
//update the change
this->resize(currentWidth,currentHeight);
}
}

Custom placeholder in QLineEdit

I want to have a QLineEdit with the specific placeholder text format: it needs to have left aligned and right aligned text. Here is an example:
Any ideas?
Unfortunately, this seems to be all hard coded in void QLineEdit::paintEvent(QPaintEvent *) as follows:
if (d->shouldShowPlaceholderText()) {
if (!d->placeholderText.isEmpty()) {
QColor col = pal.text().color();
col.setAlpha(128);
QPen oldpen = p.pen();
p.setPen(col);
QRect ph = lineRect.adjusted(minLB, 0, 0, 0);
QString elidedText = fm.elidedText(d->placeholderText, Qt::ElideRight, ph.width());
p.drawText(ph, va, elidedText);
p.setPen(oldpen);
}
}
You could reimplement this on your own in a subclass if you wish.
Naturally, you could also "cheat" with space and font sizes, but that would require a bit more work, and would be nastier in the end, too, let alone long-term reliability.
You could also contribute to the Qt Project to make this class more flexible, but they could reject it with the reason of not being common case enough. It is up to the maintainer(s).
Thanks, #lpapp ! His advice is right. Here is the code, I created from the Qt sources suggested by #lpapp :
void LineEdit::paintEvent(QPaintEvent *e) {
QLineEdit::paintEvent(e);
if (!text().isEmpty()) {
return;
}
QPainter p(this);
QStyleOptionFrameV2 panel;
initStyleOption(&panel);
QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
r.setX(r.x() + textMargins().left());
r.setY(r.y() + textMargins().top());
r.setRight(r.right() - textMargins().right());
r.setBottom(r.bottom() - textMargins().bottom());
QFontMetrics fm = fontMetrics();
int minLB = qMax(0, -fm.minLeftBearing());
int minRB = qMax(0, -fm.minRightBearing());
int vscroll = r.y() + (r.height() - fm.height() + 1) / 2;
static const int horizontalMargin = 2; // QLineEditPrivate::horizontalMargin
QRect lineRect(r.x() + horizontalMargin, vscroll, r.width() - 2*horizontalMargin, fm.height());
QRect ph = lineRect.adjusted(minLB, 0, -minRB, 0);
QColor col = palette().text().color();
col.setAlpha(128);
p.setPen(col);
QString left = fm.elidedText("left", Qt::ElideRight, ph.width());
Qt::Alignment leftAlignment = QStyle::visualAlignment(Qt::LeftToRight, QFlag(Qt::AlignLeft));
p.drawText(ph, leftAlignment, left);
QString right = fm.elidedText("right", Qt::ElideRight, ph.width());
Qt::Alignment rightAlignment = QStyle::visualAlignment(Qt::LeftToRight, QFlag(Qt::AlignRight));
p.drawText(ph, rightAlignment, right);
}
I don't know an easy way to do this. You could try to calculate the pixel width (using QFontMetrics) of both placeholder-parts and calculate the number of spaces you need to insert between the placeholder-parts to let them appear aligned. You wuld need to set/update the calculated placeholder whenever the size of the widget changes.

c++ Fade between colors? (Arduino)

I currently have this thats fading between 2 set colours:
for(int i=0;i<nLEDs;i++){
a = (255 / 100) * (incomingByte * sensitivity);
r = (r * 7 + a + 7) / 8;
g = (g * 7 + (255 - a) + 7) / 8;
b = 0;
FTLEDColour col = { r , g , b };
led.setLED(i, col);
}
But now im trying to allow users to enter their own colours:
// > Colour fade, Start colour
int colFade1Red = 0;
int colFade1Green = 255;
int colFade1Blue = 0;
// > Colour fade, End colour
int colFade2Red = 255;
int colFade2Green = 0;
int colFade2Blue = 0;
int fadeAm = 7; // Fade speed
with the fading code:
void ColourFade(){
for(int i=0;i<nLEDs;i++){
r = ctest(colFade1Red, colFade2Red, r);
g = ctest(colFade1Green, colFade2Green, g);
b = ctest(colFade1Blue, colFade2Blue, b);
FTLEDColour col = { r , g , b };
led.setLED(i, col);
}
}
int ctest(int col1, int col2, int cur){
int temp = col1 - col2;
if(temp < 0) { temp = -temp; }
int alp = (temp / 100) * (incomingByte * sensitivity);
if(col1 < col2){
return (cur * fadeAm + (col2 - alp) + fadeAm) / (fadeAm +1 );
} else {
return (cur * fadeAm + alp + fadeAm) / (fadeAm +1 );
}
}
But this starts with the Second user colour and fades into pink. How would I fade colours properly?
Also "incomingByte" is a value between 0 and 100, and the code is in a update loop.
Smooth transitions between colours is best done in a different colour space (IMHO).
As an example, to transition from bright red to bright green, do you want to go via bright yellow (around the edge of the colour wheel) or via #808000 (murky yellow) - which is what a straight line interpolation would give you in the RGB domain.
Having done this for my Moodlamp app, I used the HSL colour space. I specified a start colour and end colour, along with a number of steps for the transition to take. That enabled me to calculate how much to adjust H, S and L by at each point in the transition.
Only at the point of using the colour did I convert back to RGB.
You can see the javascript code here (please bear in mind it's the first Javascript I ever wrote, so if it seems non-idiomatic, that's probably why!):
https://github.com/martinjthompson/MoodLamp/blob/master/app/assistants/lamp-assistant.js
It's impossible to fade to pink beacuse you are starting from red and ending with green.
To avoid this kind of mistake I suggest you to write an object oriented code.
If you don't want to write the classes to handle a 3D vectonr you can use the Arduino Tinker Library
I wrote this example for you:
#include <Vect3d.h>
#include <SerialLog.h>
Tinker::Vect3d<float> red(255,0,0);
Tinker::Vect3d<float> green(0,255,0);
Tinker::SerialLog serialLog;
void setup(){
Serial.begin(9600);
serialLog.display("Fade color example");
serialLog.endline();
}
void loop(){
//fade factor computation
const uint32_t t = millis()%10000;
const float cosArg = t/10000.*3.1415*2;
const float fade = abs(cos(cosArg));
//Here's the color computation... as you can see is very easy to do!! :)
Tinker::Vect3d<uint8_t> finalColor(red*fade + green*(1-fade));
//We print the vect3d on the arduino serial port
Tinker::LOG::displayVect3d(finalColor,&serialLog);
serialLog.endline();
delay(500);
}
Which prints the following output on the serial port
Fade color example
V[255;0;0]
V[242;12;0]
V[206;48;0]
V[149;105;0]
V[78;176;0]
V[0;254;0]
V[79;175;0]
V[150;104;0]
V[206;48;0]
V[242;12;0]
V[254;0;0]
V[242;12;0]
V[205;49;0]
V[148;106;0]
V[77;177;0]
V[1;253;0]
V[80;174;0]
V[151;103;0]
hope that this helps :)
uint8_t clrR = abs(255 * cos(<some var that changes in time>));
same for clrB & clrG

Memory leak in cvLoadImage function

I have made an Intensity meter in a pictureBox. For making this intensity meter I have used a picture as Dialer(Dialer.bmp) and making needle using a line. I am doing this using openCV. And changing the needle pointer we have created a thread at form load. And the code is as follows
private: System::Void FocusExposure_Load(System::Object^ sender, System::EventArgs^ e) {
if(ExposureThreadStatus)
th->Abort();
th = gcnew Thread(gcnew ThreadStart(this,&FocusExposure::UpdateIntensity));
th->Start();
ExposureThreadStatus = true;
}
void UpdateIntensity()
{
Intensity_Values data;
while(1)
{
data = ReadImage(focusBuffer);
System::Drawing::Bitmap ^bmp=drawImageMeter(data.Max);
this->pictureBox1->Image =this->pictureBox1->Image->FromHbitmap(bmp->GetHbitmap());
delete bmp;
Sleep(1000);
}
}
System::Drawing::Bitmap^ drawImageMeter(float intensity_value)
{
IplImage *Background =cvLoadImage("Dialer.bmp", 1);
int width,height;
if(counter==1)
{
width=Background->width;
height=Background->height;
counter++;
needle_center.x=width/2;
needle_center.y=height/2;
needle_top.x=needle_center.x;
needle_top.y=needle_center.y-140;
}
double const PI = 3.14159265358979323;
int x1 = needle_top.x;
int y1 = needle_top.y;
int x0=needle_center.x;
int y0=needle_center.y;
float angle;
CurrIntensity = intensity_value;
angle = CurrIntensity-PreIntensity;
angle= 0.0703125f * angle;
// degrees, not radians
float radians = angle * (PI / 180.0f); // convert degrees to radians
if (current_max==1)
{
current_max++;
int N1x1 = needle_top.x;
int N1y1 = needle_top.y;
needle1_top.x = ((N1x1-x0) * cos(radians)) - ((N1y1-y0) * sin(radians)) + x0;
needle1_top.y = ((N1x1-x0) * sin(radians)) + ((N1y1-y0) * cos(radians)) + y0;
}
needle_top.x = ((x1-x0) * cos(radians)) - ((y1-y0) * sin(radians)) + x0;
needle_top.y = ((x1-x0) * sin(radians)) + ((y1-y0) * cos(radians)) + y0;
cvLine(Background, needle_center, needle1_top, CV_RGB(0, 0, 255), 1, 4, 0);
cvLine(Background, needle_center, needle_top, CV_RGB(255, 0, 0), 1, 4, 0);
System::Drawing::Bitmap ^bmp = gcnew System::Drawing::Bitmap(Background->width,Background->height,Background->widthStep,System::Drawing::Imaging::PixelFormat::Format24bppRgb,(System::IntPtr)Background->imageData);
PreIntensity = CurrIntensity;
return bmp;
}
This code is working fine and giving output as per my requirement. But The only problem is that when I am opening the form It is giving memory leak. I have seen in task manager and also used Intel Vtune profiler. This profiler is showing Mismatched allocation/deallocation at the following line
IplImage *Background =cvLoadImage("Dialer.bmp", 1);
We need to reload this image because we are drawing the line at the image and when needle pointer has changed it require the Dialer Image without needle.
Can anybody please suggest me any solution to solve this memory leak Issue.
Any help will be appreciated.
It seems that drawImageMeter() is being called more than once. The problem is that each time cvLoadImage() is executed, it allocates space on the HEAP for the pixels. So after displaying the image you should release it with cvReleaseImage() so the data get's freed.
A quick and dirty (and horrible) fix would be to make the variablestatic:
static IplImage* Background =cvLoadImage("Dialer.bmp", 1);
But you should really change the design of your application so Background it's allocated only once. To do that you can either make it global variable and load it on the main() method, or make it a parameter of drawImageMeter():
System::Drawing::Bitmap^ drawImageMeter(IplImage* Background, float intensity_value)
{
// code
}