370 likes | 629 Views
OpenGL and Windows. Windows Forms Programming Roger Crawfis. Creating a nice GUI. The next several slides will walk you thru a particular design that I like for my applications. The order can be a little finicky, so if you mess up, delete all files and start over!!!
E N D
OpenGL and Windows Windows Forms Programming Roger Crawfis
Creating a nice GUI • The next several slides will walk you thru a particular design that I like for my applications. • The order can be a little finicky, so if you mess up, delete all files and start over!!! • The design consists of a GUI panel on the left and a view panel on the right, with a status bar and a main menu.
Note: VS 2003 shown • The following slides and images are old, create a new project using 2005. • Also, note there is a new SplitContainer that works better for this style of GUI. • Use it rather than the Panel/Splitter/Panel approach.
Create a new Project • Start Visual Studio .NET
Create a new Project • Select a C++ Windows Forms Application
Add your GUI elements • Resize the Form to be about 800 by 600, or whatever you like. • In this order, add the following elements from the Toolbox->Windows Forms. • StatusBar • Panel • Splitter • Panel
GUI design • In the Properties window you can make changes to the content and appearance of these controls. • Change the BackColor of the splitter. • Change the Dock property of panel1 to Left. • Change the Dock property of panel2 to Fill (the center icon). • Click on the Collections property of the statusBar and add three tabs. Type in some text. • On the StatusBar properties, set ShowPanels to true.
GUI Design • Build (Build->Build Solution) and run (Debug->Start without Debugging) your application. It should look something like this:
GUI Design • In the GUI panel, let’s make one large tabbed dialogue. • Drag a tabControl over to the GUI panel. • Set its dock to Fill. • In the properties select the Collection button and add four tab panels. • Lab2 • Extra • Grading • Readme
GUI Design • Your program should now look like this. New Title added Background image added
Examine the code • Okay. We have the basic lay-out. Notice we have not done any programming yet. • Right-click the Form1.h file in the Solution panel and select Edit Code. We have: • A public constructor • A protected Dispose • A private InitializeComponents • What is this! The source code is in the .h file? • Look at the Form1.cpp file.
InitializeComponents • There is a tree view structure in the code views with Visual Studio. You can collapse a method, class or region of code to hide the details as you work on other parts of the code. • The InitializeComponents method is collapsed by default. This is automatically generated by the Designer Window. It has all of the natty details on setting up the controls. • Uncollapse it and examine the code.
Lab2 • Okay, we have the basic lay-out, now we need to embed the business logic. • What are the lab requirements? • More specifically, what controls do we need? • Specifying the number of lines / points. • Reading in (selecting) an image file. • Specifying the line-width or point size. • Specify two color values.
Lab2 Controls • Selecting an image file. • Possible Options: • Add a text box and have the user type the path / filename into the textbox. (umm Yuck!!!) • Pre-load a set of image files and have the user select from this set. (lacks flexibility). • Bring up a dialog asking the user to select an image. Limit the selection to only the proper type of files. • How do we bring up the dialog? • What do we have to do to show the dialog?
Image file Dialogue • This is why you use a higher-level API. • With windows forms and .Net 1.1 this is really trivial. Follow these simple steps: • Drag an OpenFileDialog control to anywhere on your Form. Notice that it places it in a special window pane below your design. • Drag a Button control to the Lab2 tab panel. This will be used to bring up the dialog.
Image file Dialogue • Adjust the Button’s properties: • Change the text to “Load Texture” • Adjust the OpenFileDialog’s properties • Change the Title to “Select an image file for the texture” • In the Filter property add a string to aid in the right file type selection. Something like this: “All files (*.*)|*.*|Jpeg (*.jpg)|*.jpg|Png (*.png)|*.png”. Each pair here has the format (Text string to display | regular expression). Note that adding a space before the asterisk results in a different regular expression. • Set the Filter index to 2, such that jpeg is the default format. • Set the AddExtension to False. • Make sure the Multiselect property is False.
Image file Dialogue • Okay, we have now defined everything, such that the constructor is called and the properties are set. Nothing will happen though, we need to add some logic. • Double click the “Load Texture” button. This adds some template code and brings up the source code window (Form1.h). • It also added this line inside the InitializeComponents: this->button1->Click += new System::EventHandler(this, button1_Click); • Add the following line in button1_Click: this->openFileDialog1->ShowDialog();
Image file Dialogue • Run and test the program. Clicking on the button should bring up the openFileDialog.
Image file Dialogue • Note that the dialog simply selects the file, it does not open it or perform any logic yet. • We will address actually opening the file, reading in the image and creating a texture map from it later.
Lab2 Controls • Number of primitives • There are several choices here. • Textbox (you would need to validate that it only contained numbers). • Numeric Up/Down control (it has two speeds, both of which you can configure in the Properties page). • Trackbar or slider control (you may need to provide a text label that indicates the current value). • The reference lab uses both a numericUpDown and a Trackbar tightly coupled together.
Accessibility • Most controls have accessibility features built into them. • This allows for a mouse free control. • You can hit the tab key to reach the NumericUpDown and Trackbar controls and then use the arrow keys to change their values. • Holding down the key accelerates the change.
Trackbar • Let’s use the trackbar. • Drag a trackBar control to the Lab2 tab panel. • In the Properties page: • Set the Maximum value to 100,000 (or higher) • Set the (initial or current) Value to 10,000 • Set the LargeChange to 5,000 • Set the SmallChange to 1,000 • Set the Tick Frequency to 10,000 • Also add a Label control (static text) above the trackBar to describe its purpose.
Trackbar • We should now have something like:
Trackbar • Okay, we have a control and it can change values, etc. but we do not have anything useful connected to it. • Business logic: • There are many ways you can use this: • The lazy way – simply read the control’s value whenever you need it. This mixes the business logic and the GUI logic. • The global document way – Whenever the value changes, change a corresponding data value in some document class. This still mixes the logic. Bad data hiding. • The document/view way – Whenever the value changes, call a method on the document’s class. The document now knows it’s value has changed. • Perform some action (update display, verify data, etc.) • Keep a history for undo/redo.
Document Class • Okay, I will admit it, I used method #1 in the reference lab. But I am going to make you follow method #3. • Create and add a new class to your project. • Right-click on the solution and select Add->Add Class from the context menu. • Select a generic C++ class and give a name. • Change it to managed C++ by changing class to public __gc class in the header file. • Add the following private members: • int numPrimitives; • float lineWidth; • Color color1, color2;
Document Class • You also need to include: using namespace System::Drawing; • Add public methods to set (and optionally get) each of these. For instance: void SetNumPrimitives( const int nLines ) { numPrimitives = nLines; }
Document Class • Okay, now we can wire that to the trackBar. • Double-click the trackBar in the designer. • In the code template, add something like: impressionismDoc->SetNumPrimitives( trackBar1->Value ); • We need to create and add an instance of our document. Declare a private pointer to an instance. private: Impressionism *impressionismDoc; • In the constructor for Form1 create a new instance. impressionismDoc = new Impressionism();
Lab2 Status • Okay, we are about 1/3 of the way to having some implementation for lab2. • You need to add an OpenGLPanel as in HW #1 and Lab1 and create the OpenGL drawing calls. • We also still need to add the image file and texture map, as well as the other controls.
Done • The End • Ignore the following slides!!
OhioState::OpenGLPanel • The simplest possible canvas or rendering context. • No assumptions are made (single buffer, double buffer, etc.) • Burden on user to provide all information and event processing. • Can be used as a base-class for more intelligent classes.
OpenGLPanel : Forms::Control • All of the functionality of a Control. • Creates the OpenGL rendering context. • Overrides: • protected OnPaintBackground • protected OnPaint • protected OnResize • public set_BackColor property
Public methods • OpenGLPanel( PIXELFORMATDESCRIPTOR pfd ); • void AddPreRenderCallback( OpenGLRenderCallback *callback ); • void AddRenderCallback( OpenGLRenderCallback *callback ); • void AddPostRenderCallback( OpenGLRenderCallback *callback ); • void AddResizeCallback( OpenGLResizeCallback *callback ); • int GetFrameNumber() { return frameNumber; }; • void MakeCurrent(); • void SwapBuffers();
The Constructor • No assumptions, so the user passes in the PIXELFORMATDESCRIPTOR which indicates the number of color bits, stencil bits, etc. • Make sure the dwflag entry includes PFD_SUPPORT_OPENGL. • See the sample and the MSDN help for more information.
Callbacks • The OpenGLPanel is structured around registered callbacks. These are pointers to a function, or delegates. • Two publicly defined Delegate types: • public __delegate void OpenGLRenderCallback( const int frameNumber ); • public __delegate void OpenGLResizeCallback( const int width, const int height ); • These are really type definitions: • A pointer to a function that returns void and takes one parameter of type const int.
Callbacks • Like any other type, you create and instance: glPanel->AddRenderCallback( new OhioState::OpenGLRenderCallback( this, DrawScene ) ); • DrawScene is a public method of this, or Form1 here. • Can also use static methods, in which case, this would be replaced with the class type. thank-you garbage collection
Using OpenGLPanel • Checklist: • Create a Forms::Panel (in the designer). • In the Form1 constructor: • Create an OpenGLPanel and add it to the panel (call this glPanel). • Put all of your drawing code into a method that returns void and takes a single const int. • Create a callback instance and add it to glPanel. • Create a resize function, wrap it in a callback and add it to glPanel.