Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7018 Articles
article-image-debugging-mechanisms-oracle-sql-developer-21-using-plsql
Packt
08 Sep 2010
7 min read
Save for later

Debugging mechanisms in Oracle SQL Developer 2.1 using Pl/SQL

Packt
08 Sep 2010
7 min read
Once your PL/SQL code has successfully compiled, it is important to review it to make sure it does what is required and that it performs well. You can consider a number of approaches when tuning and testing code. These approaches include: Debugging—run the code and add break points to stop and inspect areas of concern. SQL performance—use Explain Plan results to review the performance. PL/SQL performance—use the PL/SQL Hierarchical Profiler to identify bottlenecks. Unit testing—review edge cases and general function testing. Does the code do what you intended it to do? In this article by Sue Harper, author of Oracle SQL Developer 2.1, we'll review the debugger. We will see how to debug PL/SQL packages, procedures, and functions. Debugging PL/SQL code SQL and PL/SQL code may execute cleanly, and even produce an output. PL/SQL code may compile and produce results, but this is part of the task. Does it do what you are expecting it to do? Are the results accurate? Does it behave as expected for high and low values, odd dates or names? Does it behave the same way when it's called from within a program as it does when tested in isolation? Does it perform as well for massive sets of data as it does for a small test case? All of these are aspects to consider when testing code, and many can been tracked by debugging the code. Using the debugging mechanism in SQL Developer You will need a piece of compiled, working code. For this exercise, we will use the following piece of code: PROCEDURE EMP_DEPTS(P_MAXROWS VARCHAR2)ASCURSOR EMPDEPT_CURSOR ISSELECT D.DEPARTMENT_NAME, E.LAST_NAME, J.JOB_TITLEFROM DEPARTMENTS D, EMPLOYEES E, JOBS JWHERE D.DEPARTMENT_ID = E.DEPARTMENT_IDAND E.JOB_ID = J.JOB_ID;EMP_RECORD EMPDEPT_CURSOR % ROWTYPE;TYPE EMP_TAB_TYPE IS TABLE OF EMPDEPT_CURSOR % ROWTYPE INDEX BYBINARY_INTEGER;EMP_TAB EMP_TAB_TYPE;I NUMBER := 1;BEGINOPEN EMPDEPT_CURSOR;FETCH EMPDEPT_CURSORINTO EMP_RECORD;EMP_TAB(I) := EMP_RECORD;WHILE((EMPDEPT_CURSOR % FOUND) AND(I <= P_MAXROWS))LOOP I := I + 1;FETCH EMPDEPT_CURSORINTO EMP_RECORD;EMP_TAB(I) := EMP_RECORD;END LOOP;CLOSE EMPDEPT_CURSOR; FOR J IN REVERSE 1 .. ILOOP DBMS_OUTPUT.PUT_LINE('THE EMPLOYEE '|| EMP_TAB(J).LAST_NAME ||' WORKS IN DEPARTMENT '|| EMP_TAB(J).DEPARTMENT_NAME);END LOOP;END; Before you can debug code, you need to have the following privileges: EXECUTE and DEBUG—you need to be able to execute the required procedure DEBUG CONNECT SESSION—to be able to debug procedures you execute in the session Note, when granting the system privilege DEBUG ANY PROCEDURE, you are granting access to debug any procedure that you have execute privilege for and has been compiled for debug. Using the Oracle debugging packages Oracle provides two packages for debugging PL/SQL code. The first, DBMS_DEBUG, was introduced in Oracle 8i and is not used by newer IDEs. The second, DBMS_DEBUG_JWP, was introduced in Oracle 9i Release 2, and is used in SQL Developer when debugging sub-programs. Debugging When preparing to debug any code, you need to set at least one break point, and then you should select Compile for Debug. In the following screenshot, the breakpoint is set at the opening of the cursor, and the Compile for Debug option is shown in the drop-down list: Instead of using the drop-down list to select the Compile or Compile for Debug options, just click the Compile button. This compiles the PL/SQL code using the optimization level set in the Preferences. Select Database PL/SQL Compiler|. By setting the Optimization Level preference to 0 or 1 the PL/SQL is compiled with debugging information. Any PL/SQL code that has been compiled for debugging will show the little green bug overlaying the regular icon in the Connections navigator. The next screenshot shows the EMP_DEPTS procedure and the GET_SALARY function have both been compiled for debug: Compile for debug Once you have completed a debugging session, be sure to compile again afterwards to remove any debug compiler directives. While negligible, omitting this step can have a performance impact on the PL/SQL program. You are now ready to debug. To debug, click the Debug button in the toolbar. SQL Developer then sets the sessions to a debug session and issues the command DBMS_DEBUG_JDWP.CONNECT_TCP (hostname, port); and sets up the debug windows as shown in the following screenshot: This connects you to a debugger session in the database. In some instances, the port selected is not open, due to firewall or other restrictions. In this case, you can have SQL Developer prompt you for the port. To set this option, open the Preferences dialog, and select the Debugger node. You can also specify the port range available for SQL Developer to use. These options mean that you can have more control over the ports used. Navigating through the code The PL/SQL debugger provides a selection of buttons (or menu items) to step through individual lines of code, or to step over blocks of code. You can step through or over procedures, navigating to the point of contention or the area you wish to inspect. Once you start stepping into the code, you can track the data as it changes. The data is displayed in a second set of tabbed dialogs. In this example, we are looping through a set of records in order for you to see how each of the windows behaves. As you start stepping into the code, the Data tab starts to display the values: This Data tab continues to collect all of the variables as you continue to step through the code. Even if you step over and skip blocks of code, all of the code is executed and the results are gathered here. The Smart Data tab keeps track of the same detail, but only the values immediately related to the area you are working in. This is more useful in a large procedure than in a small one like the example shown. The context menu provides you with a set of options while debugging. These include: Run to Cursor—allows you to start debugging and then to quickly move to another part of the code. The code in-between is quickly executed and you can continue debugging. Watch—allows you to watch an expression or code while you are debugging. Inspect—allows you to watch values as you debug. In the following screenshot, the current execution point is at the start of the WHILE loop. If the loop is required to loop multiple times, you can skip that and have the code execute to a point further down in the code, in this case after the cursor has been completed and closed: The Watch and Inspect options remain set up if you stop and restart the debug session. This allows you to stop, change the input values, and start debugging and these will change according to the new parameters. You do not need to set up watch or inspector values each time you debug the procedure. The values appear in dockable windows, so you can dock or float them near the code as required: You can modify values that you are watching. In the following example, 'i' is the counter that we're using in the loop. You can modify this value to skip over chunks of the loop, and then continue from a particular point. Modifying values in the middle of the code can be useful, as you might want to test how the program reacts in certain circumstances. For example, before the millennium, testers may have wanted to see how code behaved, or output changed once the date switched over to the year 2000.
Read more
  • 0
  • 1
  • 17274

article-image-creating-simple-skin-using-dotnetnuke
Packt
08 Sep 2010
5 min read
Save for later

Creating a Simple Skin using DotNetNuke

Packt
08 Sep 2010
5 min read
Introduction In DNN, skinning is a term that refers to the process of customizing the look and feel of the DNN portal. One of the powerful features of DNN is that the functionality of the portal is separated from the presentation of the portal. This means we can change the appearance of the portal without affecting how the portal works. To create a skin in DNN we will work with three kinds of files: HTML, ASCX, and CSS. The HTML or ASCX file describes the layout of the page and the CSS file provides the styling. If you have worked with HTML and CSS before than you will be able to immediately get started. However, if you are familiar with ASCX (and as a DNN developer that is likely) you can achieve the same results faster than HTML. In the recipes, we will show primarily ASCX skinning with some brief examples of HTML skinning. Skin Objects Before we start looking at the recipes, we need a quick word about Skin Objects. Skin Objects are used in both HTML and ASCX skin files as placeholders for different kinds of dynamic functionality. In HTML skins, you place text tokens such as [CURRENTDATE] in your code and when the code is parsed by the skin engine it will insert the matching skin object. If you are working in ASCX, you register skin objects as controls that you place directly in your code. DNN offers many different skin objects such as CurrentDate, Logo, Login link, and others and we'll see many of these in action in the recipes of this article. Downloading and installing a skin Often the easiest way to start skinning is to download an existing skin package and see the different files used for skinning. In this recipe we will download an excellent skin created by Jon Henning from a site called CodePlex that demonstrates the most common skin objects and layouts. Another reason for starting with an existing skin is that it allows incremental development. We can start with a fully functional skin, deploy it to our DNN portal and then edit the source files right on the server. In this way the changes we make are immediately displayed and problems are easily spotted and fixed. However, as applying a skin can affect the entire site, it is best to create and test skins on a development DNN site before using them on a production site. Finally, it should also be noted that as a skin is really just another type of extension in DNN, you are already familiar with some of these steps. How to do it... Open your favorite web browser and go to the site http://codeendeavortemplate.codeplex.com/. Click on Downloads in the toolbar. Scroll down a little and click on the CodeEndeavors Template Skin link. When prompted with the License Agreement, click I Agree. The File download dialog will ask if you want to Open or Save. Click on Save and select a temporary folder to hold the ZIP file. That's all we need from the CodePlex site, so close the browser. To install the skin on the DNN site, begin by logging in as the SuperUser. Look at the Control Panel and make sure you're in Edit mode. Look under the Host menu and select Extensions. Scroll to the bottom and click on the link Install Extension Wizard. The wizard will prompt for the ZIP file (called the extension package). Click on the Browse button and select the file you just downloaded (for example CodeEndeavors.TemplateSkin.install.v01.01.07.00.zip). Click on Open then click on Next. The wizard will display the Extension information. Click on Next. The wizard will display the Release Notes. Click on Next. On the license page, check Accept License? and click on Next. Now the install script will run, creating the skin. At the end you should see the message "Installation successful". Click on Return. To make the skin active, select Skins under the Admin menu. (Move the mouse over the image to enlarge.) From the Skins drop-down lists, select CodeEndeavors.TemplateSkin. For this article, we will use the Index skin for our examples. Click on the Apply link under the index skin to make it active. To see the skin files, you can look in the root folder of the DNN instance under Portals_defaultSkinsCodeEndeavors.TemplateSkin. Here is a summary of the key files you are likely to see in a skin like this: File name Description animated.ascx An ASCX skin file. container.ascx An ASCX container file. index.html An HTML skin file. skin.css The stylesheet for the skin. container.css The stylesheet for the container. TemplateSkin.dnn The manifest file for the skin package. thumbnail_animated.jpg A preview image of the ASCX skin. thumbnail_container.jpg A preview image of the ASCX container. thumbnail_index.jpg A preview image of the HTML skin. license.txt The text of the license agreement. releasenotes.txt The text of the release notes. version.txt The version number. Images folder A folder holding the graphic images supporting a skin or container.
Read more
  • 0
  • 0
  • 3143

article-image-bpel4people
Packt
08 Sep 2010
10 min read
Save for later

BPEL4People

Packt
08 Sep 2010
10 min read
Introduction BPEL4People has been developed to provide extensive support for human interactions in BPEL processes and to standardize the human interactions. Originally, the BPEL4People specification was defined by IBM and SAP. Other companies, such as Oracle, Active Endpoints, and Adobe have also joined. Today, this specification has being advanced within the OASIS BPEL4People Technical Committee. The BPEL4People specification contains two parts: BPEL4People version 1.0, which introduces BPEL extensions to address human interactions in BPEL Web Services Human Task (WS-HumanTask) version 1.0 introduces the definition of human tasks BPEL4People is defined in a way that it is layered on top of the BPEL language. We will now have a brief look at the WS-HumanTask and then at the BPEL4People. Brief look at WS-HumanTask The WS-HumanTask specification introduces Human Tasks to BPEL. Human Tasks are services, implemented by humans. A Human Task has two interfaces. One interface exposes the service offered by the task, like a translation service or an approval service. The second interface allows people to deal with tasks, for example to query for human tasks waiting for them, and to work on these tasks. This is very similar to human tasks in WebSphere, with two main differences—WS-HumanTask standardizes tasks among different vendors, and WS-HumanTask introduces new activities for specifying the properties of human tasks. In WebSphere, we had to use the Integration Developer GUI instead. WS-HumanTask makes a distinction between Human Tasks and notifications. Notifications are a special type of Human Task that allows the sending of information about noteworthy business events to users. Notifications are always delivered one-way. There is no response from notifications expected. Overall structure The overall structure of the human interactions definition is as follows: <?xml version="1.0" encoding="UTF-8"?><htd:humanInteractions targetNamespace="anyURI" expressionLanguage="anyURI"? queryLanguage="anyURI"?> <htd:extensions>? <htd:extension namespace="anyURI" mustUnderstand="yes|no"/>+ </htd:extensions> <htd:import namespace="anyURI"? location="anyURI"? importType="anyURI" />* <htd:logicalPeopleGroups>? <htd:logicalPeopleGroup name="NCName" reference="QName"?>+ <htd:parameter name="NCName" type="QName" />* </htd:logicalPeopleGroup></htd:logicalPeopleGroups><htd:tasks>? <htd:task name="NCName">+ ... </htd:task> </htd:tasks><htd:notifications>? <htd:notification name="NCName">+ ... </htd:notification> </htd:notifications></htd:humanInteractions> Human Tasks The most important is the definition of the Human Task. The definition includes the following: Interface Priority People assignments Delegation Presentation elements Outcome Search priorities Renderings Deadlines (start and competition deadlines) The WS-HumanTask specification foresees the following syntax to define a human task: <htd:task name="NCName"> <htd:interface portType="QName" operation="NCName" responsePortType="QName"? responseOperation="NCName"?/> <htd:priority expressionLanguage="anyURI"?>? integer-expression </htd:priority> <htd:peopleAssignments> ... </htd:peopleAssignments> <htd:delegation potentialDelegatees= "anybody|nobody|potentialOwners|other"/>? <htd:from>? ... </htd:from> </htd:delegation> <htd:presentationElements> ... </htd:presentationElements> <htd:outcome part="NCName" queryLanguage="anyURI">? queryContent </htd:outcome> <htd:searchBy expressionLanguage="anyURI"?>? expression </htd:searchBy> <htd:renderings>? <htd:rendering type="QName">+ ... </htd:rendering> </htd:renderings> <htd:deadlines>? <htd:startDeadline>* ... </htd:startDeadline> <htd:completionDeadline>* ... </htd:completionDeadline> </htd:deadlines> </htd:task> Escalations Within the deadlines, escalations can be defined. An example of defining an escalation is shown as follows: <htd:escalation name="highPrio"> <htd:condition> <![CDATA[ (htd:getInput("OrderRequest")/amount < 1000 && htd:getInput("OrderRequest")/prio <= 10) ]]> </htd:condition> <htd:notification name="ClaimApprovalOverdue"> <htd:interface portType="tns:ClaimsHandlingPT" operation="escalate" /> <htd:peopleAssignments> <htd:recipients> <htd:from logicalPeopleGroup="Manager"> <htd:argument name="region"> htd:getInput("OrderRequest")/region </htd:argument> </htd:from> </htd:recipients> </htd:peopleAssignments> <htd:presentationElements> <htd:name> Order approval overdue. </htd:name> </htd:presentationElements> </htd:notification> </htd:escalation> In a similar way, a reassignment could be done. Notifications Notifications are defined with the following: Interface Priority People assignments Presentation elements Renderings An example is shown as follows: <htd:notification name="NCName"> <htd:interface portType="QName" operation="NCName"/> <htd:priority expressionLanguage="anyURI"?>? integer-expression </htd:priority> <htd:peopleAssignments> <htd:recipients> ... </htd:recipients> <htd:businessAdministrators>? ... </htd:businessAdministrators> </htd:peopleAssignments> <htd:presentationElements> ... </htd:presentationElements> <htd:renderings>? ... </htd:renderings> </htd:notification> Programming interface The WS-HumanTask specification also defines the API for applications that are involved with the life cycle of a human task or a notification. It provides several types of operations, including: Participant operations, such as operation for claiming tasks, starting, stopping, suspending tasks, completing tasks, setting priority, delegating, and so on Simple query operations, such as getMyTasks and getMyTaskAbstracts Advanced query operation, which provides several possibilities for retrieving the tasks Administrative operations for nominating and activating tasks, and setting generic human roles The specification also defined XPath extension functions to retrieve Human Task properties. Now that we are familiar with WS-HumanTask, let us have a brief look at the BPEL4People specification. Brief look at BPEL4People BPEL4People is a BPEL extension, which adds several activities to the BPEL language. The most important extensions introduced in BPEL4People are people activities and people links. People activities are used to define human interactions. For each people activity, the BPEL server must create work items and distribute them to users eligible to execute them. People activities can have input and output variables and can specify deadlines. This is very similar to what we have seen in the WebSphere support for human tasks. To specify the implementation of people activities, BPEL4People introduces tasks. Tasks specify actions that users must perform. Tasks can have descriptions, priorities, deadlines, and other properties. Tasks can be represented in-line, or using WS-HumanTask. To associate people activities and the related tasks with users or groups of users, BPEL4People introduces people links. They associate users with one or more people activities. People links are usually associated with generic human roles, such as process initiator, process stakeholders, owners, and administrators. Overall structure The overall structure of BPEL4People extensions is as follows: <bpel:process ... > ... <bpel:extensions> <bpel:extension namespace="http://www.example.org/BPEL4People" mustUnderstand="yes"/> <bpel:extension namespace="http://www.example.org/WS-HT" mustUnderstand="yes"/> </bpel:extensions> <bpel:import importType="http://www.example.org/WS-HT" …/> <b4p:humanInteractions>? <htd:logicalPeopleGroups>? <htd:logicalPeopleGroup name="NCName">+ ... </htd:logicalPeopleGroup> </htd:logicalPeopleGroups> <htd:tasks>? <htd:task name="NCName">+ ... </htd:task> </htd:tasks> <htd:notifications>? <htd:notification name="NCName">+ ... </htd:notification> </htd:notifications> </b4p:humanInteractions> <b4p:peopleAssignments> ...</b4p:peopleAssignments> ...<bpel:extensionActivity> <b4p:peopleActivity name="NCName" ...> ... </b4p:peopleActivity> </bpel:extensionActivity> ...</bpel:process> Let us have a look at the new elements: The element <b4p:humanInteractions><b4p:/humanInteractions> contains declarations of elements from the WS-HumanTask namespace, such as <htd_logicalpeoplegroups></htd_logicalpeoplegroups>, <htd_tasks></htd_tasks>, and <htd_notifications></htd_notifications>. The element <htd_logicalpeoplegroup></htd_logicalpeoplegroup> specifies a logical people group used in an inline human task or a people activity. The <htd_task></htd_task> element is used to provide the definition of an inline human task. The <htd_notification></htd_notification> element is used to provide the definition of an inline notification. The element <b4p_peopleassignments></b4p_peopleassignments> is used to assign people to process-related generic human roles. The new activity <b4p_peopleactivity></b4p_peopleactivity> is used to model human interactions within BPEL processes. People assignments BPEL4People defines generic human roles. These roles define what a person or a group of people can do with the process instance. The specification defines three process-related generic human roles: Process initiator: Person that triggered the process instance Process stakeholders: People who can influence the progress of a process instance, for example, by adding ad-hoc attachments, forwarding a task, or simply observing the progress of the process instance Business administrators: People allowed to perform administrative actions on the business process instances The syntax of people assignments looks like this: <b4p:peopleAssignments> <b4p:processInitiator>? <htd:from ...> ... </htd:from> </b4p:processInitiator> <b4p:processStakeholders>? <htd:from ...> ... </htd:from> </b4p:processStakeholders> <b4p:businessAdministrators>? <htd:from ...> ... </htd:from> </b4p:businessAdministrators> </b4p:peopleAssignments> People activities People activities are used to integrate human interactions within BPEL processes. A people activity can be integrated with an inline Human Task or with a standalone Human Task. An inline task can be defined as part of a people activity. A standalone Human Task is defined separately and can be used several times. To specify Human Tasks, we use the WS-HumanTask specification. The overall syntax is as follows: <b4p:peopleActivity name="NCName" inputVariable="NCName"? outputVariable="NCName"? isSkipable="xsd:boolean"? standard-attributes> standard-elements ( <htd:task>...</htd:task> | <b4p:localTask>...</b4p:localTask> | <b4p:remoteTask>...</b4p:remoteTask> | <htd:notification>...</htd:notification> | <b4p:localNotification>...</b4p:localNotification> | <b4p:remoteNotification>...</b4p:remoteNotification> ) <b4p:scheduledActions>? ... </b4p:scheduledActions> <bpel:toParts>? <bpel:toPart part="NCName" fromVariable="BPELVariableName"/>+ </bpel:toParts> <bpel:fromPart part="NCName" toVariable="BPELVariableName"/>+ <bpel:fromPart>? </bpel:fromParts> <b4p:attachmentPropagation fromProcess="all|none" toProcess="all|newOnly|none"/>? </b4p:peopleActivity> Summary In this article, we have covered the BPEL4People and WS-HumanTask specifications. These specifications have standardized the approach to BPEL human interactions and made it portable through different SOA process servers. We have overviewed both specifications and have seen that there are no major conceptual differences between the current Oracle support and the approach taken in specifications. At the time of writing, it has not been clear how much support these specifications will get from SOA platform vendors. Further resources on this subject: Human Interactions in BPEL [Article] Business Processes with BPEL [Article] Web Services, SOA, and WS-BPEL Technologies [Article] SOA—Service Oriented Architecture [Article] Oracle Web Services Manager: Authentication and Authorization [Article]
Read more
  • 0
  • 0
  • 1942

article-image-creating-and-styling-container-images-using-dotnetnuke
Packt
08 Sep 2010
4 min read
Save for later

Creating and Styling Container Images using DotNetNuke

Packt
08 Sep 2010
4 min read
Creating custom container images Containers can do more than just act as placeholders for module content. By expanding the container skin to include images we can create more elaborate containers limited only by our creativity. Styling a DNN container using images is a two-part process. First we must create the images we need and save them as a set of graphic files. Then we must design the container to position the images around the container. In this recipe we will see how to create the custom images for styling a container. In the next recipe Styling a container with images we'll see how to position these images to create the final container. Getting ready Here is a diagram that shows what the final container will look like. We need to create the five images highlighted in red. As you can see in this diagram, we need only create images for the distinctive elements. CSS can fill in the gaps by repeating the background image as necessary to fit the size of the container. Creating images for a container is a four-step process: Decide which sides of the container will use images. In this example we will create images for the top and bottom container edges and leave the left and right sides as a simple border line. Decide the size of the images. A container border can be very narrow or very wide. In this example we will create a 27 pixel wide border with the images. Decide which images will tile to support variable-sized containers. As containers can stretch or shrink, we need to use an image that can tile. That is, the image can be repeated as many times as necessary to fill the width of the container. In this example, we will pick one image to tile across the top and another to tile along the bottom. As the sides will be just a border line we won't need to worry about tiling vertically. Choose a drawing application (such as MS Paint) to create the images and save them to a temporary folder. You can use a variety of formats—in this example we will save the images as GIF files to save space. How to do it... Start by launching your favorite drawing application (such as MS Paint). Set the image dimensions to 98 pixels wide and 27 pixels high. There's nothing special about these dimensions, they just look good for this particular design. Draw the image for the upper-left corner of the container. This image will be the background for the title of the module, so it needs to be an image that can tile horizontally as the length of a container title varies. Save the image as a GIF file called ContainerUL.gif. Next, create a new image with dimensions 80 by 27 pixels. This image marks the end of the module title and the beginning of the top container border. It does not tile and is used in this example to connect the upper-left corner to the border image that runs across the top of the container. Save the image as a GIF file called ContainerUC1.gif. The next image is the top border of the container and will tile horizontally as the width of the container changes. Set the attributes to be 12 by 27 pixels. Save the image as a GIF file called ContainerUC2.gif. For the border along the bottom of the container, we need two more images. The first is the lower-left corner. Make this image 90 by 27. This image will tile across the bottom for the width of the container. Save the image as a GIF file called ContainerLL.gif. The last image is for the lower-right corner. This image doesn't tile. Make it 100 by 27 pixels. 12. Save the image as a GIF file called ContainerLR.gif. At this point you should have five images in a temporary folder that look something like this: (Move the mouse over the image to enlarge.) There's more... In this recipe we saw how to create images for a container and a little bit on how the images can form the borders of a container. In the next recipe we will see how to create the container that will use these images.
Read more
  • 0
  • 0
  • 2918

article-image-deploying-and-exploring-skin-objects-using-dotnetnuke
Packt
08 Sep 2010
7 min read
Save for later

Deploying and Exploring Skin Objects using DotNetNuke

Packt
08 Sep 2010
7 min read
Deploying your skins and containers Like a module, skins must be packaged and deployed to your DNN server. Once deployed, they will appear on the Skins page under the Admin menu for preview and applying to the portal. In previous article, we saw how to create new skins directly on a test DNN portal. This allows you to edit the files and immediately see the results just by refreshing your browser. But once a skin has been fully developed, this recipe will show you how to package your skin into a portable ZIP file using the package wizard. Once created, you can install the skin package on the DNN site by logging in as SuperUser then uploading the ZIP file and installing the skin package as an extension. The skin is installed in the Portals_defaultSkins folder. As of DNN 5.x, only the SuperUser can deploy skins. This was done as a security precaution as skins can now contain user-written code. Getting ready For this recipe you will need skin files to deploy. We will use the skin files from the previous article, from the recipe: Creating a simple ASCX skin. How to do it... Begin by logging in as the SuperUser. Look under the Host menu and select Extensions. The first step is to look under the installed extensions and find the SampleASCXSkin under the Skins section. If you see it, skip to step 8, otherwise you must create the extension first. Start by selecting Create Extension from the action menu (or click on the link at the bottom of the page). Choose Skin from Extension Type drop down and fill out the form as shown: (Move the mouse over the image to enlarge.) Click on Next. Provide the owner information and click on Next. The SampleASCXSkin skin will now appear in the list of extensions. Click on the Edit icon next to the name. Scroll to the bottom and click on the link Create Package. The Create Package screen will display. Click on Next. On the next screen you will see the files of the skin. Click on Next. Next, the manifest is displayed. Click on Next. The wizard will now generate a file name for the ZIP file using the owner name and the skin name. Click on Next. The wizard will now complete with a summary page. You will now find a ZIP file in the /Install/Skin folder. Click on Return to close the wizard. Once the file is packaged in a ZIP file you can upload and install it. Exploring Skin Objects In this recipe we will expand the sample ASCX skin and demonstrate additional Skin Objects you can use. Getting ready To follow along with this recipe you must have completed the following recipe: Creating a simple ASCX skin Here is a diagram of the new page layout we will create: How to do it... Start by locating the default skin folder within your test DNN portal (Portals_defaultSkins). Next, create a new folder to hold the skin files. Call this folder DetailedASCXSkin. Launch the Development Tool. Open the SampleASCXSkin.ascx file from the Creating a simple ASCX skin recipe. Add the following register directive after the existing directives: <%@ Register TagPrefix="dnn" TagName="BREADCRUMB" Src="~/Admin/Skins/BreadCrumb.ascx" %> <%@ Register TagPrefix="dnn" TagName="TEXT" Src="~/Admin/Skins/Text.ascx" %> <%@ Register TagPrefix="dnn" TagName="LINKS" Src="~/Admin/Skins/Links.ascx" %> <%@ Register TagPrefix="dnn" TagName="COPYRIGHT" Src="~/Admin/Skins/Copyright.ascx" %> <%@ Register TagPrefix="dnn" TagName="CURRENTDATE" Src="~/Admin/Skins/CurrentDate.ascx" %> Next, replace the existing Login_Style DIV with a new DIV encompassing both Login and User links: <%--By placing the SkinObjects inside a parent DIV they share the same style--%> <div class="bread_bg"> <div id="bread_style"> <dnn:TEXT runat="server" id="dnnTEXT" CssClass="breadcrumb_text" Text="You are here" ResourceKey="Breadcrumb" /> &nbsp;<span> <dnn:BREADCRUMB runat="server" id="dnnBREADCRUMB" CssClass="Breadcrumb" RootLevel="0" Separator="&nbsp;>&nbsp;" /> </span> </div> <div id="login_style" class="user"> <dnn:USER runat="server" id="dnnUSER" CssClass="user" /> &nbsp;&nbsp;|&nbsp;&nbsp; <dnn:LOGIN runat="server" id="dnnLOGIN" CssClass="user" /> </div> </div> Lastly, leave the ContentPane DIV as it is and add these new DIVs right below it: <div class="bot_bg links"> <dnn:LINKS runat="server" id="dnnLINKS" CssClass="links" Level="Root" Separator="&nbsp;&nbsp;&nbsp;| &nbsp;&nbsp;&nbsp;" /> </div> <div class="bot_pad"> <div id="copy_style" class="footer"> <dnn:COPYRIGHT runat="server" id="dnnCOPYRIGHT" Align CssClass="footer" /> </div> <div id="date_style" class="footer"> <dnn:CURRENTDATE runat="server" id="dnnCURRENTDATE" Align CssClass="footer" /> </div> </div> Separating the Copyright and Date into separate DIVs allow us to put one in the lower-left corner and one in the lower-right corner of the page. Select Save SampleASCXSkin.ascx As… from the File menu. Browse to the DetailedASCXSkin folder and save the file as DetailedASCXSkin.ascx. These new Skin Objects will need styles. Open the skin.css file from the Creating a simple ASCX skin recipe. Add the following styles to the bottom of the skin.css file: /*breadcrumbs*/ .Breadcrumb, a.Breadcrumb:link, a.Breadcrumb:active, a.Breadcrumb:visited {color: #800000; font-size: 13px;} a.Breadcrumb:hover {color: #C00;} .bread_bg {background-color: #EAEAEA; fontfamily: Verdana, Arial, Helvetica, sans-serif; border-left: solid 1px #EAEAEA; border-right: solid 1px #EAEAEA; margin: 0 5px 0 5px; height: 30px;} #bread_style {float: left; padding: 10px 0px 0px 17px; color: #000000; font-size: 13px;} /* for the bottom of the page */ .bot_bg {background-color: #EAEAEA; border-left: solid 1px #EAEAEA;border-right: solid 1px #EAEAEA; border-bottom: solid 1px #EAEAEA; padding: 10px; margin: 0 5px 0 5px;} .bot_pad {font-family: Verdana, Arial, Helvetica, sans-serif; margin-bottom: 20px; padding: 0 30px 0 20px;} #terms_style {float: left;} #copy_style {float: left;} #date_style {float: right; padding: 0px 0px 0px 10px; } /*links*/ .links {text-align: center;} .links, a.links:link, a.links:active, a.links:visited {fontweight: bold; color: #800000; font-size: 11px;} a.links:hover {color: #C00;} /* Sets the font size and text color of the footer at the bottom of the page */ .footer, a.footer:link, a.footer:active, a.footer:visited {color: #800000; font-size: 12px;} a.footer:hover {color: #C00;} With these styles we are positioning the Breadcrumb links just below the menu on the left. The Login and User links remain on the right side, ContentPane and RightPane stay the same, and the new Links, Copyright, and CurrentDate controls are arranged along the bottom of the page. Select Save skin.css As… from the File menu. Browse to the DetailedASCXSkin folder and save the file as skin.css. To see how the new skin will look on the site, log in as the SuperUser. Look under the Admin menu and select Skins. The new DetailedASCXSkin will now appear: Click on Preview to see how the skin looks: See how the breadcrumbs (You are here:) appear on the left under the menu, user and login are on the right, and links along the bottom and the copyright message and current date appear in the bottom corners. There's more... If you're curious to see which Skin Objects are available on your DNN site, you can log in as the SuperUser and select Extensions under the Host menu. This will list all the skin objects and their version number. Some skin objects may not be installed by default and will not appear in this list until installed.
Read more
  • 0
  • 0
  • 1746

article-image-human-interactions-bpel
Packt
08 Sep 2010
13 min read
Save for later

Human Interactions in BPEL

Packt
08 Sep 2010
13 min read
(For more resources on BPEL, SOA and Oracle see here.) Human interactions in business processes The main objective of BPEL has been to standardize the process automation. BPEL business processes make use of services and externalize their functionality as services. BPEL processes are defined as a collection of activities through which services are invoked. BPEL does not make a distinction between services provided by applications and other interactions, such as human interactions, which are particularly important. Real-world business processes namely often integrate not only systems and services, but also humans. Human interactions in business processes can be very simple, such as approval of certain tasks or decisions, or complex, such as delegation, renewal, escalation, nomination, chained execution, and so on. Human interactions are not limited to approvals and can include data entries, process monitoring and management, process initiation, exception handling, and so on. Task approval is the simplest and probably the most common human interaction. In a business process for opening a new account, a human interaction might be required to decide whether the user is allowed to open the account. In a travel approval process, a human might approve the decision from which airline to buy the ticket (as shown in the following figure). If the situation is more complex, a business process might require several users to make approvals, either in sequence or in parallel. In sequential scenarios, the next user often wants to see the decision made by the previous user. Sometimes, particularly in parallel human interactions, users are not allowed to see the decisions taken by other users. This improves the decision potential. Sometimes one user does not even know which other users are involved, or whether any other users are involved at all. A common scenario for involving more than one user is workflow with escalation. Escalation is typically used in situations where an activity does not fulfill a time constraint. In such a case, a notification is sent to one or more users. Escalations can be chained, going first to the first-line employees and advancing to senior staff if the activity is not fulfilled. Sometimes it is difficult or impossible to define in advance which user should perform an interaction. In this case, a supervisor might manually nominate the task to other employees; the nomination can also be made by a group of users or by a decision-support system. In other scenarios, a business process may require a single user to perform several steps that can be defined in advance or during the execution of the process instance. Even more complex processes might require that one workflow is continued with another workflow. Human interactions are not limited to only approvals; they may also include data entries or process management issues, such as process initiation, suspension, and exception management. This is particularly true for long-running business processes, where, for example, user exception handling can prevent costly process termination and related compensation for those activities that have already been successfully completed. As a best practice for human workflows, it is usually not wise to associate human interactions directly to specific users; it is better to connect tasks to roles and then associate those roles with individual users. This gives business processes greater flexibility, allowing any user with a certain role to interact with the process and enabling changes to users and roles to be made dynamically. To achieve this, the process has to gain access to users and roles, stored in the enterprise directory, such as LDAP (Lightweight Directory Access Protocol). Workflow theory has defined several workflow patterns, which specify the abovedescribed scenarios in detail. Examples of workflow patterns include sequential workflow, parallel workflow, workflow with escalation, workflow with nomination, ad-hoc workflow, workflow continuation, and so on. Human Tasks in BPEL So far we have seen that human interaction in business processes can get quite complex. Although BPEL specification does not specifically cover human interactions, BPEL is appropriate for human workflows. BPEL business processes are defined as collections of activities that invoke services. BPEL does not make a distinction between services provided by applications and other interactions, such as human interactions. There are mainly two approaches to support human interactions in BPEL. The first approach is to use a human workflow service. Several vendors today have created workflow services that leverage the rich BPEL support for asynchronous services. In this fashion, people and manual tasks become just another asynchronous service from the perspective of the orchestrating process and the BPEL processes stay 100% standard. The other approach has been to standardize the human interactions and go beyond the service invocations. This approach resulted in the workflow specifications emerging around BPEL with the objective to standardize the explicit inclusion of human tasks in BPEL processes. The BPEL4People specification has emerged, which was originally put forth by IBM and SAP in July 2005. Other companies, such as Oracle, Active Endpoints, and Adobe joined later. Finally, this specification is now being advanced within the OASIS BPEL4People Technical Committee. The BPEL4People specification contains two parts: BPEL4People version 1.0, which introduces BPEL extensions to address human interactions in BPEL as a first-class citizen. It defines a new type of basic activity, which uses human tasks as an implementation, and allows specifying tasks local to a process or use tasks defined outside of the process definition. BPEL4People is based on the WS-HumanTask specification that it uses for the actual specification of human tasks. Web Services Human Task (WS-HumanTask) version 1.0 introduces the definition of human tasks, including their properties, behavior, and a set of operations used to manipulate human tasks. It also introduces a coordination protocol in order to control autonomy and lifecycle of service-enabled human tasks in an interoperable manner. The most important extensions introduced in BPEL4People are people activities and people links. People activity is a new BPEL activity used to define user interactions; in other words, tasks that a user has to perform. For each people activity, the BPEL server must create work items and distribute them to users eligible to execute them. People activities can have input and output variables and can specify deadlines. To specify the implementation of people activities, BPEL4People introduced tasks. Tasks specify actions that users must perform. Tasks can have descriptions, priorities, deadlines, and other properties. To represent tasks to users, we need a client application that provides a user interface and interacts with tasks. It can query available tasks, claim and revoke them, and complete or fail them. To associate people activities and the related tasks with users or groups of users, BPEL4People introduced people links. People links are somewhat similar to partner links; they associate users with one or more people activities. People links are usually associated with generic human roles, such as process initiator, process stakeholders, owners, and administrators. The actual users that are associated with people activities can be determined at design time, deployment time, or runtime. BPEL4People anticipates the use of directories such as LDAP to select users. However, it doesn't define the query language used to select users. Rather, it foresees the use of LDAP filters, SQL, XQuery, or other methods. BPEL4People proposes complex extensions to the BPEL specification. However, so far it is still quite high level and doesn't yet specify the exact syntax of the new activities mentioned above. Until the specification becomes more concrete, we don't expect vendors to implement the proposed extensions. But while BPEL4People is early in the standardization process, it shows a great deal of promise. The BPEL4People proposal raises an important question: Is it necessary to introduce such complex extensions to BPEL to cover user interactions? Some vendor solutions model user interactions as just another web service, with well-defined interfaces for both BPEL processes and client applications. This approach does not require any changes to BPEL. To become portable, it would only need an industry-wide agreement on the two interfaces. And, of course, both interfaces can be specified with WSDL, which gives developers great flexibility and lets them use practically any environment, language, or platform that supports Web Services. Clearly, a single standard approach has not yet been adopted for extending BPEL to include Human Tasks and workflow services. However, this does not mean that developers cannot use BPEL to develop business processes with user interactions. Human Task integration with BPEL To interleave user interactions with service invocations in BPEL processes we can use a workflow service, which interacts with BPEL using standard WSDL interfaces. This way, the BPEL process can assign user tasks and wait for responses by invoking the workflow service using the same syntax as for any other service. The BPEL process can also perform more complex operations such as updating, completing, renewing, routing, and escalating tasks. After the BPEL process has assigned tasks to users, users can act on the tasks by using the appropriate applications. The applications communicate with the workflow service by using WSDL interfaces or another API (such as Java) to acquire the list of tasks for selected users, render appropriate user interfaces, and return results to the workflow service, which forwards them to the BPEL process. User applications can also perform other tasks such as reassign, escalate, route, suspend, resume, and withdraw. Finally, the workflow service may allow other communication channels, such as e-mail and SMS, as shown in the following figure: Oracle Human Workflow concepts Oracle SOA Suite 11g provides the Human Workflow component, which enables including human interaction in BPEL processes in a relatively easy way. The Human Workflow component consists of different services that handle various aspects of human interaction with business process and expose their interfaces through WSDL; therefore, BPEL processes invoke them just like any other service. The following figure shows the overall architecture of the Oracle Workflow services: As we can see in the previous figure, the Workflow consists of the following services: Task Service exposes operations for task state management, such as operations to update a task, complete a task, escalate a task, reassign a task, and so on. When we add a human task to the BPEL process, the corresponding partner link for the Task Service is automatically created. Task Assignment Service provides functionality to route, escalate, reassign tasks, and more. Task Query Service enables retrieving the task list for a user based on a search criterion. Task Metadata Service enables retrieving the task metadata. Identity Service provides authentication and authorization of users and lookup of user properties and privileges. Notification Service enables sending of notifications to users using various channels (e-mail, voice message, IM, SMS, and so on). User Metadata Service manages metadata, related to workflow users, such as user work queues, preferences, and so on. Runtime Configuration Service provides functionality for managing metadata used in the task service runtime environment. Evidence Store Service supports management of digitally-signed workflow tasks. BPEL processes use the Task Service to assign tasks to users. More specifically, tasks can be assigned to: Users: Users are defined in an identity store configured with the SOA infrastructure. Groups: Groups contain individual users, which can claim a task and act upon it. Application roles: Used to logically group users and other roles. These roles are application specific and are not stored in the identity store. Assigning tasks to groups or roles is more flexible, as every user in a certain group (role) can review the task to complete it. Oracle SOA Suite 11g provides three methods for assigning users, groups, and application roles to tasks: Static assignment: Static users, groups, or application roles can be assigned at design time. Dynamic assignment: We can define an XPath expression to determine the task participant at runtime. Rule-based assignment: We can create a list of participants with complex expressions. Once the user has completed the task, the BPEL process receives a callback from the Task Service with the result of the user action. The BPEL process continues to execute. The Oracle Workflow component provides several possibilities regarding how users can review the tasks that have been assigned to them, and take the corresponding actions. The most straightforward approach is to use the Oracle BPM Worklist application. This application comes with Oracle SOA Suite 11g and allows users to review the tasks, to see the task details, and to select the decision to complete the task. If the Oracle BPM Worklist application is not appropriate, we can develop our own user interface in Java (using JSP, JSF, Swing, and so on) or almost any other environment that supports Web Services (such as .NET for example). In this respect, the Workflow service is very flexible and we can use a portal, such as Oracle Portal, a web application, or almost any other application to review the tasks. The third possibility is to use e-mail for task reviews. We use e-mails over the Notification service. Workflow patterns To simplify the development of workflows, Oracle SOA Suite 11g provides a library of workflow patterns (participant types). Workflow patterns define typical scenarios of human interactions with BPEL processes. The following participant types are supported: Single approver: Used when a participant maps to a user, group, or role. Parallel: Used if multiple users have to act in parallel (for example, if multiple users have to provide their opinion or vote). The percentage of required user responses can be specified. Serial: Used if multiple users have to act in a sequence. A management chain or a list of users can be specified. FYI (For Your Information): Used if a user only needs to be notified about a task, but a user response is not required. With these, we can realize various workflow patterns, such as: Simple workflow: Used if a single user action is required, such as confirmation, decision, and so on. A timeout can also be specified. Simple workflow has two extension patterns: Escalation: Provides the ability to escalate the task to another user or role if the original user does not complete the task in the specified amount of time. Renewal: Provides the ability to extend the timeout if the user does not complete the task in the specified time. Sequential workflow: Used if multiple users have to act in a sequence. A management chain or a list of users can be specified. Sequential workflow has one extension pattern: Escalation: Same functionality as above. Parallel workflow: Used if multiple users have to act in parallel (for example, if multiple users have to provide their opinion or vote). The percentage of required user responses can be specified. This pattern has an extension pattern: Final reviewer: Is used when the final review has to act after parallel users have provided feedback. Ad-hoc (dynamic) workflow: Used to assign the task to one user, who can then route the task to other user. The task is completed when the user does not route it forward. FYI workflow: Used if a user only needs to be notified about a task, but a user response is not required. Task continuation: Used to build complex workflow patterns as a chain of simple patterns (those described above).
Read more
  • 0
  • 0
  • 3481
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-blender-3d-249-working-textures
Packt
03 Sep 2010
9 min read
Save for later

Blender 3D 2.49: Working with Textures

Packt
03 Sep 2010
9 min read
(For more resources on Blender, see here.) Before we start to dig into textures, let me say that the biggest problem of working with them is actually finding or creating a good texture. That's why it's highly recommended that you start to create your own textures library as soon as possible. Textures are mostly image files, which represent some kind of surface, such as wood or stone, based on the photography. They work like wallpaper, which we can place on a surface or object. For instance, if we place an image of wood on a plane, it will give the impression that the plane is made of wood. That's the main principle of using textures; to make an object look like something in the real world. For some projects, we may need a special kind of texture, which won't be found in a common library, so we will have to take a picture ourselves or buy an image from someone. But don't worry, because often we deal with common surfaces that have common textures as well. Procedural textures vs. Non-procedural textures Blender basically has two types of textures, which are procedural textures and bitmap textures. Each one has positive and negative points; which one is the best? It will depend on your project needs: Procedural: This kind of texture is generated by the software at rendering time, just like vector lines in software, such as Inkscape or Illustrator. This means that it won't depend of any type of image file. The best thing about this type of texture is that being resolution-independent, we can set the texture to be rendered at high resolutions with minimum loss of quality. The negative point of this kind of texture is that it's harder to get realistic textures with it. The advantage of using procedural textures is that because they are all based on algorithms, they don't depend on a fixed number of pixels. Non-Procedural: To use this kind of texture, we will need an image file, such as a JPEG, PNG, or TGA file. The good thing about these textures is that we can quickly achieve a very realistic look and feel with it. On the other hand, we must find the texture file before using it. And what's more, if you are creating a high-resolution render, the texture file size must be as well. Texture library Do you remember the way we organized materials? We can do the exact same thing with textures. Besides setting names and storing the Blender files to import and use again later, collecting bitmap textures is another important point. Even if you don't start right away, it's important to know where to look for textures. So, here is a small list of websites, which provide free texture downloads: http://www.cgtextures.com http://blender-archi.tuxfamily.org/textures Applying textures To use a texture, we must apply a material to an object, and then use the texture with this material. We always use the texture inside a material. For instance, to make a plane that simulates a marble floor, we have to use a texture, and set up how the surface will react to light to give the surface a proper look of marble. To do that, we use the Texture panel, which is located right next to the Materials button. We can use a keyboard shortcut to open this panel; just hit F6 to open it: There is a way to add a texture in the Material panel also, with a menu named Texture: To get all the options, the best way to add a texture is with the Texture panel. In this panel, we will be able to see buttons, which represent the texture channels. Each one of these channels can hold a texture. The final texture will be a mix of all the channels. If we have a texture in channel 1 and another texture in channel 2, these textures will be blended and represented on the material. Before adding a new texture, we must select a channel by clicking on one of them. Usually the first channel will be selected, but if you want to use another one, just click on the channel. When the channel is selected, just click on the Add New button to add a new texture: The texture controls are very similar to the materials controls. We can give a name to the texture at the top and erase the texture if we don't want it anymore. With the selector, we can choose a previously created texture also. Just click and select it: Now, here comes the fun part. With a texture added, we have to choose a texture type. To do that, we click on the Texture Type combo box: There are a lot of textures, but most of them are procedural textures, and we won't use them frequently. The only texture that isn't procedural is the Image type. We see an example of a procedural Wood texture in the following screenshot: We can use textures such as Clouds and Wood to create effects and give surfaces a more complex look, or even create a grass texture with dirt on it. But most of the time, the texture type which we will be using will be the Image type: Each texture has it's own set of parameters to determine how it will look on the object. If we add a Wood texture, it will show the configuration parameters to the right: (Move the mouse over the image to enlarge.) If we choose texture type as Clouds, the parameters shown on the right will be completely different. With the Image texture type, it's not different. This kind of texture has its own type of setup. Following is the control panel: To show how to set up a texture, let's use an image file that represents a wood floor and a plane. We can apply the texture to this plane and set up how it's going to look, testing all parameters: The first thing to do is to assign a material to the plane, and then add a texture to this material. We choose the Image option as texture type. Blender will show the configuration options for this kind of texture. To apply the image as a texture to the plane, just click on the Load button, located in the Image menu. When we hit this button, we will be able to select the image file: Locate the image file, and the texture will be applied. If we want to have more control on how this texture is organized and placed on the plane, we need to learn how the controls work. Every time you make any changes to the setup of a texture, these changes will be shown in the preview window. Use it to make the required changes. Here is a list of what some of the buttons can do for the texture: UseAlpha: If the texture has an alpha channel, we have to press this button for Blender to calculate the channel. An image has an alpha channel when some kind of transparency is stored in the image. For instance, a PNG file with a transparent background has an alpha channel. We can use this to create a texture with a logo, for a bottle, or to add an image of a tree or person, to a plane. Rot90: With this option, we can rotate the texture by 90 degrees. Repeat: Every texture must be distributed on the object surface; repeating the texture in lines and columns is the default way to do that. Extend: If this button is pressed, the texture will be adjusted to fit the entire object surface area. Clip: With this option, the texture will be cropped, and we will be able to show only a part of it. To adjust which parts of the texture will be displayed, use the Min/Max X/Y options. Xrepeat / Yrepeat: This option determines how many times a texture is repeated with the repeat option turned on. Normal Map: If the texture will be used to create Normal Maps, press this button. These are textures used to change the face normals of an object. Still: With this button selected, we will specify that the image used as texture is a still image. This option is marked by default. Movie: If you want to use a movie file as texture, press this button. This is very useful if we need to make something similar to a theater projection screen or a tv screen. Sequence: We can use a sequence of images as a texture too. Just press this button. It works the same way as with a movie file. There are a few more parameters, such as the Reload button. If your texture file is updated outside of Blender, you must press this button to make Blender update the texture in your project. The X button can erase this texture; use it if you need to select another image file. When we add a texture to any material, an external link is created to this file. This link can be absolute or relative. Suppose we add a texture named wood.png, which is located in the root of your primary hard disk, such as C:. A link to this texture will be created like this — c:wood.png. So every time you open this file, the software will look for that file at that exact place. This is an absolute link, but we can use a relative link as well. For instance, when we add a texture located in the same folder as our scene, a relative link will be created. Every time we use an absolute link and we have to move the .blend file to another computer, the texture file must go with it. To imbue the image file with .blend, just press the icon for gift package: To save all the textures used in a scene, just access the File menu and use the Pack Data option. It will cause all the texture files to get embedded with the source .blend file.
Read more
  • 0
  • 0
  • 3825

article-image-examples-mysql-daemon-plugin
Packt
01 Sep 2010
8 min read
Save for later

Examples of MySQL Daemon Plugin

Packt
01 Sep 2010
8 min read
(For more resources on MySQL, see here.) A Hello World! Daemon plugin Now, let's look at our first complete plugin example. This plugin is probably the most basic plugin we can have. It simply prints a message into the MySQL error log when loaded: #include <stdio.h> #include <mysql/plugin.h> #include <mysql_version.h> These are the basic includes required for most Daemon plugins. The most important being mysql/plugin.h, which contains macros and data structures necessary for a MySQL plugin. static int hello_world_plugin_init(void *p) { fprintf(stderr, "Hello World: " "This is a static text daemon example plugin!n"); return 0; } In the plugin initialization function we simply write a message to stderr. MySQL redirects stderr to the error log (if there is one) so our message will end up there. We then return 0 to indicate that the initialization was successful. struct st_mysql_daemon hello_world_info = { MYSQL_DAEMON_INTERFACE_VERSION }; This structure is used for the info part of the plugin declaration. In Daemon plugins it simply contains the API version that this plugin was compiled against. The Daemon plugin API version matches the MySQL server version, which means MySQL Daemon plugins can only be used with a MySQL server version they have been compiled against. Indeed, for a Daemon plugin to do something non-trivial it will invariably need access to the server's internal functions and data structures that change with every MySQL version. Other plugins that are implemented according to a certain functionality API are separated from the server internals and are binary compatible with a wide range of server releases. Having defined all of the functions and auxiliary structures, we can declare a plugin: mysql_declare_plugin(hello_world) { This is a Daemon plugin so we need to specify it as such with this defined constant: MYSQL_DAEMON_PLUGIN, info points to the structure declared earlier. With other plugin types this may contain additional information valuable to the plugin functionality: &hello_world_info, We are calling this plugin "hello_world". This is its name for the INSTALL PLUGIN command and any plugin status: "hello_world", The author string, is useful for providing contact information about the author of the plugin: "Andrew Hutchings (<a href="Andrew.Hutchings@Sun.COM" target="_blank">Andrew.Hutchings@Sun.COM)", A Simple line of text that gives a basic description of what our plugin does: "Daemon hello world example, outputs some static text", This plugin is licensed under GPL so we set the license type to this: PLUGIN_LICENSE_GPL, This is our initialization function that has been defined earlier in the code: hello_world_plugin_init, As our simple plugin does not need a de-initialization function, we put NULL here: NULL, This plugin is given version 1.0 because it is our first GA release of the plugin. In future versions we can increment this: 0x0100, There are no status or system variables in this example. Hence, everything below the version is set to NULL: NULL, NULL, NULL } mysql_declare_plugin_end; We can now install this plugin using the INSTALL PLUGIN syntax Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 2 Server version: 5.1.47 Source distribution Type 'help;' or 'h' for help. Type 'c' to clear the current input statement. mysql> INSTALL PLUGIN hello_world SONAME 'hello_world.so'; Query OK, 0 rows affected (0.00 sec) Going to the error log we see: 090801 22:18:00 [Note] /home/linuxjedi/Programming/Builds/mysql-5.1.47/ libexec/mysqld: ready for connections. Version: '5.1.47' socket: '/tmp/mysql.sock' port: 3306 Source distribution Hello World: This is a static text daemon example plugin! A system and status variables demo plugin Let's see a more complex example. This plugin shows how to create system and status variables. It has one global system variable and one status variable, both defined as long long. When you set the global system variable, its value is copied into the status variable. #include <stdio.h> #include <mysql/plugin.h> #include <mysql_version.h> long long system_var = 0; long long status_var = 0; struct st_mysql_show_var vars_status_var[] = { {"vars_status_var", (char *) &status_var, SHOW_LONGLONG}, {0, 0, 0} }; We have one status variable in this plugin called vars_status_var which is bound to the status_var variable defined near the top of this source code. We are defining this variable as long long so we use the SHOW_LONGLONG type. int sysvar_check(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, struct st_mysql_value *value) { This function is to be called before our system variable is updated. A plugin is not required to provide it but it can be used to check if the data entered is valid and, as an example, we will only allow values that are not too close to status_var. long long buf; value->val_int(value, &buf); First we retrieve the new value-to-be and store it in buf. *(longlong*) save = buf; We then set save to the contents of buf, so that the update function could access it and store the value in our system_var variable. If we do not implement our own sysvar_check() function for our system variable, MySQL will provide a default one that performs all of the above (but nothing of the following). if (buf * 2 < status_var || buf > status_var * 3) return 0; else return 1; } This is our special condition. In this example we allow an update only if the new value is either less than a half of or three times bigger than the value of status_var. We return 0 when the new value is valid, and an update should be allowed, and 1 when an update should be canceled. In our update function we copy the value of the system_var to a status_var, to see how its value changes in SHOW STATUS and to get a different range on valid values for the system_var on every update. Note that the update function cannot return a value. It is not supposed to fail! void sysvar_update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { system_var = *(long long *)save; status_var = system_var; } We update our system_var variable without any mutex protection, even though many threads may try to execute the SET statement at the same time. Nevertheless, it is safe. MySQL internally guards all accesses to global system variables with a mutex, which means we do not have to. MYSQL_SYSVAR_LONGLONG(vars_system, system_var, 0, "A demo system var", sysvar_check, sysvar_update, 0, 0, 123456789, 0); This is the declaration for our system variable. It is a long long and is called vars_system. In fact as this is a variable for the vars plugin, the full name will be vars_vars_system in SHOW VARIABLES. It is associated with the system_var variable in the code, has the check function sysvar_check() and an update function sysvar_update() as defined above, and it can only take values between 0 and 123456789. struct st_mysql_sys_var* vars_system_var[] = { MYSQL_SYSVAR(vars_system), NULL }; This is the structure which stores all system variables to be passed to the declaration for this plugin. As we only have one variable we shall only include that. struct st_mysql_daemon vars_plugin_info= { MYSQL_DAEMON_INTERFACE_VERSION }; mysql_declare_plugin(vars) { MYSQL_DAEMON_PLUGIN, &vars_plugin_info, "vars", "Andrew Hutchings", "A system and status variables example", PLUGIN_LICENSE_GPL, NULL, NULL, 0x0100, vars_status_var, vars_system_var, NULL } mysql_declare_plugin_end; This is very similar to the declaration of our first plugin, but this one has structures for the status variables and system variable listed. When putting our new plugin into action we should see the following: mysql> INSTALL PLUGIN vars SONAME 'vars.so'; Query OK, 0 rows affected (0.00 sec) mysql> SHOW STATUS LIKE 'vars_%'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | vars_status_var | 0 | +-----------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'vars_%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | vars_vars_system | 0 | +------------------+-------+ 1 row in set (0.00 sec) Our status and system variables are both set to 0 by default. mysql> SET GLOBAL vars_vars_system=2384; Query OK, 0 rows affected (0.00 sec) mysql> SHOW STATUS LIKE 'vars_%'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | vars_status_var | 2384 | +-----------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'vars_%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | vars_vars_system | 2384 | +------------------+-------+ 1 row in set (0.00 sec) Setting our system variable to 2384 has altered both the system variable and the status variable, so we have success! mysql> SET GLOBAL vars_vars_system=2383; ERROR 1210 (HY000): Incorrect arguments to SET Our special check function works too. The variable cannot be updated to a value that is too close to its old value!
Read more
  • 0
  • 0
  • 2925

article-image-look-inside-mysql-daemon-plugin
Packt
31 Aug 2010
11 min read
Save for later

A Look Inside a MySQL Daemon Plugin

Packt
31 Aug 2010
11 min read
A look inside a Daemon plugin Unlike UDFs, MySQL plugins store all of the metadata in the plugins shared library. So when installing a plugin you only need to specify the name of the plugin and its shared library filename. This eliminates much of the user error while installing. With UDFs it is very easy to choose the wrong return type or forget the AGGREGATE keyword, but with plugins this is not possible. Why write a Daemon plugin Just like UDFs and other MySQL plugin types the Daemon plugin can be used to add extra functionality to MySQL with the same advantages and disadvantages. Daemon plugins are ideal for writing code that needs to reside in the server but does not need to communicate with it—such as a heartbeat plugin or monitoring plugins—because the simple Daemon plugin API does not provide any means for a server and a plugin to communicate with each other. Installing and using Daemon plugins Installing plugins is relatively easy because all of the information about a plugin is stored inside it. To install a plugin we can use the INSTALL PLUGIN statement as follows: mysql> INSTALL PLUGIN my_plugin SONAME 'my_plugin.so'; Likewise, to remove a plugin we use: mysql> UNINSTALL PLUGIN my_plugin; When a plugin is installed it is initialized instantly and this means that the code we write will start automatically when our plugin is installed. Upon installing a plugin it is added to the mysql.plugin table so MySQL knows it is installed and can load it again on startup. In other words, similar to UDFs, all installed plugins are loaded automatically when a server is started. A plugin is de-initialized when either it is uninstalled or the MySQL server is being shut down. It is worth noting at this time that if the MySQL server crashes for any reason the de-initialization of the plugin will not happen. If a plugin is installed, we can prevent it from being loaded and executed at startup with the --disable-plugin-my-plugin or --plugin-my-plugin=OFF commands. If we do not do that MySQL will try to load it because the default behavior is --plugin-my-plugin=ON. If the plugin fails to load, MySQL will note that fact in the error log and will continue without this plugin. If we want to be sure that a plugin is absolutely loaded in the server, and that the server will not simply ignore a plugin failure, we can use --plugin-my-plugin=FORCE. In this mode the server will exit if our plugin fails to load. As we can see below, the mysql.plugin table simply contains the plugin name and the filename for the shared library containing the plugin: mysql> SELECT * FROM mysql.plugin;+-----------+--------------+| name | dl |+-----------+--------------+| my_plugin | my_plugin.so |+-----------+--------------+1 row in set (0.01 sec) MySQL has a SHOW command to give us information about installed plugins. This is very useful to see if a plugin is actually running. If there was a problem during initialization then the status for the plugin will be marked as DISABLED. A sample output for SHOW PLUGINS can be seen below: mysql> SHOW PLUGINSG....*************************** 11. row *************************** Name: my_plugin Status: ACTIVE Type: DAEMONLibrary: my_plugin.soLicense: GPL11 rows in set (0.00 sec) Information Schema also includes a table for use with plugins, and it contains more detail than SHOW PLUGINS. It shows version information supplied by the plugin as well as the plugin description: mysql> SELECT * FROM information_schema.plugins WHERE PLUGIN_NAME='my_plugin'G*************************** 1. row *************************** PLUGIN_NAME: my_plugin PLUGIN_VERSION: 1.0 PLUGIN_STATUS: ACTIVE PLUGIN_TYPE: DAEMON PLUGIN_TYPE_VERSION: 50147.0PLUGIN_LIBRARY: my_plugin.so PLUGIN_LIBRARY_VERSION: 1.0 PLUGIN_AUTHOR: Andrew Hutchings PLUGIN_DESCRIPTION: Daemon example, shows a declaration PLUGIN_LICENSE: GPL1 row in set (0.00 sec) Technically, loading of plugins is very similar to loading of UDFs. Problems that can arise, ways of solving them, and error messages are similar to those of UDFs. The role of a version As we have seen, there are three two-component version numbers in the INFORMATION_SCHEMA.PLUGINS table. One of them, PLUGIN_VERSION, is purely informational. It is a number that a plugin author can specify arbitrarily, and MySQL itself does not do anything with it. The other two are very important though. They are used to protect the API, to make sure that if a plugin is loaded it uses the same API version that the server provides. This is one of the main differences to UDFs. UDF API is not versioned. Hence, it was not developed and still has only those features that it did in 3.21.24. Extending UDF API is risky; any change and old UDFs may start crashing the server. Plugin API, on the other hand, is safe. It is protected by a version, and this version is part of every plugin library, the API version that the plugin was compiled against. When a plugin is loaded the server verifies that the version is supported by the server and refuses to load a plugin otherwise. That is, the server can guarantee that all loaded plugins are fully compatible with the server, and no crash can happen because of API mismatch. The API is protected with two version numbers, as it contains two parts—one is common to all plugin types. It is version 1.0, as can be seen in the PLUGIN_LIBRARY_ VERSION column above. The other one is specific to each plugin type. For Daemon plugins this version is 50147.0, as seen in the PLUGIN_TYPE_VERSION column, and it is derived from the MySQL server version (which was 5.1.47 in my examples). Defining Daemon plugins The most basic of Daemon plugins needs no code at all; only a declaration is required. A plugin declaration is an instance of a st_mysql_plugin structure: struct st_mysql_plugin{ int type; void *info; const char *name; const char *author; const char *descr; int license; int (*init)(void *); int (*deinit)(void *); unsigned int version; struct st_mysql_show_var *status_vars; struct st_mysql_sys_var **system_vars; void *__reserved1}; The type defines what type of plugin this will be, which in turn defines what it can do. In MySQL 5.1, type can be set to one of the following enumerated values: Here we are talking about Daemon plugins so this should be set to MYSQL_DAEMON_PLUGIN. The info member is a pointer to the descriptor of the plugin and its members. It contains the information specific to this particular plugin type (while the st_mysql_plugin structure itself contains the information applicable to any plugin, independently of its type). It always starts with an API version number and for Daemon plugins this is all it contains. For other plugin types it may also contain plugin methods that the server will call, but Daemon plugins are not designed to communicate with the server, and their descriptor structure contains nothing besides the version: struct st_mysql_daemon my_daemon_plugin ={ MYSQL_DAEMON_INTERFACE_VERSION }; Next we have the name member, which specifies the name of the plugin as it will be used by MySQL. This is the name that needs to be used in the INSTALL PLUGIN statement, the name that will be seen in SHOW PLUGINS and SHOW ENGINES, the name that all plugin configuration variables and command-line options will start from. That is, the name of the plugin should be a valid SQL identifier and should be good for the command line too. A safe choice would be a name that consists of only Latin letters, digits, and an underscore. Plugin names are not case-sensitive. The author member is a string containing details about the author; it can contain anything we wish. It must be in UTF-8 and can be arbitrarily long, but MySQL will only show the first 64 characters of it. The final string member is descr, which should contain a description of the plugin. Again we are free to put whatever we like here, but we would normally put a short line stating what the plugin does. Again, it is supposed to be UTF-8, but it can be as long as you want. In the next member, each plugin specifies its license. This does not strictly do anything as such, but should help with accidental distribution of a plugin under the wrong license. There are currently three possible values for the license member: Then we come to the init and deinit members, which are pointers to the plugin initialization and de-initialization functions. The initialization function is called when the plugin is loaded during INSTALL PLUGIN or server startup. The de-initialization function is called when a plugin is unloaded, which, again, can happen for two reasons, UNINSTALL PLUGIN or server shutdown. In a Daemon plugin the initialization function is often used to fork a thread to run the main function of the plugin. Both the initialization and the de-initialization functions should return 0 on success or 1 on failure. The version member should be used for the current version of our plugin. A two-component version is encoded as a hexadecimal number, where the lower 8 bits store the second component (minor version) and all others bits store the first component (major version). For example, if the version is set to 0x205, MySQL will show it as "2.5", and if the version is set to 0x123FF, MySQL will show it as "291.255". Unfortunately, there is no way to store in this member a more complex version such as "1.10.14b-RC2". MySQL has many status variables that can be seen with the SHOW STATUS statement, and there are different third-party tools that analyze this data, how the status variables change over time, draw graphs, and so on. A plugin can benefit from that and make its status and various statistics and performance values visible as MySQL status variables. A pointer to the list of the plugin status variables is stored in the status_vars member. Similarly, there is a SHOW VARIABLES statement. It lists all MySQL system variables, variables that are used to alter the behavior of the server. They can have serverwide or session-only effect, some of them can be set on the command line or in the configuration file. They can be modifiable run-time or read-only. This is all available to plugins too. A plugin can add new system variables to the server, global or session, with or without command-line option support, modifiable or read-only. As we would expect, a pointer to the array of these variables goes into the system_vars member. Finally there is one __reserved1 member, which is unused in MySQL 5.1 and should be set to NULL. MySQL provides two macros that help to declare plugins. A plugin declaration starts from the mysql_declare_plugin() macro. It takes one argument, a unique identifier for this plugin library, it will be used automatically as needed to avoid name clashes when plugins are linked statically into the server. This identifier must be the same one that was used as a plugin name in the plug.in file. We can put many plugins in one library, but they all need to be declared in one place, after the mysql_declare_plugin() macro, separated by commas. We end the list of plugin declarations with a mysql_declare_plugin_end macro. A complete example of the plugin declarations can be seen as follows: mysql_declare_plugin(my_plugin){ MYSQL_DAEMON_PLUGIN, &my_plugin_info, "my_plugin", "Andrew Hutchings (Andrew.Hutchings@Sun.COM)", "Daemon example, shows a declaration", PLUGIN_LICENSE_GPL, my_plugin_init, my_plugin_deinit, 0x0100, NULL, NULL, NULL},{ MYSQL_DAEMON_PLUGIN, &my_plugin2_info, "my_plugin2", "Sergei Golubchik (serg@mariadb.org)", "Another Daemon example, shows a declaration", PLUGIN_LICENSE_GPL, my_plugin2_init, NULL, 0xC0FFEE, status, vars, NULL}mysql_declare_plugin_end; This declares two plugins. We can see that the first one: is a Daemon plugin has an info structure called my_plugin_info is called my_plugin and was written by me (Andrew Hutchings) is described as an example plugin is GPL licensed has initialization and de-initialization functions is of version 1.0 has no system or status variables The second plugin can be interpreted similarly. It is also a Daemon plugin of version 49407.238 with initialization function, without de-initialization function, with both status and system variables.
Read more
  • 0
  • 0
  • 2671

article-image-adding-advanced-form-features-using-chronoforms
Packt
31 Aug 2010
16 min read
Save for later

Adding Advanced Form Features using ChronoForms

Packt
31 Aug 2010
16 min read
(For more resources on ChronoForms, see here.) Using PHP to create "select" dropdowns One frequent request is to create a select drop-down from a set of records in a database table. Getting ready You'll need to know the MySQL query to extract the records you need from the database table. This requires some Joomla! knowledge and some MySQL know-how. Joomla! keeps its articles in the jos_content database table and the two-table columns that we want are the article title and the article id. A quick check in the database tells us that the columns are appropriately called title and id. We can also see that the section id column is called sectionid (with no spaces); the column that tells us if the article is published or not is called state and takes the values 1 for published and 0 for not published. How to do it . . . We are going to use some PHP to look up the information about the articles in the database table and then output the results it finds as a series of options for our drop-down box. You'll recall that normal HTML code for a drop-down box looks something like this: <select . . .> <option value='option_value_1'>Option 1 text</option> <option value='option_value_2'>Option 2 text</option> . . .</select> This is simplified a little so that we can see the main parts that concern us here—the options. Each option uses the same code with two variables—a value attribute and a text description. The value will be returned when the form is submitted; the text description is shown when the form is displayed on your site. In simple forms, the value and the description text are often the same. This can be useful if all you are doing with the results is to display them on a web page or in an e-mail. If you are going to use them for anything more complicated than that, it can be much more useful to use a simplified, coded form in the value. For our list of articles, it will be helpful if our form returns the ID of the article rather than its title. Hence, we need to set the options to be something like this: <option value='99'>Article title</option> Having the article ID will let us look up the article in the database and extract any other information that we might need, or to update the record to change the corresponding record. Here's the code that we'll use. The beginning and ending HTML lines are exactly the same as the standard drop-down code that ChronoForms generates but the "option" lines are replaced by the section inside the <?php . . . ?> tags. The PHP snippet looks up the article IDs and titles from the jos_content table, then loops through the results writing an <option> tag for each one: <div class="form_item"> <div class="form_element cf_dropdown"> <label class="cf_label" style="width: 150px;"> Articles</label> <select class="cf_inputbox validate-selection" id="articles" size="1" name="articles"> <option value=''>--?--</option><?phpif (!$mainframe->isSite() ) {return;}$db =& JFactory::getDBO();$query = " SELECT `id`, `title` FROM `#__content` WHERE `sectionid` = 1 AND `state` = 1 ;";$db->setQuery($query);$options = $db->loadAssocList();foreach ( $options as $o ) { echo "<option value='".$o[id]."'>".$o[title]."</option>";}?> </select> </div> <div class="cfclear">&nbsp;</div></div> The resulting HTML will be a standard select drop-down that displays the list of titles and returns the article ID when the form is submitted. Here's what the form input looks like: A few of the options from the page source are shown: <option value='1'>Welcome to Joomla!</option><option value='2'>Newsflash 1</option><option value='3'>Newsflash 2</option>. . . How it works... We are loading the values of the id and title columns from the database record and then using a PHP for each loop to go through the results and add each id and title pair into an <option> tag. There's more... There are many occasions when we want to add select drop-downs into forms with long lists of options. Date and time selectors, country and language lists, and many others are frequently used. We looked here to get the information from a database table which is simple and straightforward when the data is in a table or when the data can conveniently be stored in a table. It is the preferred solution for data such as article titles that can change from day to day. There are a couple of other solutions that can also be useful: Creating numeric options lists directly from PHP Using arrays to manage option lists that change infrequently Creating numeric options lists Let's imagine that we need to create a set of six numeric drop-downs to select: day, month, year, hour, minute, and second. We could clearly do these with manually-created option lists but it soon gets boring creating sixty similar options. There is a PHP method range() that lets us use a similar approach to the one in the recipe. For a range of zero to 60, we can use range(0, 60). Now, the PHP part of our code becomes: <div class="form_item"> <div class="form_element cf_dropdown"> <label class="cf_label" style="width: 150px;"> Minutes</label> <select class="cf_inputbox validate-selection" id="minutes" size="1" name="minutes"> <option value=''>--?--</option><?phpif (!$mainframe->isSite() ) {return;}foreach ( range(0, 60) as $v ) { echo "<option value='$v'>$v</option>";}?> </select> </div> <div class="cfclear">&nbsp;</div></div> This is slightly simpler than the database foreach code, as we don't need the quotes round the array values. This will work very nicely and we could repeat something very similar for each of the other five drop-downs. However, when we think about it, they will all be very similar and that's usually a sign that we can use more PHP to do some of the work for us. Indeed we can create our own little PHP function to output blocks of HTML for us. Looking at this example, there are four things that will change between the blocks—the label text, the name and id, and the range start and the range end. We can set these as variables in a PHP function: <?phpif ( !$mainframe->isSite() ) {return;}function createRangeSelect($label, $name, $start, $end) {?><div class="form_item"> <div class="form_element cf_dropdown"> <label class="cf_label" style="width: 150px;"> <?php echo $label; ?></label> <select class="cf_inputbox validate-selection" id="<?php echo $name; ?>" size="1" name="<?php echo $name; ?>"> <option value=''>--?--</option><?php foreach ( range($start, $end) as $v ) { echo "<option value='$v'>$v</option>"; }?> </select> </div> <div class="cfclear">&nbsp;</div></div><?php}?> Notice that this is very similar to the previous code example. We've added the function . . . line at the start, the } at the end, and replaced the values with variable names. It's important to get the placement of the <?php . . . ?> tags right. Code inside the tags will be treated as PHP, outside them as HTML. All that remains now is to call the function to generate our drop-downs: <?phpif (!$mainframe->isSite() ) {return;}createRangeSelect('Day', 'day', 0, 31);createRangeSelect('Month', 'month', 1, 12);createRangeSelect('Year', 'year', 2000, 2020);createRangeSelect('Hour', 'hour', 0, 24);createRangeSelect('Minute', 'minute', 0, 60);createRangeSelect('Second', 'second', 0, 60);function createRangeSelect($label, $name, $start, $end) {. . . The result tells us that we have more work to do on the layout, but the form elements work perfectly well. Creating a drop-down from an array In the previous example, we used the PHP range() method to generate our options. This works well for numbers but not for text. Imagine that we have to manage a country list. These do change, but not frequently. So they are good candidates for keeping in an array in the Form HTML. It's not too difficult to find pre-created PHP arrays of countries with a little Google research and it's probably easier to use one of these and correct it for your needs than to start from scratch. As we mentioned with the Article list, it's generally simpler and more flexible to use a list with standard IDs (we've used two-letter codes below). With countries, this can remove many problems with special characters and translations. Here are the first few lines of a country list: $countries = array( 'AF'=>'Afghanistan', 'AL'=>'Albania', 'DZ'=>'Algeria', 'AS'=>'American Samoa', . . .); Once we have this, it's easy to modify our foreach . . . loop to use it: foreach ( $countries as $k => $v ) { echo "<option value='$k'>$v</option>";} If you are going to use the country list in more than one form, then it may be worthwhile keeping it in a separate file that is included in the Form HTML. That way, any changes you make will be updated immediately in all of your forms. Using Ajax to look up e-mail addresses It's not very difficult to add Ajax functionality to ChronoForms, but it's not the easiest task in the world either. We'll walk through a fairly simple example here which will provide you with the basic experience to build more complex applications. You will need some knowledge of JavaScript to follow through this recipe. Normally, the only communication between the ChronoForms client (the user in their browser) and the server (the website host) is when a page is loaded or a form is submitted. Form HTML is sent to the client and a $_POST array of results is returned. Ajax is a technique, or a group of techniques, that enables communication while the user is browsing the page without them having to submit the form. As usual, at the browser end the Ajax communication is driven by JavaScript and at the server end we'll be responding using PHP. Put simply, the browser asks a question, the server replies, then the browser shows the reply to the user. For the browser JavaScript and the server PHP to communicate, there needs to be an agreed set of rules about how the information will be packaged. We'll be using the JSON (www.json.org) format. The task we will work on will use a newsletter form. We'll check to see if the user's e-mail is already listed in our user database. This is slightly artificial but the same code can easily be adapted to work with the other database tables and use more complex checks. Getting ready We'll need a form with an e-mail text input. The input id needs to be email for the following code to work: <div class="form_item"> <div class="form_element cf_textbox"> <label class="cf_label" style="width: 150px;">Email</label> <input class="cf_inputbox" maxlength="150" size="30" title="" id="email" name="email" type="text" /> </div> <div class="cfclear">&nbsp;</div></div> The form we use will also have a name text input and a submit button, but they are to make it look like a real form and aren't used in the Ajax coding. How to do it . . . We'll follow the action and start with the user action in the browser. We need to start our check when the user makes an entry in the e-mail input. So, we'll link our JavaScript to the blur event in that input. Here's the core of the code that goes in the Form JavaScript box: / set the url to send the request tovar url = 'index.php?option=com_chronocontact &chronoformname=form_name&task=extra&format=raw';// define 'email'var email = $('email');// set the Ajax call to run// when the email field loses focusemail.addEvent('blur', function() { // Send the JSON request var jSonRequest = new Json.Remote(url, { . . . }).send({'email': email.value});}); Note that the long line starting with var url = . . . &format=raw'; is all one line and should not have any breaks in it. You also need to replace 'form_name' with the name of your form in this URL. There really isn't too much to this. We are using the MooTools JSON functions and they make sending the code very simple. The next step is to look at what happens back on the server. The URL we used in the JavaScript includes the task=extra parameter. When ChronoForms sees this, it will ignore the normal Form Code and instead run the code from the Extra Code boxes at the bottom of the Form Code tab. By default, ChronoForms will execute the code from Extra code 1. If you need to access one of the other boxes, then use for example, task=extra&extraid=3 to run the code from Extra Code box 3. Now, we are working back on the server. So, we need to use PHP to unpack the Ajax message, check the database, and send a message back: <?php// clean up the JSON message$json = stripslashes($_POST['json']);$json = json_decode($json);$email = strtolower(trim($json->email));// check that the email field isn't empty$response = false;if ( $email ) { // Check the database $db =& JFactory::getDBO(); $query = " SELECT COUNT(*) FROM `#__users` WHERE LOWER(`email`) = ".$db->quote($email)."; "; $db->setQuery($query); $response = (bool) !$db->loadResult();}$response = array('email_ok' => $response );//send the replyecho json_encode($response);// stop the from running$MyForm->stopRunning = true;die;?> This code has three main parts: To start with, we "unwrap" the JSON message. Then, we check if it isn't empty and run the database query. Lastly, we package up the reply and tidy up at the end to stop any more form processing from this request. The result we send will be array('email_ok' => $response ) where $response will be either true or false. This is probably the simplest JSON message possible, but is enough for our purpose. Note that here, true means that this e-mail is not listed and is OK to use. The third step is to go back to the form JavaScript and decide how we are going to respond to the JSON reply. Again, we'll keep it simple and just change the background color of the box—red if the e-mail is already in use (or isn't a valid e-mail) or green if the entry isn't in use and is OK to submit. Here's the code snippet to do this using the onComplete parameter of the MooTools JSON function: onComplete: function(r) { // check the result and set the background color if ( r.email_ok ) { email.setStyle('background-color', 'green'); } else { email.setStyle('background-color', 'red'); }} Instead of (or as well as) changing the background color, we could make other CSS changes, display a message, show a pop-up alert, or almost anything else. Lastly let's put the two parts of the client-side JavaScript together with a little more code to make it run smoothly and to check that there is a valid e-mail before sending the JSON request. window.addEvent('domready', function() { // set the url to send the request to var url = 'index.php?option=com_chronocontact &chronoformname=form_name&task=extra&format=raw'; var email = $('email'); email.addEvent('blur', function() { // clear any background color from the input email.setStyle('background-color', 'white'); // check that the email address is valid regex = /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i; var value = email.value.trim(); if ( value.length > 6 && regex.test(value) ) { // if all is well send the JSON request var jSonRequest = new Json.Remote(url, { onComplete: function(r) { // check the result and set the background color if ( r.email_ok ) { email.setStyle('background-color', 'green'); } else { email.setStyle('background-color', 'red'); } } }).send({'email': email.value}); } else { // if this isn't a valid email set background color red email.setStyle('background-color', 'red'); } });}); Note that the long line starting with var url = . . . &format=raw'; is all one line and should not have any breaks in it. You also need to replace form_name with the name of your form in this URL. Make sure both the code blocks are in place in the Form JavaScript box and in the Extra Code 1 box, save, and publish your form. Then, test it to make sure that the code is working OK. The Ajax may take a second or two to respond but once you move out of the e-mail, input by tabbing on to another input or clicking somewhere else; the background colour should go red or green. How it works... As far as the Ajax and JSON parts of this are concerned, all we can say here is that it works. You'll need to dig into the MooTools, Ajax, or JSON documents to find out more. From the point of view of ChronoForms, the "clever" bit is the ability to interpret the URL that the Ajax message uses. We ignored most of it at the time but the JavaScript included this long URL (with the query string broken up into separate parameters): index.php?option=com_chronocontact&chronoformname=form_name&task=extra&format=raw The option parameter is the standard Joomla! way to identify which extension to pass the URL to. The chronoformname parameter tells ChronoForms which form to pass the URL to. The task=extra parameter tells ChronoForms that this URL is a little out of the ordinary (you may have noticed that forms usually have &task=send in the onSubmit URL). When ChronoForms sees this, it will pass the URL to the Extra Code box for processing and bypass the usual OnSubmit processing. Lastly, the format=raw parameter tells Joomla! to show the resulting code without any extra formatting and without adding the template code. This means that all that is sent back is just the JSON message. Without it we'd have to dig the message out from loads of surrounding HTML we don't need.
Read more
  • 0
  • 0
  • 5641
article-image-python-multimedia-fun-animations-using-pyglet
Packt
31 Aug 2010
8 min read
Save for later

Python Multimedia: Fun with Animations using Pyglet

Packt
31 Aug 2010
8 min read
(For more resources on Python, see here.) So let's get on with it. Installation prerequisites We will cover the prerequisites for the installation of Pyglet in this section. Pyglet Pyglet provides an API for multimedia application development using Python. It is an OpenGL-based library, which works on multiple platforms. It is primarily used for developing gaming applications and other graphically-rich applications. Pyglet can be downloaded from http://www.pyglet.org/download.html. Install Pyglet version 1.1.4 or later. The Pyglet installation is pretty straightforward. Windows platform For Windows users, the Pyglet installation is straightforward—use the binary distribution Pyglet 1.1.4.msi or later. You should have Python 2.6 installed. For Python 2.4, there are some more dependencies. We won't discuss them in this article, because we are using Python 2.6 to build multimedia applications. If you install Pyglet from the source, see the instructions under the next sub-section, Other platforms. Other platforms The Pyglet website provides a binary distribution file for Mac OS X. Download and install pyglet-1.1.4.dmg or later. On Linux, install Pyglet 1.1.4 or later if it is available in the package repository of your operating system. Otherwise, it can be installed from source tarball as follows: Download and extractthetarballextractthetarball the tarball pyglet-1.1.4.tar.gz or a later version. Make sure that python is a recognizable command in shell. Otherwise, set the PYTHONPATH environment variable to the correct Python executable path. In a shell window, change to the mentioned extracted directory and then run the following command: python setup.py install Review the succeeding installation instructions using the readme/install instruction files in the Pyglet source tarball. If you have the package setuptools (http://pypi.python.org/pypi/setuptools) the Pyglet installation should be very easy. However, for this, you will need a runtime egg of Pyglet. But the egg file for Pyglet is not available at http://pypi.python.org. If you get hold of a Pyglet egg file, it can be installed by running the following command on Linux or Mac OS X. You will need administrator access to install the package: $sudo easy_install -U pyglet Summary of installation prerequisites Package Download location Version Windows platform Linux/Unix/OS X platforms Python http://python.org/download/releases/ 2.6.4 (or any 2.6.x) Install using binary distribution Install from binary; also install additional developer packages (For example, with python-devel in the package name in a rpm-based Linux distribution).   Build and install from the source tarball. Pyglet http://www.pyglet.org/download.html 1.1.4 or later Install using binary distribution (the .msi file) Mac: Install using disk image file (.dmg file). Linux: Build and install using the source tarball. Testing the installation Before proceeding further, ensure that Pyglet is installed properly. To test this, just start Python from the command line and type the following: >>>import pyglet If this import is successful, we are all set to go! A primer on Pyglet Pyglet provides an API for multimedia application development using Python. It is an OpenGL-based library that works on multiple platforms. It is primarily used for developing gaming and other graphically-rich applications. We will cover some important aspects of Pyglet framework. Important components We will briefly discuss some of the important modules and packages of Pyglet that we will use. Note that this is just a tiny chunk of the Pyglet framework. Please review the Pyglet documentation to know more about its capabilities, as this is beyond the scope of this article. Window The pyglet.window.Window module provides the user interface. It is used to create a window with an OpenGL context. The Window class has API methods to handle various events such as mouse and keyboard events. The window can be viewed in normal or full screen mode. Here is a simple example of creating a Window instance. You can define a size by specifying width and height arguments in the constructor. win = pyglet.window.Window() The background color for the image can be set using OpenGL call glClearColor, as follows: pyglet.gl.glClearColor(1, 1, 1, 1) This sets a white background color. The first three arguments are the red, green, and blue color values. Whereas, the last value represents the alpha. The following code will set up a gray background color. pyglet.gl.glClearColor(0.5, 0.5, 0.5, 1) The following illustration shows a screenshot of an empty window with a gray background color. Image The pyglet.image module enables the drawing of images on the screen. The following code snippet shows a way to create an image and display it at a specified position within the Pyglet window. img = pyglet.image.load('my_image.bmp')x, y, z = 0, 0, 0img.blit(x, y, z) A later section will cover some important operations supported by the pyglet.image module. Sprite This is another important module. It is used to display an image or an animation frame within a Pyglet window discussed earlier. It is an image instance that allows us to position an image anywhere within the Pyglet window. A sprite can also be rotated and scaled. It is possible to create multiple sprites of the same image and place them at different locations and with different orientations inside the window. Animation Animation module is a part of pyglet.image package. As the name indicates, pyglet.image.Animation is used to create an animation from one or more image frames. There are different ways to create an animation. For example, it can be created from a sequence of images or using AnimationFrame objects. An animation sprite can be created and displayed within the Pyglet window. AnimationFrame This creates a single frame of an animation from a given image. An animation can be created from such AnimationFrame objects. The following line of code shows an example. animation = pyglet.image.Animation(anim_frames) anim_frames is a list containing instances of AnimationFrame. Clock Among many other things, this module is used for scheduling functions to be called at a specified time. For example, the following code calls a method moveObjects ten times every second. pyglet.clock.schedule_interval(moveObjects, 1.0/10) Displaying an image In the Image sub-section, we learned how to load an image using image.blit. However, image blitting is a less efficient way of drawing images. There is a better and preferred way to display the image by creating an instance of Sprite. Multiple Sprite objects can be created for drawing the same image. For example, the same image might need to be displayed at various locations within the window. Each of these images should be represented by separate Sprite instances. The following simple program just loads an image and displays the Sprite instance representing this image on the screen. 1 import pyglet23 car_img= pyglet.image.load('images/car.png')4 carSprite = pyglet.sprite.Sprite(car_img)5 window = pyglet.window.Window()6 pyglet.gl.glClearColor(1, 1, 1, 1)78 @window.event9 def on_draw():10 window.clear()11 carSprite.draw()1213 pyglet.app.run() On line 3, the image is opened using pyglet.image.load call. A Sprite instance corresponding to this image is created on line 4. The code on line 6 sets white background for the window. The on_draw is an API method that is called when the window needs to be redrawn. Here, the image sprite is drawn on the screen. The next illustration shows a loaded image within a Pyglet window. In various examples in this article, the file path strings are hardcoded. We have used forward slashes for the file path. Although this works on Windows platform, the convention is to use backward slashes. For example, images/car.png is represented as imagescar.png. Additionally, you can also specify a complete path to the file by using the os.path.join method in Python. Regardless of what slashes you use, the os.path.normpath will make sure it modifies the slashes to fit to the ones used for the platform. The use of oos.path.normpath is illustrated in the following snippet: import osoriginal_path = 'C:/images/car.png"new_path = os.path.normpath(original_path) The preceding image illustrates Pyglet window showing a still image. Mouse and keyboard controls The Window module of Pyglet implements some API methods that enable user input to a playing animation. The API methods such as on_mouse_press and on_key_press are used to capture mouse and keyboard events during the animation. These methods can be overridden to perform a specific operation. Adding sound effects The media module of Pyglet supports audio and video playback. The following code loads a media file and plays it during the animation. 1 background_sound = pyglet.media.load(2 'C:/AudioFiles/background.mp3',3 streaming=False)4 background_sound.play() The second optional argument provided on line 3 decodes the media file completely in the memory at the time the media is loaded. This is important if the media needs to be played several times during the animation. The API method play() starts streaming the specified media file.
Read more
  • 0
  • 0
  • 12514

Packt
31 Aug 2010
8 min read
Save for later

MySQL 5.1 Plugin: HTML Storage Engine—Reads and Writes

Packt
31 Aug 2010
8 min read
(For more resources on MySQL, see here.) An idea of the HTML engine Ever thought about what your tables might look like? Why not represent a table as a <TABLE>? You would be able to see it, visually, in any browser. Sounds cool. But how could we make it work? We want a simple engine, not an all-purpose Swiss Army Knife HTML-to-SQL converter, which means we will not need any existing universal HTML or XML parsers, but can rely on a fixed file format. For example, something like this: <html><head><title>t1</title></head><body><table border=1><tr><th>col1</th><th>other col</th><th>more cols</th></tr><tr><td>data</td><td>more data</td><td>more data</td></tr><!-- this row was deleted ... --><tr><td>data</td><td>more data</td><td>more data</td></tr>... and so on ...</table></body></html> But even then this engine is way more complex than the previous example, and it makes sense to split the code. The engine could stay, as usual, in the ha_html.cc file, the declarations in ha_html.h, and if we need any utility functions to work with HTML we can put them in the htmlutils.cc file. Flashback A storage engine needs to declare a plugin and an initialization function that fills a handlerton structure. Again, the only handlerton method that we need here is a create() method. #include "ha_html.h"static handler* html_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root){ return new (mem_root) ha_html(hton, table);}static int html_init(void *p){ handlerton *html_hton = (handlerton *)p; html_hton->create = html_create_handler; return 0;}struct st_mysql_storage_engine html_storage_engine ={ MYSQL_HANDLERTON_INTERFACE_VERSION };mysql_declare_plugin(html){ MYSQL_STORAGE_ENGINE_PLUGIN, &html_storage_engine, "HTML", "Sergei Golubchik", "An example HTML storage engine", PLUGIN_LICENSE_GPL, html_init, NULL, 0x0001, NULL, NULL, NULL}mysql_declare_plugin_end; Now we need to implement all of the required handler class methods. Let's start with simple ones: const char *ha_html::table_type() const{ return "HTML";}const char **ha_html::bas_ext() const{ static const char *exts[] = { ".html", 0 }; return exts;}ulong ha_html::index_flags(uint inx, uint part, bool all_parts) const{ return 0;}ulonglong ha_html::table_flags() const{ return HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_NO_BLOBS;}THR_LOCK_DATA **ha_html::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type){ if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) lock.type = lock_type; *to ++= &lock; return to;} These methods are familiar to us. They say that the engine is called "HTML", it stores the table data in files with the .html extension, the tables are not transactional, the position for ha_html::rnd_pos() is obtained by calling ha_html::position(), and that it does not support BLOBs. Also, we need a function to create and initialize an HTML_SHARE structure: static HTML_SHARE *find_or_create_share( const char *table_name, TABLE *table){ HTML_SHARE *share; for (share = (HTML_SHARE*)table->s->ha_data; share; share = share->next) if (my_strcasecmp(table_alias_charset, table_name, share->name) == 0) return share; share = (HTML_SHARE*)alloc_root(&table->s->mem_root, sizeof(*share)); bzero(share, sizeof(*share)); share->name = strdup_root(&table->s->mem_root, table_name); share->next = (HTML_SHARE*)table->s->ha_data; table->s->ha_data = share; return share;} It is exactly the same function, only the structure is now called HTML_SHARE, not STATIC_SHARE. Creating, opening, and closing the table Having done the basics, we can start working with the tables. The first operation, of course, is the table creation. To be able to read, update, or even open the table we need to create it first, right? Now, the table is just an HTML file and to create a table we only need to create an HTML file with our header and footer, but with no data between them. We do not need to create any TABLE or Field objects, or anything else—MySQL does it automatically. To avoid repeating the same HTML tags over and over we will define the header and the footer in the ha_html.h file as follows: #define HEADER1 "<html><head><title>"#define HEADER2 "</title></head><body><table border=1>\n"#define FOOTER "</table></body></html>"#define FOOTER_LEN ((int)(sizeof(FOOTER)-1)) As we want a header to include a table name we have split it in two parts. Now, we can create our table: int ha_html::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info){ char buf[FN_REFLEN+10]; strcpy(buf, name); strcat(buf, *bas_ext()); We start by generating a filename. The "table name" that the storage engine gets is not the original table name, it is converted to be a safe filename. All "troublesome" characters are encoded, and the database name is included and separated from the table name with a slash. It means we can safely use name as the filename and all we need to do is to append an extension. Having the filename, we open it and write our data: FILE *f = fopen(buf, "w"); if (f == 0) return errno; fprintf(f, HEADER1); write_html(f, table_arg->s->table_name.str); fprintf(f, HEADER2 "<tr>"); First, we write the header and the table name. Note that we did not write the value of the name argument into the header, but took the table name from the TABLE_SHARE structure (as table_arg->s->table_name.str), because name is mangled to be a safe filename, and we would like to see the original table name in the HTML page title. Also, we did not just write it into the file, we used a write_html() function—this is our utility method that performs the necessary entity encoding to get a well-formed HTML. But let's not think about it too much now, just remember that we need to write it, it can be done later. Now, we iterate over all fields and write their names wrapped in <th>...</th> tags. Again, we rely on our write_html() function here: for (uint i = 0; i < table_arg->s->fields; i++) { fprintf(f, "<th>"); write_html(f, table_arg->field[i]->field_name); fprintf(f, "</th>"); } fprintf(f, "</tr>"); fprintf(f, FOOTER); fclose(f); return 0;} Done, an empty table is created. Opening it is easy too. We generate the filename and open the file just as in the create() method. The only difference is that we need to remember the FILE pointer to be able to read the data later, and we store it in fhtml, which has to be a member of the ha_html object: int ha_html::open(const char *name, int mode, uint test_if_locked){ char buf[FN_REFLEN+10]; strcpy(buf, name); strcat(buf, *bas_ext()); fhtml = fopen(buf, "r+"); if (fhtml == 0) return errno; When parsing an HTML file we will often need to skip over known patterns in the text. Instead of using a special library or a custom pattern parser for that, let's try to use scanf()—it exists everywhere, has a built-in pattern matching language, and it is powerful enough for our purposes. For convenience, we will wrap it in a skip_html() function that takes a scanf() format and returns the number of bytes skipped. Assuming we have such a function, we can finish opening the table: skip_html(fhtml, HEADER1 "%*[^<]" HEADER2 "<tr>"); for (uint i = 0; i < table->s->fields; i++) { skip_html(fhtml, "<th>%*[^<]</th>"); } skip_html(fhtml, "</tr>"); data_start = ftell(fhtml); We skip the first part of the header, then "everything up to the opening angle bracket", which eats up the table name, and the second part of the header. Then we skip individual row headers in a loop and the end of row </tr> tag. In order not to repeat this parsing again we remember the offset where the row data starts. At the end we allocate an HTML_SHARE and initialize lock objects: share = find_or_create_share(name, table); if (share->use_count++ == 0) thr_lock_init(&share->lock); thr_lock_data_init(&share->lock,&lock,NULL); return 0;} Closing the table is simple, and should not come as a surprise to us: int ha_html::close(void){ fclose(fhtml); if (--share->use_count == 0) thr_lock_delete(&share->lock); return 0;}
Read more
  • 0
  • 0
  • 1439

article-image-using-chronoforms-add-more-features-your-joomla-form
Packt
31 Aug 2010
11 min read
Save for later

Using ChronoForms to add More Features to your Joomla! Form

Packt
31 Aug 2010
11 min read
(For more resources on ChronoForms, see here.) Showing a YouTube video This is the "not really a form" recipe in this article, it just opens a little door to some of the other, more unexpected, capabilities of ChronoForms. For the most part Joomla! protects the content you can display on your pages; it's easy to show HTML + CSS formatted content, more difficult to show PHP and JavaScript. There are many modules, plug-ins and extensions that can help with this but if you have ChronoForms installed then it may be able to help. ChronoForms is designed to show pages that use HTML, CSS, PHP, and JavaScript working together. Most often the pages created are forms but nothing actually requires that any form inputs are included so we can add any code that we like. ChronoForms will wrap our code inside <form>. . .</form> tags which means that we can't embed a form (why would we want to?), but otherwise most things are possible.   Getting ready You will need the ID of the YouTube video that you want to display. We're going to use a video from a conference at Ashridge Business School, but any video will work in essentially the same way. This recipe was developed for this particular video to force display of the HD version. At that time HD was a new option on YouTube and was not readily accessible as it is now. How to do it... Find the video you want on YouTube and look for the links boxes in the right hand column. Here we've clicked on the "customize" icon—the little gear wheel—to open up the options menu. When you've set the options you want copy the code from the Embed box. Here is the code from this video with some added line breaks for clarity: <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/2Ok1SFnMS4E&hl=en_GB&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/2Ok1SFnMS4E&hl=en_GB&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object> To create a good looking page, we are going to add some HTML before and after this snippet: <h3>Video Postcards from the Edge</h3><div>The video of the 2008 AMOC Conference</div><div style='margin:6px; padding:0px; border:6px solid silver;width:425px;'><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/2Ok1SFnMS4E&hl =en&fs=1&ap=%2526fmt%3D18"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/2Ok1SFnMS4E&hl=en&fs =1&ap=%2526fmt%3D18" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></div><div>Some more text . . .</div> If you look closely, you'll see that there is also a new parameter in the URL—&ap=%2526fmt%3D18—which is there to force the HD version of the video to be used. Paste this code into the Form HTML box of a new form, save, and publish it. Of course, it would be entirely possible to embed the video and to add form inputs in the same page, maybe to ask for comments or reviews. How it works... Very simply ChronoForms allows you to embed scripts into the page HTML that are not permitted in standard Joomla! articles. Adding a barcode to a form e-mail Sometimes it's important to add a unique identifier to the form response, for example travel or event tickets. In this recipe we will look at generating a "random" identifier and adding it to the form e-mail as a scannable barcode. Getting ready We're going to need a simple form. Our newsletter form will be perfect although we'll be adding to the code in the Form HTML box. We'll need a simple function to create the "random identifier" which we will see shortly. Lastly we"ll need code to generate a barcode. Rather than taking time reinventing this particular wheel, we're going to use a PHP program created by Charles J Scheffold and made available for use or download from http://www.sid6581.net/cs/php-scripts/barcode/. How to do it... First, grab a copy of the barcode.php file from sid6581.net. We'll need to make this file accessible to our form. So let's create a new folder inside the ChronoForms front-end folder. You'll probably need to use an FTP client to do this, or install the "exTplorer" Joomla! extension which will allow you to create folders from within the Joomla! Site Admin interface. Browse to [root]/components/com_chronocontact and create a new includes folder Copy the standard Joomla! index.html file from the com_chronocontact folder into the new folder Upload the barcode.php file into the new includes folder Now, we are going to add the function to create a "random" identifier to the Form HTML. This is a small function that creates an alphanumeric string when it is called. <?phpif ( !$mainframe->isSite() ) { return; }/* function to generate a random alpha-numeric code using a specified pattern * * @param $pattern string * @return string */function generateIdent($pattern='AA9999A'){ $alpha = array("A","B","C","D","E","F","G","H", "J","K","L","M","N","P","Q","R","S","T","U","V","W", "X","Y","Z"); $digit = array("1","2","3","4","5","6","7","8","9"); $return = ""; $pattern_array = str_split($pattern, 1); foreach ( $pattern_array as $v ) { if ( is_numeric($v) ) { $return .= $digit[array_rand($digit)]; } elseif ( in_array(strtoupper($v), $alpha) ) { $return .= $alpha[array_rand($alpha)]; } else { $return .= " "; } } return $return;}?> We call this function using generateIdent() or generateIdent('pattern') where the pattern is a string of As and 9s that defines the shape of the ident we want. The default is AA9999A, giving idents like KX4629G. This will be perfectly fine for our example here. We also want to add the ident into the form and we'll use a hidden field to do that, but to make it visible we'll also display the value. <?php$ident = generateIdent();echo "<div>Ident is $ident</div>";?><input type='hidden' name='ident' id='ident' value='<?php echo $ident; ?>' /> In day to day use we probably wouldn't generate the ident until after the form is submitted. There is often no useful value in displaying it on the form and essentially the same code will work in the OnSubmit boxes. However, here it makes the process clearer to generate it in the form HTML. We can add both these code snippets to our form just before the submit button element. Then apply or save the form and view it in your browser. The layout may not be very elegant but the Ident is there. Refresh the browser a few times to be sure that it is different each time. It's simpler and tempting to use serial numbers to identify records. If you are saving data in a table then these are generated for you as record IDs. It does create some problems though; in particular, it can make it very easy to guess what other IDs are valid and if, as we often do, we include the ID in a URL it may well be possible to guess what other URLs will be valid. Using a random string like this makes that kind of security breach more difficult and less likely. We said though that we'd generate a barcode, so let's develop this form one more step and show the barcode in the form. If you look at the code in barcode.php, it shows a list of parameters and says what we can use. For example: <img src="barcode.php?barcode=123456&width=320&height=200"> We need to modify this a little to link to the new folder for the file and to add our generated ident value: <img src="/components/com_chronocontact/includes/barcode.php?barcode=<? php echo $ident;?>&width=320&height=8"> This code can go in place of the "echo" line we used to display the ident value: <?php$ident = generateIdent();echo "<img src='".JURI::base()."components/com_chronocontact/includes/barcode.php?barcode=".$ident."&width=320&height=80' />";?> Apply or save the form and view it in your browser. There we have it—a bar code in our form showing the random ident that we have created. If you don't see any graphic and the code appears to be correct then you may not have the PHP GD graphics library installed. Check on the AntiSpam tab for any of your forms and you will see a GD Info box. The GD library is now included in the vast majority of PHP installations. If you don't have it then check with your ISP to see if the library can be enabled. Now that's actually not of much use except to show that it works, you can't scan a bar code off the screen. Where we want it is in our Email template. The code to add to the template is: <div>Your code: {ident}</div><img src="<?php echo JURI::base().'components/com_chronocontact/ includes/'; ?>barcode.php?barcode={ident}&width=280&height=100" /> As this includes some PHP, we can't add it using the Rich Text Editor. First we need to go to the Email Setup | Properties box and set Use Template Editor to No, apply the Properties, then apply the form and go to the Email Template tab. To avoid an "oddity" in the current release of ChronoForms it may be necessary to comment out the generateIdent() function code block in the Form HTML, while you create an Email Setup. Just put /* & */ before and after the block if you get a blank page or see a PHP Error message about re-declaring the function. Now click on the Email Template tab and paste the code at the end of the textarea. Submit the form to test. We now have a printable e-mail complete with a barcode showing our random ident. How it works... In this recipe we did a couple of things. We added some more complex PHP to the Form HTML that we had before and we imported a PHP script found on the internet and successfully used that in combination with ChronoForms. There are many hundreds of useful scripts available for almost any conceivable function. Not all are of good quality and not all will work in this way but, with a little work, a surprising number will function perfectly well. There's more... We said earlier that it might be better to generate the ident after the form is submitted. Here's the code to use in the OnSubmit Before code box to get the same result in the e-mail: <?phpif ( ! $mainframe->isSite() ) { return; }JRequest::setVar('ident', generateIdent());/* function to generate a random alpha-numeric code using a specified pattern * * @param $pattern string * @return string */function generateIdent($pattern='AA9999A'){ $alpha = array("A","B","C","D","E","F","G","H", "J","K","L","M","N","P","Q","R","S","T","U","V","W", "X","Y","Z"); $digit = array("1","2","3","4","5","6","7","8","9"); $return = ""; $pattern_array = str_split($pattern, 1); foreach ( $pattern_array as $v ) { if ( is_numeric($v) ) { $return .= $digit[array_rand($digit)]; } elseif ( in_array(strtoupper($v), $alpha) ) { $return .= $alpha[array_rand($alpha)]; } else { $return .= " "; } } return $return;}?> If you use this, then you can remove all of the additional code from the Form HTML box leaving just the basic HTML generated by the Form Wizard. The Email template code remains as we created it previously.
Read more
  • 0
  • 0
  • 6253
article-image-python-multimedia-animation-examples-using-pyglet
Packt
31 Aug 2010
7 min read
Save for later

Python Multimedia: Animation Examples using Pyglet

Packt
31 Aug 2010
7 min read
(For more resources on Python, see here.) Single image animation Imagine that you are creating a cartoon movie where you want to animate the motion of an arrow or a bullet hitting a target. In such cases, typically it is just a single image. The desired animation effect is accomplished by performing appropriate translation or rotation of the image. Time for action – bouncing ball animation Lets create a simple animation of a 'bouncing ball'. We will use a single image file, ball.png, which can be downloaded from the Packt website. The dimensions of this image in pixels are 200x200, created on a transparent background. The following screenshot shows this image opened in GIMP image editor. The three dots on the ball identify its side. We will see why this is needed. Imagine this as a ball used in a bowling game. The image of a ball opened in GIMP appears as shown in the preceding image. The ball size in pixels is 200x200. Download the files SingleImageAnimation.py and ball.png from the Packt website. Place the ball.png file in a sub-directory 'images' within the directory in which SingleImageAnimation.py is saved. The following code snippet shows the overall structure of the code. 1 import pyglet2 import time34 class SingleImageAnimation(pyglet.window.Window):5 def __init__(self, width=600, height=600):6 pass7 def createDrawableObjects(self):8 pass9 def adjustWindowSize(self):10 pass11 def moveObjects(self, t):12 pass13 def on_draw(self):14 pass15 win = SingleImageAnimation()16 # Set window background color to gray.17 pyglet.gl.glClearColor(0.5, 0.5, 0.5, 1)1819 pyglet.clock.schedule_interval(win.moveObjects, 1.0/20)2021 pyglet.app.run() Although it is not required, we will encapsulate event handling and other functionality within a class SingleImageAnimation. The program to be developed is short, but in general, it is a good coding practice. It will also be good for any future extension to the code. An instance of SingleImageAnimation is created on line 14. This class is inherited from pyglet.window.Window. It encapsulates the functionality we need here. The API method on_draw is overridden by the class. on_draw is called when the window needs to be redrawn. Note that we no longer need a decorator statement such as @win.event above the on_draw method because the window API method is simply overridden by this inherited class. The constructor of the class SingleImageAnimation is as follows: 1 def __init__(self, width=None, height=None):2 pyglet.window.Window.__init__(self,3 width=width,4 height=height,5 resizable = True)6 self.drawableObjects = []7 self.rising = False8 self.ballSprite = None9 self.createDrawableObjects()10 self.adjustWindowSize() As mentioned earlier, the class SingleImageAnimation inherits pyglet.window.Window. However, its constructor doesn't take all the arguments supported by its super class. This is because we don't need to change most of the default argument values. If you want to extend this application further and need these arguments, you can do so by adding them as __init__ arguments. The constructor initializes some instance variables and then calls methods to create the animation sprite and resize the window respectively. The method createDrawableObjects creates a sprite instance using the ball.png image. 1 def createDrawableObjects(self):2 """3 Create sprite objects that will be drawn within the4 window.5 """6 ball_img= pyglet.image.load('images/ball.png')7 ball_img.anchor_x = ball_img.width / 28 ball_img.anchor_y = ball_img.height / 2910 self.ballSprite = pyglet.sprite.Sprite(ball_img)11 self.ballSprite.position = (12 self.ballSprite.width + 100,13 self.ballSprite.height*2 - 50)14 self.drawableObjects.append(self.ballSprite) The anchor_x and anchor_y properties of the image instance are set such that the image has an anchor exactly at its center. This will be useful while rotating the image later. On line 10, the sprite instance self.ballSprite is created. Later, we will be setting the width and height of the Pyglet window as twice of the sprite width and thrice of the sprite height. The position of the image within the window is set on line 11. The initial position is chosen as shown in the next screenshot. In this case, there is only one Sprite instance. However, to make the program more general, a list of drawable objects called self.drawableObjects is maintained. To continue the discussion from the previous step, we will now review the on_draw method. def on_draw(self): self.clear() for d in self.drawableObjects: d.draw() As mentioned previously, the on_draw function is an API method of class pyglet.window.Window that is called when a window needs to be redrawn. This method is overridden here. The self.clear() call clears the previously drawn contents within the window. Then, all the Sprite objects in the list self.drawableObjects are drawn in the for loop. The preceding image illustrates the initial ball position in the animation. The method adjustWindowSize sets the width and height parameters of the Pyglet window. The code is self-explanatory: def adjustWindowSize(self): w = self.ballSprite.width * 3 h = self.ballSprite.height * 3 self.width = w self.height = h So far, we have set up everything for the animation to play. Now comes the fun part. We will change the position of the sprite representing the image to achieve the animation effect. During the animation, the image will also be rotated, to give it the natural feel of a bouncing ball. 1 def moveObjects(self, t):2 if self.ballSprite.y - 100 < 0:3 self.rising = True4 elif self.ballSprite.y > self.ballSprite.height*2 - 50:5 self.rising = False67 if not self.rising:8 self.ballSprite.y -= 59 self.ballSprite.rotation -= 610 else:11 self.ballSprite.y += 512 self.ballSprite.rotation += 5 This method is scheduled to be called 20 times per second using the following code in the program. pyglet.clock.schedule_interval(win.moveObjects, 1.0/20) To start with, the ball is placed near the top. The animation should be such that it gradually falls down, hits the bottom, and bounces back. After this, it continues its upward journey to hit a boundary somewhere near the top and again it begins its downward journey. The code block from lines 2 to 5 checks the current y position of self.ballSprite. If it has hit the upward limit, the flag self.rising is set to False. Likewise, when the lower limit is hit, the flag is set to True. The flag is then used by the next code snippet to increment or decrement the y position of self.ballSprite. The highlighted lines of code rotate the Sprite instance. The current rotation angle is incremented or decremented by the given value. This is the reason why we set the image anchors, anchor_x and anchor_y at the center of the image. The Sprite object honors these image anchors. If the anchors are not set this way, the ball will be seen wobbling in the resultant animation. Once all the pieces are in place, run the program from the command line as: $python SingleImageAnimation.py This will pop up a window that will play the bouncing ball animation. The next illustration shows some intermediate frames from the animation while the ball is falling down. What just happened? We learned how to create an animation using just a single image. The image of a ball was represented by a sprite instance. This sprite was then translated and rotated on the screen to accomplish a bouncing ball animation. The whole functionality, including the event handling, was encapsulated in the class SingleImageAnimation.
Read more
  • 0
  • 0
  • 13658

article-image-python-multimedia-working-audios
Packt
30 Aug 2010
14 min read
Save for later

Python Multimedia: Working with Audios

Packt
30 Aug 2010
14 min read
(For more resources on Python, see here.) So let's get on with it! Installation prerequisites Since we are going to use an external multimedia framework, it is necessary to install the necessary to install the packages mentioned in this section. GStreamer GStreamer is a popular open source multimedia framework that supports audio/video manipulation of a wide range of multimedia formats. It is written in the C programming language and provides bindings for other programming languages including Python. Several open source projects use GStreamer framework to develop their own multimedia application. Throughout this article, we will make use of the GStreamer framework for audio handling. In order to get this working with Python, we need to install both GStreamer and the Python bindings for GStreamer. Windows platform The binary distribution of GStreamer is not provided on the project website http://www.gstreamer.net/. Installing it from the source may require considerable effort on the part of Windows users. Fortunately, GStreamer WinBuilds project provides pre-compiled binary distributions. Here is the URL to the project website: http://www.gstreamer-winbuild.ylatuya.es The binary distribution for GStreamer as well as its Python bindings (Python 2.6) are available in the Download area of the website: http://www.gstreamer-winbuild.ylatuya.es/doku.php?id=download You need to install two packages. First, the GStreamer and then the Python bindings to the GStreamer. Download and install the GPL distribution of GStreamer available on the GStreamer WinBuilds project website. The name of the GStreamer executable is GStreamerWinBuild-0.10.5.1.exe. The version should be 0.10.5 or higher. By default, this installation will create a folder C:gstreamer on your machine. The bin directory within this folder contains runtime libraries needed while using GStreamer. Next, install the Python bindings for GStreamer. The binary distribution is available on the same website. Use the executable Pygst-0.10.15.1-Python2.6.exe pertaining to Python 2.6. The version should be 0.10.15 or higher. GStreamer WinBuilds appears to be an independent project. It is based on the OSSBuild developing suite. Visit http://code.google.com/p/ossbuild/ for more information. It could happen that the GStreamer binary built with Python 2.6 is no longer available on the mentioned website at the time you are reading this book. Therefore, it is advised that you should contact the developer community of OSSBuild. Perhaps they might help you out! Alternatively, you can build GStreamer from source on the Windows platform, using a Linux-like environment for Windows, such as Cygwin (http://www.cygwin.com/). Under this environment, you can first install dependent software packages such as Python 2.6, gcc compiler, and others. Download the gst-python-0.10.17.2.tar.gz package from the GStreamer website http://www.gstreamer.net/. Then extract this package and install it from sources using the Cygwin environment. The INSTALL file within this package will have installation instructions. Other platforms Many of the Linux distributions provide GStreamer package. You can search for the appropriate gst-python distribution (for Python 2.6) in the package repository. If such a package is not available, install gst-python from the source as discussed in the earlier the Windows platform section. If you are a Mac OS X user, visit http://py26-gst-python.darwinports.com/. It has detailed instructions on how to download and install the package Py26-gst-python version 0.10.17 (or higher). Mac OS X 10.5.x (Leopard) comes with the Python 2.5 distribution. If you are using packages using this default version of Python, GStreamer Python bindings using Python 2.5 are available on the darwinports website: http://gst-python.darwinports.com/ PyGobject There is a free multiplatform software utility library called 'GLib'. It provides data structures such as hash maps, linked lists, and so on. It also supports the creation of threads. The 'object system' of GLib is called GObject. Here, we need to install the Python bindings for GObject. The Python bindings are available on the PyGTK website at: http://www.pygtk.org/downloads.html. Windows platform The binary installer is available on the PyGTK website. The complete URL is: http://ftp.acc.umu.se/pub/GNOME/binaries/win32/pygobject/2.20/?. Download and install version 2.20 for Python 2.6. Other platforms For Linux, the source tarball is available on the PyGTK website. There could even be binary distribution in the package repository of your Linux operating system. The direct link to the Version 2.21 of PyGObject (source tarball) is: http://ftp.gnome.org/pub/GNOME/sources/pygobject/2.21/ If you are a Mac user and you have Python 2.6 installed, a distribution of PyGObject is available at http://py26-gobject.darwinports.com/. Install version 2.14 or later. Summary of installation prerequisites The following table summarizes the packages needed for this article. Package Download location Version Windows platform Linux/Unix/OS X platforms GStreamer http://www.gstreamer.net/ 0.10.5 or later Install using binary distribution available on the Gstreamer WinBuild website: http://www.gstreamer-winbuild.ylatuya.es/doku.php?id=download Use GStreamerWinBuild-0.10.5.1.exe (or later version if available). Linux: Use GStreamer distribution in package repository. Mac OS X: Download and install by following instructions on the website: http://gstreamer.darwinports.com/. Python Bindings for GStreamer http://www.gstreamer.net/ 0.10.15 or later for Python 2.6 Use binary provided by GStreamer WinBuild project. See http://www.gstreamer-winbuild.ylatuya.es for details pertaining to Python 2.6. Linux: Use gst-python distribution in the package repository. Mac OS X: Use this package (if you are using Python2.6): http://py26-gst-python.darwinports.com/. Linux/Mac: Build and install from the source tarball. Python bindings for GObject "PyGObject" Source distribution: http://www.pygtk.org/downloads.html 2.14 or later for Python 2.6 Use binary package from pygobject-2.20.0.win32-py2.6.exe Linux: Install from source if pygobject is not available in the package repository. Mac: Use this package on darwinports (if you are using Python2.6) See http://py26-gobject.darwinports.com/ for details. Testing the installation Ensure that the GStreamer and its Python bindings are properly installed. It is simple to test this. Just start Python from the command line and type the following: >>>import pygst If there is no error, it means the Python bindings are installed properly. Next, type the following: >>>pygst.require("0.10")>>>import gst If this import is successful, we are all set to use GStreamer for processing audios and videos! If import gst fails, it will probably complain that it is unable to work some required DLL/shared object. In this case, check your environment variables and make sure that the PATH variable has the correct path to the gstreamer/bin directory. The following lines of code in a Python interpreter show the typical location of the pygst and gst modules on the Windows platform. >>> import pygst>>> pygst<module 'pygst' from 'C:Python26libsite-packagespygst.pyc'>>>> pygst.require('0.10')>>> import gst>>> gst<module 'gst' from 'C:Python26libsite-packagesgst-0.10gst__init__.pyc'> Next, test if PyGObject is successfully installed. Start the Python interpreter and try importing the gobject module. >>import gobject If this works, we are all set to proceed! A primer on GStreamer In this article, we will be using GStreamer multimedia framework extensively. Before we move on to the topics that teach us various audio processing techniques, a primer on GStreamer is necessary. So what is GStreamer? It is a framework on top of which one can develop multimedia applications. The rich set of libraries it provides makes it easier to develop applications with complex audio/video processing capabilities. Fundamental components of GStreamer are briefly explained in the coming sub-sections. Comprehensive documentation is available on the GStreamer project website. GStreamer Application Development Manual is a very good starting point. In this section, we will briefly cover some of the important aspects of GStreamer. For further reading, you are recommended to visit the GStreamer project website: http://www.gstreamer.net/documentation/ gst-inspect and gst-launch We will start by learning the two important GStreamer commands. GStreamer can be run from the command line, by calling gst-launch-0.10.exe (on Windows) or gst-launch-0.10(on other platforms). The following command shows a typical execution of GStreamer on Linux. We will see what a pipeline means in the next sub-section. $gst-launch-0.10 pipeline_description GStreamer has a plugin architecture. It supports a huge number of plugins. To see more details about any plugin in your GStreamer installation, use the command gst-inspect-0.10 (gst-inspect-0.10.exe on Windows). We will use this command quite often. Use of this command is illustrated here. $gst-inspect-0.10 decodebin Here, decodebin is a plugin. Upon execution of the preceding command, it prints detailed information about the plugin decodebin. Elements and pipeline In GStreamer, the data flows in a pipeline. Various elements are connected together forming a pipeline, such that the output of the previous element is the input to the next one. A pipeline can be logically represented as follows: Element1 ! Element2 ! Element3 ! Element4 ! Element5 Here, Element1 through to Element5 are the element objects chained together by the symbol !. Each of the elements performs a specific task. One of the element objects performs the task of reading input data such as an audio or a video. Another element decodes the file read by the first element, whereas another element performs the job of converting this data into some other format and saving the output. As stated earlier, linking these element objects in a proper manner creates a pipeline. The concept of a pipeline is similar to the one used in Unix. Following is a Unix example of a pipeline. Here, the vertical separator | defines the pipe. $ls -la | more Here, the ls -la lists all the files in a directory. However, sometimes, this list is too long to be displayed in the shell window. So, adding | more allows a user to navigate the data. Now let's see a realistic example of running GStreamer from the command prompt. $ gst-launch-0.10 -v filesrc location=path/to/file.ogg ! decodebin ! audioconvert ! fakesink For a Windows user, the gst command name would be gst-launch-0.10.exe. The pipeline is constructed by specifying different elements. The !symbol links the adjacent elements, thereby forming the whole pipeline for the data to flow. For Python bindings of GStreamer, the abstract base class for pipeline elements is gst.Element, whereas gst.Pipeline class can be used to created pipeline instance. In a pipeline, the data is sent to a separate thread where it is processed until it reaches the end or a termination signal is sent. Plugins GStreamer is a plugin-based framework. There are several plugins available. A plugin is used to encapsulate the functionality of one or more GStreamer elements. Thus we can have a plugin where multiple elements work together to create the desired output. The plugin itself can then be used as an abstract element in the GStreamer pipeline. An example is decodebin. We will learn about it in the upcoming sections. A comprehensive list of available plugins is available at the GStreamer website http://gstreamer.freedesktop.org. In almost all applications to be developed, decodebin plugin will be used. For audio processing, the functionality provided by plugins such as gnonlin, audioecho, monoscope, interleave, and so on will be used. Bins In GStreamer, a bin is a container that manages the element objects added to it. A bin instance can be created using gst.Bin class. It is inherited from gst.Element and can act as an abstract element representing a bunch of elements within it. A GStreamer plugin decodebin is a good example representing a bin. The decodebin contains decoder elements. It auto-plugs the decoder to create the decoding pipeline. Pads Each element has some sort of connection points to handle data input and output. GStreamer refers to them as pads. Thus an element object can have one or more "receiver pads" termed as sink pads that accept data from the previous element in the pipeline. Similarly, there are 'source pads' that take the data out of the element as an input to the next element (if any) in the pipeline. The following is a very simple example that shows how source and sink pads are specified. >gst-launch-0.10.exe fakesrc num-bufferes=1 ! fakesink The fakesrc is the first element in the pipeline. Therefore, it only has a source pad. It transmits the data to the next linkedelement, that is fakesink which only has a sink pad to accept elements. Note that, in this case, since these are fakesrc and fakesink, just empty buffers are exchanged. A pad is defined by the class gst.Pad. A pad can be attached to an element object using the gst.Element.add_pad() method. The following is a diagrammatic representation of a GStreamer element with a pad. It illustrates two GStreamer elements within a pipeline, having a single source and sink pad. Now that we know how the pads operate, let's discuss some of special types of pads. In the example, we assumed that the pads for the element are always 'out there'. However, there are some situations where the element doesn't have the pads available all the time. Such elements request the pads they need at runtime. Such a pad is called a dynamic pad. Another type of pad is called ghost pad. These types are discussed in this section.   Dynamic pads Some objects such as decodebin do not have pads defined when they are created. Such elements determine the type of pad to be used at the runtime. For example, depending on the media file input being processed, the decodebin will create a pad. This is often referred to as dynamic pad or sometimes the available pad as it is not always available in elements such as decodebin. Ghost pads As stated in the Bins section a bin object can act as an abstract element. How is it achieved? For that, the bin uses 'ghost pads' or 'pseudo link pads'. The ghost pads of a bin are used to connect an appropriate element inside it. A ghost pad can be created using gst.GhostPad class. Caps The element objects send and receive the data by using the pads. The type of media data that the element objects will handle is determined by the caps (a short form for capabilities). It is a structure that describes the media formats supported by the element. The caps are defined by the class gst.Caps. Bus A bus refers to the object that delivers the message generated by GStreamer. A message is a gst.Message object that informs the application about an event within the pipeline. A message is put on the bus using the gst.Bus.gst_bus_post() method. The following code shows an example usage of the bus. 1 bus = pipeline.get_bus()2 bus.add_signal_watch()3 bus.connect("message", message_handler) The first line in the code creates a gst.Bus instance. Here the pipeline is an instance of gst.PipeLine. On the next line, we add a signal watch so that the bus gives out all the messages posted on that bus. Line 3 connects the signal with a Python method. In this example, the message is the signal string and the method it calls is message_handler. Playbin/Playbin2 Playbin is a GStreamer plugin that provides a high-level audio/video player. It can handle a number of things such as automatic detection of the input media file format, auto-determination of decoders, audio visualization and volume control, and so on. The following line of code creates a playbin element. playbin = gst.element_factory_make("playbin") It defines a property called uri. The URI (Uniform Resource Identifier) should be an absolute path to a file on your computer or on the Web. According to the GStreamer documentation, Playbin2 is just the latest unstable version but once stable, it will replace the Playbin. A Playbin2 instance can be created the same way as a Playbin instance. gst-inspect-0.10 playbin2 With this basic understanding, let us learn about various audio processing techniques using GStreamer and Python.
Read more
  • 0
  • 0
  • 4728
Modal Close icon
Modal Close icon