I am developing a program for windows using wxWidgets. I'm trying to implement a print function that will print a wxPanel (or wxWindow) to the printer. wxWidgets has a nice handy class that does this, if you draw into a DC.
Is there a way to get wxWidgets to draw a wxPanel or wxWindow in a DC?
I tried to use the HandlePrintClient (in response to a WM_PRINTCLIENT) function, but this just draws the background.
I also tried to create a printer DC and send it trough a similar function to HandlePrint, but the wxWidget stuff seems to be to tightly coupled with BeginPrint.
Is there some way to do what I want to do? Perhaps a class already written that will tack a wxScrolledWindow and send it to the printer? The window will have other controls and windows, like a wxGrid on it.
In the end, it is probably easier to draw what you want into the the printDC. However, with some care, you can use BLIT to copy what is displayed in your panel into the PrintDC without having to redraw everything.
So, in your override of the wxPrintout::OnPrintPage you can write something like this:
wxClientDC frameDC( wxGetApp().GetTopWindow() );
GetDC()->StretchBlit(0,0,5000,5000,
&frameDC, 0, 0, 500,500 );
This will copy everything displayed in your applications top level wondow into the printerDC.
The snag is that the print preview window tends to obliterate your top level frame contents when it pops up. If you have a large monitor and a small application window you can arrange things so they do not overlap
void MyFrame::OnPrint(wxCommandEvent& )
{
wxPrintPreview *preview = new wxPrintPreview(new MyPrintout(this), new MyPrintout(this));
wxPreviewFrame *frame = new wxPreviewFrame(preview, this,
"Demo Print Preview",
wxPoint(600, 100), // move preview window out of the way
wxSize(500, 500));
//frame->Centre(wxBOTH);
frame->Initialize();
frame->Show(true);
A better approach would be to BLIT the frame display into a memoryDC before popping up the print preview, then BLIT from the MemoryDC to the printerDC.
Something along these lines:
void MyFrame::OnPrint(wxCommandEvent& )
{
// save the display before it is clobbered by the print preview
static wxMemoryDC memDC;
static wxBitmap bitmap(500,500);
memDC.SelectObject( bitmap );
wxClientDC frameDC( wxGetApp().GetTopWindow() );
memDC.Blit(0,0,5000,5000,
&frameDC, 0, 0 );
wxPrintPreview *preview = new wxPrintPreview(new MyPrintout(memDC), new MyPrintout(memDC));
wxPreviewFrame *frame = new wxPreviewFrame(preview, this,
"Demo Print Preview",
wxPoint(600, 100), // move preview window out of the way
wxSize(500, 500));
frame->Centre(wxBOTH);
frame->Initialize();
frame->Show(true);
}
and then
class MyPrintout : public wxPrintout
{
wxMemoryDC & myMemDC;
public:
MyPrintout( wxMemoryDC & memDC)
: myMemDC( memDC )
{
}
bool OnPrintPage( int PageNum )
{
// copy saved dispay to printer DC
GetDC()->StretchBlit(0,0,5000,5000,
&myMemDC, 0, 0, 500,500 );
return true;
}
};
Related
I'm writing my first Qt 5 application... This uses a third-party map library (QGeoView).
I need to draw an object (something like a stylized airplane) over this map. Following the library coding guidelines, I derived from the base class QGVDrawItem my QGVAirplane.
The airplane class contains heading and position values: such values must be used to draw the airplane on the map (of course in the correct position and with correct heading). The library requires QGVDrawItem derivatives to override three base class methods:
QPainterPath projShape() const;
void projPaint(QPainter* painter);
void onProjection(QGVMap* geoMap)
The first method is used to achieve the area of the map that needs to be updated. The second is the method responsible to draw the object on the map. The third method is needed to reproject the point from the coordinate space on the map (it's not relevant for the solution of my problem).
My code looks like this:
void onProjection(QGVMap* geoMap)
{
QGVDrawItem::onProjection(geoMap);
mProjPoint = geoMap->getProjection()->geoToProj(mPoint);
}
QPainterPath projShape() const
{
QRectF _bounding = createGlyph().boundingRect();
double _size = fmax(_bounding.height(), _bounding.width());
QPainterPath _bounding_path;
_bounding_path.addRect(0,0,_size,_size);
_bounding_path.translate(mProjPoint.x(), mProjPoint.y());
return _bounding_path;
}
// This function creates the path containing the airplane glyph
// along with its label
QPainterPath createGlyph() const
{
QPainterPath _path;
QPolygon _glyph = QPolygon();
_glyph << QPoint(0,6) << QPoint(0,8) << QPoint(14,6) << QPoint(28,8) << QPoint(28,6) << QPoint(14,0);
_path.addPolygon(_glyph);
_path.setFillRule(Qt::FillRule::OddEvenFill);
_path.addText(OFF_X_TEXT, OFF_Y_TEXT, mFont , QString::number(mId));
QTransform _transform;
_transform.rotate(mHeading);
return _transform.map(_path);
}
// This function is the actual painting method
void drawGlyph(QPainter* painter)
{
painter->setRenderHints(QPainter::Antialiasing, true);
painter->setBrush(QBrush(mColor));
painter->setPen(QPen(QBrush(Qt::black), 1));
QPainterPath _path = createGlyph();
painter->translate(mProjPoint.x(), mProjPoint.y());
painter->drawPath(_path);
}
Of course:
mProjPoint is the position of the airplane,
mHeading is the heading (the direction where the airplane is pointing),
mId is a number identifying the airplane (will be displayed as a label under airplane glyph),
mColor is the color assigned to the airplane.
The problem here is the mix of rotation and translation. Transformation: since the object is rotated, projShape() methods return a bounding rectangle that's not fully overlapping the object drawn on the map...
I also suspect that the center of the object is not correctly pointed on mProjPoint. I tried many times trying to translate the bounding rectangle to center the object without luck.
Another minor issue is the fillup of the font... the label under the airplane glyph is not solid, but it is filled with the same color of the airplane.
How can I fix this?
Generically speaking, the general pattern for rotation is to scale about the origin first and then finish with your final translation.
The following is pseudocode, but it illustrates the need to shift your object's origin to (0, 0) prior to doing any rotation or scaling. After the rotate and scale are done, the object can be moved back from (0, 0) back to where it came from. From here, any post-translation step may be applied.
translate( -origin.x, -origin.y );
rotate( angle );
scale( scale.x, scale y);
translate( origin.x, origin.y );
translate( translation.x, translation.y )
I finally managed to achieve the result I meant....
QPainterPath projShape() const
{
QPainterPath _path;
QRectF _glyph_bounds = _path.boundingRect();
QPainterPath _textpath;
_textpath.addText(0, 0, mFont, QString::number(mId));
QRectF _text_bounds = _textpath.boundingRect();
_textpath.translate(_glyph_bounds.width()/2-_text_bounds.width()/2, _glyph_bounds.height()+_text_bounds.height());
_path.addPath(_textpath);
QTransform _transform;
_transform.translate(mProjPoint.x(),mProjPoint.y());
_transform.rotate(360-mHeading);
_transform.translate(-_path.boundingRect().width()/2, -_path.boundingRect().height()/2);
return _transform.map(_path);
}
void projPaint(QPainter* painter)
{
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setRenderHint(QPainter::TextAntialiasing, true);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
painter->setRenderHint(QPainter::HighQualityAntialiasing, true);
painter->setBrush(QBrush(mColor));
painter->setPen(QPen(QBrush(Qt::black), 1));
painter->setFont(mFont);
QPainterPath _path = projShape();
painter->drawPath(_path);
}
Unluckly I still suffer the minor issue with text fill mode:
I would like to have a solid black fill for the text instead of the mColor fill I use for the glyph/polygon.
I'm using the Embarcadero RAD Studio C++ builder XE7 compiler. In an application project, I'm using the both Windows GDI and GDI+ to draw on several device contexts.
My drawing content is something like that:
On the above sample the text background and the user picture are drawn with GDI+. The user picture is also clipped with a rounded path. All the other items (the text and the emojis) are drawn with the GDI.
When I draw to the screen DC, all works fine.
Now I want to draw on a printer device context. Whichever I use for my tests is the new "Export to PDF" printer device available in Windows 10. I prepare my device context to draw on an A4 viewport this way:
HDC GetPrinterDC(HWND hWnd) const
{
// initialize the print dialog structure, set PD_RETURNDC to return a printer device context
::PRINTDLG pd = {0};
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
// get the printer DC to use
::PrintDlg(&pd);
return pd.hDC;
}
...
void Print()
{
HDC hDC = NULL;
try
{
hDC = GetPrinterDC(Application->Handle);
const TSize srcPage(793, 1123);
const TSize dstPage(::GetDeviceCaps(hDC, PHYSICALWIDTH), ::GetDeviceCaps(hDC, PHYSICALHEIGHT));
const TSize pageMargins(::GetDeviceCaps(hDC, PHYSICALOFFSETX), ::GetDeviceCaps(hDC, PHYSICALOFFSETY));
::SetMapMode(hDC, MM_ISOTROPIC);
::SetWindowExtEx(hDC, srcPage.Width, srcPage.Height, NULL);
::SetViewportExtEx(hDC, dstPage.Width, dstPage.Height, NULL);
::SetViewportOrgEx(hDC, -pageMargins.Width, -pageMargins.Height, NULL);
::DOCINFO di = {sizeof(::DOCINFO), config.m_FormattedTitle.c_str()};
::StartDoc (hDC, &di);
// ... the draw function is executed here ...
::EndDoc(hDC);
return true;
}
__finally
{
if (hDC)
::DeleteDC(hDC);
}
}
The draw function executed between the StartDoc() and EndDoc() functions is exactly the same as whichever I use to draw on the screen. The only difference is that I added a global clipping rect on my whole page, to avoid the drawing to overlaps on the page margins when the size is too big, e.g. when I repeat the above drawing several times under the first one. (This is experimental, later I will add a page cutting process, but this is not the question for now)
Here are my clipping functions:
int Clip(const TRect& rect, HDC hDC)
{
// save current device context state
int savedDC = ::SaveDC(hDC);
HRGN pClipRegion = NULL;
try
{
// reset any previous clip region
::SelectClipRgn(hDC, NULL);
// create clip region
pClipRegion = ::CreateRectRgn(rect.Left, rect.Top, rect.Right, rect.Bottom);
// select new canvas clip region
if (::SelectClipRgn(hDC, pClipRegion) == ERROR)
{
DWORD error = ::GetLastError();
::OutputDebugString(L"Unable to select clip region - error - " << ::IntToStr(error));
}
}
__finally
{
// delete clip region (it was copied internally by the SelectClipRgn())
if (pClipRegion)
::DeleteObject(pClipRegion);
}
return savedDC;
}
void ReleaseClip(int savedDC, HDC hDC)
{
if (!savedDC)
return;
if (!hDC)
return;
// restore previously saved device context
::RestoreDC(hDC, savedDC);
}
As mentioned above, I expected a clipping around my page. However the result is just a blank page. If I bypass the clipping functions, all is printed correctly, except that the draw may overlap on the page margins. On the other hands, if I apply the clipping on an arbitrary rect on my screen, all works fine.
What I'm doing wrong with my clipping? Why the page is completely broken when I enables it?
So I found what was the issue. Niki was close to the solution. The clipping functions seem always applied to the page in pixels, ignoring the coordinate system and the units defined by the viewport.
In my case, the values passed to the CreateRectRgn() function were wrong, because they remained untransformed by the viewport, although the clipping was applied after the viewport was set in the device context.
This turned the identification of the issue difficult, because the clipping appeared as transformed while the code was read, as it was applied after the viewport, just before the drawing was processed.
I don't know if this is a GDI bug or a wished behavior, but unfortunately I never seen this detail mentioned in all the documents I read about the clipping. Although it seems to me important to know that the clipping isn't affected by the viewport.
I created a skin called customSliderTrack in the graphical editor of Adobe Flash CS5.5. This Slider is now in the "library" of the FLA file.
I can apply this skin with the following code:
var cls:Class = getDefinitionByName("CustomSliderTrack") as Class;
var tmpTrack:Sprite = new cls();
slider.setStyle("sliderTrackSkin",tmpTrack);
However due to the binary nature of the FLA file and lack of compatibility of different Versions of Adobe Flash I need to implement it all in Actionscript.
I understand that cls is a MovieClip object but I cant get the same results with new MovieClip(). I think this might be related to the dashed Lines in the graphical editor(I modified the default SliderTrack_skin). I havn't found out yet what they mean and how to replace them with Actionscript code.
setStyle automatically sets the track.height and track.width. In case of the track.height the slider.height attribute does not seem to have any effect. To work around this problem simply set the track.height to the best value.
To access the track extend the Slider class and override the configUI Function:
public class CustomSlider extends Slider
{
override protected function configUI():void
{
// Call configUI of Slider
super.configUI();
// The sprite that will contain the track
var t:Sprite = new Sprite();
// Draw the content into the sprite
t.graphics.beginFill(0x000000, 0.1);
t.graphics.drawRect(0, -15, width, 30);
t.graphics.endFill();
// Set the Sprite to be the one used by the Slider
this.setStyle("sliderTrackSkin",t);
// Reset the height to the value that it should be
track.height = 30;
}
}
Depending on the complexity of your track asset, you could accomplish this with the drawing API: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html
A very simple example would be:
var track:Sprite = new Sprite();
track.graphics.lineStyle(2, 0xcccccc);
track.graphics.beginFill(0x000000, 1);
track.graphics.drawRect(0, 0, 400, 20);
track.graphics.endFill();
track.scale9Grid = new Rectangle(2, 2, 396, 16);
slider.setStyle("sliderTrackSkin",track);
This creates a track that is just a black rectangle, 400x20 pixels in size. You can set the scale9grid in code to control how the skin scales. In the example above the rectangle's border wont scale, but the black rectangle inside will. Experimenting with the methods in the drawing API could be all you need.
If you need a more complex asset, I'd recommend loading an image and then passing that in to slider.setStyle.
I am working on an eBook app where I need to transition the screens from left to right and right to left. I tried many samples that I've found, but I am not successful. How do I change the screen frequently when user clicks on the screen from left to right and right to left. What is the basic idea for transition of pages. I went through the Developer Support Forum thread "page-flip effect" looking for a solution, but I can't see it.
The following code is not logical. In which position do I have to implement flip effect for flipping pages in the screen and how to implement it?
public class TransitionScreen extends FullScreen implements Runnable{
private int angle = 0;
Bitmap fromBmp,toBmp;
public TransitionScreen(){
}
public TransitionScreen(AnimatableScreen from,AnimatableScreen to) {
fromBmp = new Bitmap(Display.getWidth(), Display.getHeight());
toBmp = new Bitmap(Display.getWidth(), Display.getHeight());
Graphics fromGraphics = Graphics.create(fromBmp);
Graphics toGraphics = Graphics.create(toBmp);
Object eventLock = getApplication().getEventLock();
synchronized(eventLock) {
from.drawAnimationBitmap(fromGraphics);
to.drawAnimationBitmap(toGraphics);
// Interpolate myOffset to target
// Set animating = false if myOffset = target
invalidate();
}
try {
synchronized (Application.getEventLock()) {
Ui.getUiEngine().suspendPainting(true);
}
} catch (final Exception ex) {
}
}
protected void paint(Graphics g){
//control x,y positions of the bitmaps in the timer task and the paint will just paint where they go
g.drawBitmap(0,0, 360,
480, toBmp, 0, 0);
g.drawBitmap(0, 0, 360,
480, fromBmp, 0, 0);
// invalidate();
}
protected boolean touchEvent(TouchEvent event) {
if (!this.isFocus())
return true;
if (event.getEvent() == TouchEvent.CLICK) {
// invalidate();
}
return super.touchEvent(event);
}
}
Assuming you're working with version 5.0 or later of the OS, this page has a simple example:
http://docs.blackberry.com/en/developers/deliverables/11958/Screen_transitions_detailed_overview_806391_11.jsp
From where did you get the code sample posted in your question? That code does not appear to be close to working.
Update: you can actually animate transitions like this yourself fairly simply. Assuming you know how to use the Timer class, you basically have a class-level variable that stores the current x-position of your first Bitmap (the variable would have a value of 0 initially). In each timer tick, you subtract some amount from the x-position (however many pixels you want it to move each tick) and then call invalidate();.
In each call to the paint method, then, you just draw the first bitmap using the x-position variable for the call's x parameter, and draw the second bitmap using the x-position variable plus the width of the first bitmap. The resulting effect is to see the first bitmap slide off to the left while the second slides in from the right.
A caveat : Because this is java (which means the timer events are not real-time - they're not guaranteed to occur when you want them to), this animation will be kind of erratic and unsmooth. The best way to get smooth animation like this is to pre-render your animation cells (where each is a progressive combination of the two bitmaps you're transitioning between), so that in the paint method you're just drawing a single pre-rendered bitmap.
My question is rather simple, but I couldn't find an answer, could be because I'm using the wrong terms, but let me try: is there a way for a BlackBerry application (extending the regular Screen component) to keep the status bar visible (by status bar, to clarify, I mean the area where you see the battery strength, network name, signal strength, etc.)?
thank you
There is as yet (in my experience up to OS version 4.6) no API exposed to do this, strange as that is. You may of course program your own status bar, as many applications do, if you feel it necessary. But you have to gather the information and display the status information with logic coded into your own program.
Here's some sample code. First, for a nice title bar, look here: http://www.naviina.eu/wp/blackberry/iphone-style-field-for-blackberry/
To display a battery strength image:
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.DeviceInfo;
...
public static Bitmap getBatteryImage(){
int batteryPercent = DeviceInfo.getBatteryLevel();
int val = 1;
if(batteryPercent > 80){
val = 5;
}else if(batteryPercent > 60 ){
val = 4;
}else if(batteryPercent > 40){
val = 3;
}else if(batteryPercent > 20){
val = 2;
}else {
val = 1;
}
Bitmap batteryImage = Bitmap.getBitmapResource("mybattery"+val+".png");
return batteryImage;
}
...
You need to create images mybattery1.png to mybattery5.png, and place them in your src folder. A good size to start with is 28x11 pixels (GIMP is a good free image editor). If you used the title bar code from Naviina.eu, then insert the following code in the paint method, like so:
protected void paint(Graphics graphics) {
...
int w = this.getPreferredWidth();
int h = this.getPreferredHeight();
Bitmap batteryImage = getBatteryImage();
int batteryStartY = (h - batteryImage.getHeight()) / 2;
graphics.drawBitmap(w - batteryImage.getWidth(), batteryStartY, w, h,
batteryImage, 0, 0);
...
}
Some things to note: the image(s) do not refresh unless you invalidate the screen or push/pop to another screen. Also, you may want smaller images for a Pearl vs. a Curve or Storm.
You could use the StandardTitleBar, it might be a bit more straightforward.
http://www.blackberry.com/developers/docs/6.0.0api/net/rim/device/api/ui/component/StandardTitleBar.html
There are actually three places you can insert your status information in a MainScreen subclass:
Banner area - is located at the top
of the screen
Title area - is below the Banner area and normally has a different background
Status area - at the bottom of the screen
You use setBanner(Field), setTitle(Field) and setStatus(Field) to display information as shown as follows:
HorizontalFieldManager hfm = new HorizontalFieldManager();
EncodedImage logo = EncodedImage.getEncodedImageResource("img/Logo.png");
Bitmap bm = logo.getBitmap();
hfm.add(new BitmapField(bm));
hfm.add(new LabelField("Banner area"));
setBanner(hfm);
setTitle(new LabelField("Title area", LabelField.FIELD_HCENTER));
setStatus(new LabelField("Status area", LabelField.FIELD_HCENTER));
The advantage is that each method accepts a Field as an argument and the programmer can compose a complex Field with managers.