I have been learning wxWidgets for the last week, both by using IDEs and writing C++ code. I'm starting to despair at ever getting something that looks nice, as the ability to position items seems very limited.
I'm porting a large-ish project from C++Builder to VStudio, for cost reasons. I've got quite a few dialogs to recreate. They generally contain varying combinations of a 'blocks' of input - for lack of a better word. I chose wxWidgets for my GUI because I thought I could build up the dialogs in code from functions that produce a row of the form. For example in the dialog shown below I might have functions CreateLabelEdit, CreateLabelCombo, CreateBoldLabel, etc. I'm actually having some success at that part but failing miserably in styling the dialog.
Here are some of the design criteria from this particular form:
All of the inputs are left aligned and different widths appropriate to their contents.
The input labels are all right aligned.
The alignment point (ie. the width of the column containing the input labels is the same across all dialogs, and the widths of the various inputs is as well. Of course there are exceptions!
Other types of labels are aligned at a different spot. BoldLabels in this case.
Basically I want to control the size, location and alignment of my objects. Is this a case of having the wrong mindset for the tool I have chosen? Or can these types of things be accomplished with this tool?
PARTIAL SOLUTION
I've tried using absolute positions mixed with sizers and that doesn't work at all.
Then I tried using a flexgrid sizer to do a part of the example, If I can make one of the blocks then I can stitch together multiple blocks. It's all good except when I set the width of the first column using SetItemMinSize, as #VZ suggested, that item loses it's sizerflag settings:
form4::form4() :wxDialog(nullptr, wxID_ANY, "title")
{
wxSizerFlags flagT1(1);
flagT1.Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT).Border(wxTOP | wxLEFT, 10);
wxSizerFlags flagT2(1);
flagT2.Align(wxALIGN_CENTER_VERTICAL ).Border(wxTOP | wxLEFT, 10);
wxSizerFlags flagT3(1);
flagT3.Align(wxALIGN_CENTER_VERTICAL).Border(wxTOP | wxLEFT| wxRIGHT, 10);
wxBoxSizer* bSize = new wxBoxSizer(wxVERTICAL);
wxStaticText* _Text = new wxStaticText(this, wxID_ANY, "this should be bold");
wxFont _Font = _Text->GetFont();
_Font.MakeBold();
_Font.SetPixelSize(_Font.GetPixelSize()*1.1);
_Text->SetFont(_Font);
bSize->Add(_Text, wxSizerFlags().Border(wxTOP, 10).Center());
bSize->Add(new wxStaticText(this, wxID_ANY, "this should be bold"),
wxSizerFlags().Border(wxTOP, 10).Center());
wxFlexGridSizer* bSizer = new wxFlexGridSizer(3,0,0);
_Text = new wxStaticText(this, wxID_ANY, "bob");
// bSizer->SetItemMinSize(_Text, wxSize(150, -1)); //this has no effect here
bSizer->Add(_Text, flagT1);
bSizer->SetItemMinSize(_Text, wxSize(150, -1));// loses styling of flagT1
bSizer->Add(new wxTextCtrl(this, wxID_ANY, "default_value", wxDefaultPosition, wxSize(150, -1)), flagT2);
bSizer->Add(new wxStaticText(this, wxID_ANY, "cm", wxPoint(200, 20)), flagT3);
bSizer->Add(new wxStaticText(this, wxID_ANY, " longer"), flagT1);
bSizer->Add(new wxTextCtrl(this, wxID_ANY, "default_value2", wxPoint(-1, -1), wxSize(150, -1)), flagT2);
bSizer->Add(new wxStaticText(this, wxID_ANY, "cm", wxPoint(200, 20)), flagT3);
bSizer->Add(new wxStaticText(this, wxID_ANY, "bob"), flagT1);
bSizer->Add(new wxTextCtrl(this, wxID_ANY, "default_value3", wxPoint(-1, -1), wxSize(150, -1)), flagT2);
bSizer->Add(new wxStaticText(this, wxID_ANY, "cm", wxPoint(200, 20)), flagT3);
bSize->Add(bSizer);
bSize->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags(0).Border(wxALL, 10));
this->SetSizerAndFit(bSize);
// this->Layout();
this->Centre(wxBOTH);
}
produces this:
This particular dialog is not especially simply to create in wxWidgets because it can't be done with a simple wxFlexGridSizer. You have several choices:
If you're willing to compromise slightly, move the bold labels entirely into the left column. Then it becomes a simple two column wxFlexGridSizer and creating it should be very easy in either the code or a dialog editor.
If you want to keep precisely this layout but still would like to use wxFlexGridSizer, you need to have several of them and manually align their first columns. The way to do is to find the width of the longest label (use GetTextExtent()) and set the minimal item size (SetItemMinSize()) to it for all sizers.
If you don't want to bother with width computation but still want to have this exact layout, you can also use wxGridBagSizer. Personally, I don't recommend using it because it's messier (you have to manually track columns/rows), but it does allow to do exactly what you want.
Related
Right now I'm using wxWidgets msw 3.0.5 on Windows 7 and the xrc file was created with wxFormBuilder 3.10.1
Normally I use something like this to avoid my wxframe be resized smaller than my child controls:
MyFrame::MyFrame() : wxFrame( NULL, wxID_ANY, wxT("Sometitle") )
{
wxBoxSizer* frameSizer = new wxBoxSizer( wxVERTICAL );
wxPanel* panel = new wxPanel( this, wxID_ANY );
// ..
frameSizer->Add( panel, 1, wxEXPAND | wxALL, 0 );
SetSizerAndFit( frameSizer );
Centre();
}
But, when I'm use xrc file to load my frame I using like this:
MyFrame::MyFrame()
{
// used in App::OnInit()
// wxXmlResource::Get()->InitAllHandlers();
// wxXmlResource::Get()->Load( wxT("some.xrc" ) );
wxXmlResource::Get()->LoadFrame( this, NULL, wxT("MyFrame") );
// ...
Fit(); // this resize my frame to show my child controls as expected (like SetSizerAndFit does )
SetMinClientSize(GetSize()); // is this the right way?
Centre();
}
Fit() resize my frame same as SetSizerAndFit, but if don't use SetMinClientSize my frame can be horrible resize shriking and hiden my controls. So my questions are:
Is there a feature that I'm missing in wxFormBuilder to avoid resize less the frame child controls?
Using SetMinClientSize is the right way?
Is there are any other ways in xrc file to have the same effect like SetSizerAndFit?
Thanks.
Note: this happens also in my linux wxwidgets library
The names are unfortunately confusing, but SetSizerAndFit() calls SetSizer() and wxSizer::SetSizeHints(), and not wxWindow::Fit() as might be expected. So to have exactly the same behaviour as in the first case you need to call GetSizer()->SetSizeHints(this) in your frame code.
And, FWIW, wxSizer::SetSizeHints() does something sensibly equivalent to
wxSize size = GetBestClientSize();
SetMinClientSize(size);
SetClientSize(size);
i.e. it sets both the current and min sizes, which is what you presumably want.
Is there a way to tell oxyplot to use the x value as the connect/draw order for a line plot instead of the order that the points are added?
Let's assume I have this huge amount of points (2Gb in RAM) of a time series. At a higher level I am only adding a small percentage of those points and as the user zooms into a more specific region I add more points to the series so he can see more detail. Almost like a texture mimap. I can add all the points at once but then oxy becomes really slow when I tries to render all those points in a single window in WPF Maybe there is another solution here?
The problem that I am facing is that oxy draws a line from the last point in the graph to the first one of the new batch because it uses the series points list as the draw order not the X value. Is there a way to change this?
Here is a tiny example based on the 'WPF Simple Example':
var plotModel = new PlotModel { Title = "Simple example", Subtitle = "using OxyPlot" };
var timeAxis = new DateTimeAxis
{
Position = AxisPosition.Bottom,
StringFormat = "hh:mm:ss",
};
plotModel.Axes.Add(timeAxis);
var now = DateTime.UtcNow;
// Create two line series (markers are hidden by default)
var series1 = new LineSeries { Title = "Series 1", MarkerType = MarkerType.Circle };
series1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(now), 5));
series1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(now.AddMinutes(1)), 7));
series1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(now.AddMinutes(2)), 8));
series1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(now.AddMinutes(0.5)), 10));
// Add the series to the plot model
plotModel.Series.Add(series1);
// Set the Model property, the INotifyPropertyChanged event will make the WPF Plot control update its content
this.Model = plotModel;
Here is the output, note that the line draws "back in time":
Is there a solution that does not involves me sorting the points list (not even sure if this works)?
Or maybe I am doing this extra points for details thing completely wrong :)
I'm trying to draw what amounts to a screen-tone effect over an existing image, but I'd like to leave an area blank. Think of how spotlights in old games are sometimes done, where most of the image is darkened, and but part of it is the "normal" color.
To do this, I need my overlay to be transparent (since the original image has to show through). I'm also drawing this with wxDC.DrawCircle(...) (with a transparent brush), since it allows me to not draw over a circular area.
Problem is, the stipple (screen tone effect) isn't transparent, it's solid. I've tried just about everything I can think of, but nothing seems to work.
My current code is roughly like this:
const char* ScreenToneColor[] =
{
/* columns rows colors chars-per-pixel */
"3 3 2 1",
"X c Black",
"O c None",
/* pixels */
"OOO",
"OXO",
"OOO"
};
CustomPanel::CustomPanel(wxWindow* parent)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(151, 151))
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
// MemberVariables
m_Stipple = wxBitmap(wxImage(ScreenToneColor));
m_ScreenTone = wxPen(*wxBLACK, 2 * VeryLargeRadius, wxPENSTYLE_STIPPLE);
m_ScreenTone.SetStipple(m_Stipple);
}
// Supplied with a wxAutoBufferedDC
void CustomPanel::Render(wxDC& dc)
{
dc.SetBrush(*wxGREEN_BRUSH);
dc.DrawRectangle(m_PanelRectange);
// "fade out" trimmed areas by drawing a ring.
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen(m_ScreenTone);
dc.DrawCircle(m_AnimatedCenter, VeryLargeRadius + m_VisibleRadius);
}
I've tried supplying the mask, using the different stipple masks (avoiding wxPENSTYLE_STIPPLE_MASK_OPAQUE), etc.
I'm on Windows 10 and compiling against wxWidgets 3.1, although the project is being built/run on other OSs, and possibly a lower library version.
wxDC doesn't support transparency with just the only exception of drawing bitmaps with alpha channel. If you want to do anything involving alpha, you need to use wxGraphicsContext and related classes. I'm not sure if GDI+ or Direct2D implementations of it currently handle this correctly, but after checking the code it seems like at least the former one should.
I try to write small GUI programm for windows using wx and wxFrame. I have this code in ctor:
MainFrame::MainFrame(wxPoint pos)
: wxFrame(NULL, wxID_ANY, wxT("Sample text"), pos,
wxDefaultSize, wxBORDER_NONE | wxFRAME_NOTASKBAR)
{
SetSize(wxSize(250, 100));
CreateLayout();
}
And CreateLayout function:
void MainFrame::CreateLayout()
{
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(new wxStaticText(this, -1, "1000.56"), 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 5);
SetSizer(sizer);
}
As you can see, I want to align text to vertical center of the frame. But with this set of frame style flags sizers doesn't work! They work only if i set wxDEFAULT_FRAME_STYLE! But i need to have frame without borders and capture and close|minimize|maximize buttons, how can I get this work?
In the MainFrame constructor, move the call to SetSize() after the call to CreateLayout(). That's it.
Normally, the call to Show() after creating the frame should make the above unnecessary, but it looks like in this case it doesn't (see the details below). Anyway, I think it makes sense to first set up all the sizers and controls inside the window, and then change the size to trigger a Layout(), just to be on the safe side.
This also means an alternative solution is to leave the call to SetSize() where it is, and force a layout by adding a call to Layout() at the very end of your CreateLayout(). But... I like being efficient (read: lazy); I'd go with the first solution.
EDIT: I've found some information regarding why this happens.
For a wxFrame created with wxDEFAULT_FRAME_STYLE, the call to Show() ends up calling ::ShowWindow(), which triggers a WM_SIZE, which eventually triggers a Layout().
For a wxFrame created with wxBORDER_NONE, ::ShowWindow() doesn't seem to trigger a WM_SIZE, which is why the solutions above for forcing the call to Layout() are necessary.
I'm currently creating a PDF with PdfSharp which mostly consists of text and some images.
The text elements have different colors. My problem is that as soon as I use a different color than the color I started with, the text is not visible in the resulting PDF (e.g. I start with black text, switch to a red text, the red text is not visible). All text elements are in the resulting PDF (I can select them), but the red elements are invisible.
So here is the code:
// Create a new PDF document with one page
var document = new PdfDocument();
var page = document.AddPage();
page.Width = 800;
page.Height = 600;
var defaultFont = new XFont("Arial", 12, XFontStyle.Regular, new XPdfFontOptions(PdfFontEmbedding.Always));
var gfx = XGraphics.FromPdfPage(page);
// black text
gfx.DrawString("black", defaultFont, XBrushes.Black, new XRect(x, y, width, height), XStringFormats.Center);
// red text
gfx.DrawString("red", defaultFont, XBrushes.Red, new XRect(x2, y2, width2, height2), XStringFormats.Center);
I've already found a solution (re-creating the XGraphics object) but it's quiete messy because it needs to be called after each color change:
// ...
// black text
gfx.DrawString("black", defaultFont, XBrushes.Black, new XRect(x, y, width, height), XStringFormats.Center);
// disposing the old graphics context and creating a new one seems to help
gfx.Dispose();
gfx = XGraphics.FromPdfPage(page);
// red text
gfx.DrawString("red", defaultFont, XBrushes.Red, new XRect(x2, y2, width2, height2), XStringFormats.Center);
I guess there is a better solution, but I couldn't find one yet.
Edit
As suggested in this answer, I wanted to create a SSCCE. During the creation I found the actual bug. Instead of XBrushes.Red I used an own defined XBrush, but didn't mention it in the above code snippet, because I thought it was unnecessary.
As already mentioned in the last section of the question, I used an own defined brush instead of XBrushes.Red.
I defined it the following way:
XBrush redBrush = new XSolidBrush(new XColor {R = 207, G = 0, B = 44});
This way the brush only worked after I disposed the graphics object and created a new one. But after some googling I found the correct way to define a brush:
XBrush redBrush = new XSolidBrush(XColor.FromArgb(207, 0, 44));
I tried to replicate your problem using your code snippet and PDFsharp version 1.32. I used VS Express 2013 which automatically converted all projects to .NET 4.5.
I tried both builds (GDI+ and WPF) and all colours worked fine for me.
So instead of just a code snippet you should provide an SSCCE.
See also:
http://forum.pdfsharp.net/viewtopic.php?p=2094#p2094