Filter: hardware,software, code, computer science, tech, life, robotics

Qt Manual/Fixed Layout Manager ("QManualLayout")

I still question whether I missed something very obvious, but it seems difficult to manually layout widgets in the Qt Framework, and there isn't any provided QManualLayout layout manager. Below you will find my implementation of one. And yes, there are plenty of special cases where you might need one. Using my layout manager you can place widgets anywhere on the layout using the common widget methods such as move(), resize(), and setGeometry().

/*******************************************************************
**
** Qt Manual/Fixed Layout Manager ("QManualLayout")
** ManualLayout.h
** v1.0
**
** This layout manager lets you easily programmatically place
** widgets in a fixed/manual method. Widgets can be placed
** in a fixed method and dynamically moved on the layout manager's
** widget resizeEvent(QResizeEvent*) event.
**
** Usage:
**
** this->setLayout(new ManualLayout());
** QWidget *widget = new QWidget(this);
** widget->setGeometry(10,10,200,100);
**
** Copyright 2012 Dan Krusi, Nerves Design & Engineering
** dan.krusi@nerves.ch | www.dankrusi.com | www.nerves.ch
**
*******************************************************************/

#ifndef MANUALLAYOUT_H
#define MANUALLAYOUT_H

#include <QtGui>
#include <QList>

class ManualLayout : public QLayout {

private:
QList<QLayoutItem*> list;

public:
ManualLayout(QWidget *parent): QLayout(parent) {}
ManualLayout(): QLayout() {}
~ManualLayout();

public:
int count() const;
void addItem(QLayoutItem *item);
QSize sizeHint() const;
QSize minimumSize() const;
QLayoutItem *itemAt(int) const;
QLayoutItem *takeAt(int);
void setGeometry(const QRect &rect);
};

#endif // MANUALLAYOUT_H

/*******************************************************************
**
** Qt Manual/Fixed Layout Manager ("QManualLayout")
** ManualLayout.cpp
** v1.0
**
** This layout manager lets you easily programmatically place
** widgets in a fixed/manual method. Widgets can be placed
** in a fixed method and dynamically moved on the layout manager's
** widget resizeEvent(QResizeEvent*) event.
**
** Usage:
**
** this->setLayout(new ManualLayout());
** QWidget *widget = new QWidget(this);
** widget->setGeometry(10,10,200,100);
**
** Copyright 2012 Dan Krusi, Nerves Design & Engineering
** dan.krusi@nerves.ch | www.dankrusi.com | www.nerves.ch
**
*******************************************************************/

#include "ManualLayout.h"

int ManualLayout::count() const {
return list.size();
}

QLayoutItem *ManualLayout::itemAt(int idx) const {
return list.value(idx);
}

QLayoutItem *ManualLayout::takeAt(int idx) {
return idx >= 0 && idx < list.size() ? list.takeAt(idx) : 0;
}

void ManualLayout::addItem(QLayoutItem *item) {
list.append(item);
}

ManualLayout::~ManualLayout() {
QLayoutItem *item;
while ((item = takeAt(0))) delete item;
}

void ManualLayout::setGeometry(const QRect &r) {
QLayout::setGeometry(r);
}

QSize ManualLayout::sizeHint() const {
QSize s(0,0);
int n = list.count();
int i = 0;
while (i < n) {
s = s.expandedTo(list.at(i)->sizeHint());
i++;
}
return s;
}

QSize ManualLayout::minimumSize() const {
QSize s(0,0);
int n = list.count();
int i = 0;
while (i < n) {
s = s.expandedTo(list.at(i)->minimumSize());
i++;
}
return s;
}

You can download a demo project including the source here:

Source Download: ManualLayout_src.zip

Tags: code

ConvertX for ImageMagick

I use ImageMagick to convert images on a daily basis. It's rarely failed me with missing features or problems, but one thing that really bothers me is the way convert handles batch conversion of *.ext files on Unix. Other than no being able to easily change/append/prepend filenames, the greatest problem is that all images are done in one process. That means if I convert 200 images, they are all done in one convert process. That's why I wrote ConvertX, which simplifies this process for me and splits up batch jobs into separate commands.

Once installed, the command convertx works just like convert, except there are added arguments and variables that can be used.

Example:

Let's say I have three PNG images (A.png, B.png and C.png) and want to convert them to low quality JPG images with an appended "low" to the filename. With convertx I can do this easily:

convertx "*.png" -quality 80 "%f_low.jpg"

The command is then split up into the following convert commands:

convert A.png -quality 80 "A_low.jpg"
convert B.png -quality 80 "B_low.jpg"
convert C.png -quality 80 "C_low.jpg"

Here is the full source code:

#!/bin/sh

# Grab files to convert...
FILES=$1
shift
echo $@
ARGUMENTS=$@

# Loop files
for FILE in $FILES
do

# Extract filename
FILENAME=`echo $FILE | sed 's/\(.*\)\..*/\1/'`

# Replace filename variable...
ARGUMENTS_PROCESSED=`echo $@ | sed 's/\%f/'$FILENAME'/'`

# Do conversion
echo "convert $FILE $ARGUMENTS_PROCESSED"
convert $FILE $ARGUMENTS_PROCESSED

done

You can also download a ZIP file with the script and accompanying setup/install script:

Source Code: ConvetX.zip

Tags: code

Stretching the Timeline in Flash

One of the greatest lacking features in Adobe Flash is the ability to stretch the timeline. I find this an unbelievable missing feature. I assume the reason it is missing is because of the technical questions that arise when stretching the timeline: mainly that which has to do with rounding errors. Because flash is keyframe oriented, rounding errors can become sorely visible when performing stretch operations. A difficult problem to solve universally. However, in many cases these errors can be ignored. Below is my JSFL script which will stretch the selected frames on the timeline by any given factor.

//
// Stretch Timeline Flash Extension Command
// v2
//
// This extension will automatically stretch the selected frames
// to the given factor of the currently selected timeline.
//
// Compatible with Adobe Flash CS3/CS4.
//
// Based on Reineir Feijen's Flash Extensions. Contributions by Nitin Malik.
//
// Copyright 2011 Dan Krusi, Nerves
// dan.krusi@nerves.ch | www.nerves.ch
//

// Init
var valid = true;
var factor = 1.0;

// Sanity check and factor input
if (fl.getDocumentDOM() == null || fl.getDocumentDOM().getTimeline() == null) {
alert("No timeline is selected!");
} else {
var ret = prompt("Stretch Timeline by a factor. For example, 0.8 would speed up the timeline while 1.4 would slow it down...\n ");
if(ret != null) {
factor = parseFloat(ret);
if (isNaN(factor)) {
alert("The factor '"+ret+"' is not valid!");
} else {
// Rock n Roll!
stretchTimeline(fl.getDocumentDOM().getTimeline(),factor);
}
}
}

// Strech the timeline
function stretchTimeline(timeline_obj, factor) {
// Init
var tl = timeline_obj;
//cleanSelection(tl)
var sel = tl.getSelectedFrames().concat([]);

// Loop selection
for (var i=0;i<sel.length;i+=3) {
var splices = [];
var changed = 0;
var frameCount = tl.layers[ sel[i] ].frameCount;

// Nitin Malik: If startFrame is on empty frame
// Based on fact empty frames can only come after all frames on the layer
if( sel[i+1] > frameCount ) {
sel[i+1] = frameCount;
}

// Nitin Malik: If endFrame is on empty frame
if( sel[i+2] > frameCount ) {
sel[i+2] = frameCount;
}

tl.setSelectedFrames([sel[i],sel[i+1],sel[i+2]]);

// Calculate splices
for (var j=sel[i+1];j<sel[i+2]+1;j++) {
if (j==sel[i+2] || (tl.layers[sel[i]].frames[j] && j==tl.layers[sel[i]].frames[j].startFrame)) {//only keyframes and last frame
var goal = sel[i+1]+Math.round((j-sel[i+1])*factor);
if (factor>=1) {
var df = goal-(j+changed);
var at = j-1+changed;
} else {
var df = (j-changed)-goal;
var at = j-changed;
}
if(df>0) {
splices.push([at, df]);
changed += df;
}
}
}

// Insert/remove necessary frames
if (factor>=1) {
for (var j=0;j<splices.length;j++) {
tl.insertFrames(splices[j][1], false, splices[j][0]);
}
} else {
for (var j=0;j<splices.length;j++) {
tl.removeFrames(splices[j][0]-splices[j][1], splices[j][0]);
}
}
}
}

Download: StretchTimeline_v2.jsfl

Select the Frames to Stretch on the Timeline

Select 'Run Command...' from the Menu

Open the StretchTimeline_v1 Script

Enter the Desired Stretch Factor

Verify the Results

Update: Thanks to Nitin Malik (blog.nitinmalik.com) for contributing some changes to handling empty frames.

Tags: code

Complex Numbers

To better understand complex numbers and how their various functions work, I implemented a templated operator-overloaded C++ class which represents a complex number (complex.h, see below).

typedef ComplexT<double> Complex;

Complex c1(1.0,-1.5); // real=1, imag.=-1.5
Complex c2(M_PI,0);
Complex c3 = (c1 + c2)*2.0;
Complex c4 = (c3 / c2) * c1;
Complex c5 = sqrt(exp(c3)); // sqrt( e^c3 )
Complex c6 = pow(c1,4); // c1^4
double abs = c6.abs(); // radius polar notation
double arg = c6.arg(); // angle polar notation

To test my class, I also implemented a GUI which features an input plane on the left with the output on the right, and various functions to apply to the output. The input can also be changed from the classical colored grid lines, to other inputs such as a radial grid or an image.

Complex Numbers Input/Output Graph

Complex Transformation of an Image

Download Binaries for Windows, Linux, OSX and Full Sorce Code: ComplexNumbers-1.1-win-linux-osx-source.tar.gz

Tags: code, computer science

Zip Library for iPhone SDK

Below you will find the source files for a relatively light-weight zip library for the iPhone SDK including an Objective-C interface. iZip is a simple interface to the zLib library for iPhone OS based on the miniunz application. To use this class you must include all the zLib C files. Just drag the iZip folder into your project. Unzipping an archive requires only a single call: unzipArchiveWithSourcePath:DestPath:.

@interface iZip : NSObject

/*!
@function unzipArchiveWithSourcePath:DestPath:
@abstract Unzips the entire given source file zip to the given destination directory.
@param srcPathParam
@param destPathParam
*/
+ (void) unzipArchiveWithSourcePath:(NSString*)srcPathParam DestPath:(NSString*)destPathParam;

/*!
@function unzipArchiveWithSourcePath:DestPath:Password:FileToExtract:Overwrite:
@abstract Unzips either the entire source file zip or a single file within the source file zip to the given destination directory.
@param srcPathParam
@param destPathParam
@param passwordParam
The password used to decrypt the zip file. Specify NULL if none.
@param fileToExtractParam
The name of the file within the zip file which is to be extracted. Specify NULL to unzip the entire zip file contents.
@param overwriteParam
*/
+ (void) unzipArchiveWithSourcePath:(NSString*)srcPathParam DestPath:(NSString*)destPathParam Password:(NSString*)passwordParam FileToExtract:(NSString*)fileToExtractParam Overwrite:(BOOL)overwriteParam;

@end

Example usage:

#import "iZip.h"
...
// Get the full source path to zip file
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"zip"];

// Get full destination path
NSString *destPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"test"];

// Make sure the destination folder exists
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath]) [[NSFileManager defaultManager] createDirectoryAtPath:destPath withIntermediateDirectories:NO attributes:nil error:NULL];

// Unzip!
[iZip unzipArchiveWithSourcePath:sourcePath DestPath:destPath];

iPhone Zip Library Source: iZip-1.1.zip

Tags: code

Line Scanner-Parser Objective-C

For anyone who needs to quickly and efficiently scan each line in a string stream...

NSString *data = NULL; // Get your string data or stream
NSUInteger startPtr = 0;
NSUInteger lineEndPtr = 0;
NSUInteger contentsEndPtr = 0;
while (startPtr <= data.length) {
[data getLineStart:&startPtr end:&lineEndPtr contentsEnd:&contentsEndPtr forRange:NSMakeRange(startPtr,0)];
NSString *line = [data substringWithRange:NSMakeRange(startPtr, contentsEndPtr-startPtr)];
// Here you have your line!
startPtr = lineEndPtr + 1;
}

Tags: code

Modal View UIAlertView iPhone SDK

The iPhone SDK is quite mature and feature rich, however there are still a number of things missing. One of those is some sort of thread-blocking alert box. Below is the source code for just such a thing. It's a nice solution as it just adds a showModal message to the UIAlertView interface. In addition, there is a class method alertWithTitle:Message: which doesn't require you to create an instance of a UIAlertView - very practical.

/*!
@function alertWithTitle:Message:
@abstract Displays a blocking UIAlertView with a single OK button.
@param title
@param message
*/
+ (void) alertWithTitle:(NSString*)title Message:(NSString*)message;

/*!
@function showModal
@abstract Displays a UIAlertView in a blocking fashion.
*/
- (void) showModal;

Example usage:

// Alert with a title and message
[UIAlertView alertWithTitle:@"Title" Message:@"Message..."];

// Custom view
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Title" message:@"Message..." delegate:NULL cancelButtonTitle:@"OK" otherButtonTitles:NULL];
[alert showModal];
[alert release];

Source Code:

UIAlertViewModal.h

UIAlertViewModal.m

A practical example is as follows, where any exception in your iPhone App is caught and displayed to the user before the App dies...

#import "UIAlertModal.h"

int main(int argc, char *argv[]) {
@try{
// Init app
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
@catch (NSException * e) {
// Catch and display any occuring exception!
[UIAlertView alertWithTitle:@"Error" Message:[NSString stringWithFormat:@"An @. The application will now close.", e.name, e.reason]];
return FALSE;
}
}

Tags: software, code

Eclipse Launcher

I know of several plugins that break the eclipse Workspace Launch dialog. By breaking I mean disabling, and there is no setting in the world to bring it back if you are unwilling to rid your plugin.

Since the eclipse Launch dialog is rudementary anyways, I decided to write my own. An added feature is passing of workspace arguments. More features will probably pop-up as I need them. Below you will find screenshots, binary, and source files.

Launch Window

Workspace Settings

Win32 Binary: EclipseLauncher.zip

Win32 Binary: EclipseLauncher.exe

C++ Source: EclipseLauncher_src.zip

Tags: software, code

Make Any C# Control Class Mutable

It's not always trivial to make any C# Control Class mutable - meaning the user can move and resize the control using the mouse.

My code snippet below shows how you can do this by just copy and pasting the snippet into any class which extends the Control class (or of course any sub-class of Control). Even better, it doesn't mess with any events of the parent container. Pretty neat huh.

/// <summary>
/// Allows simple mutation of any control without intefering with
/// parent container events.
///
/// Just copy the following to any class extending the Control class:
///
/// enum Mutation
/// private Mutation mutation
/// private Point lastLocation
/// private Point originalLocation
/// private const int handleSize
/// protected override void OnMouseDown(MouseEventArgs e)
/// protected override void OnMouseUp(MouseEventArgs e)
/// protected override void OnMouseMove(MouseEventArgs e)
///
/// That's all - you're set!
/// Plug: www.dankrusi.com
/// </summary>
enum Mutation {
None, Move, ResizeE, ResizeW, ResizeN, ResizeS, ResizeNE, ResizeNW, ResizeSE, ResizeSW
}
private Mutation mutation;
private Point lastLocation;
private Point originalLocation;
private const int handleSize = 8;
protected override void OnMouseDown(MouseEventArgs e) {
lastLocation = e.Location;
originalLocation = e.Location;
if (e.X > this.Width - handleSize && e.Y > this.Height - handleSize) {
this.Cursor = Cursors.SizeNWSE;
this.mutation = Mutation.ResizeSE;
} else if (e.X < handleSize && e.Y < handleSize) {
this.Cursor = Cursors.SizeNWSE;
this.mutation = Mutation.ResizeNW;
} else if (e.X > this.Width - handleSize && e.Y < handleSize) {
this.Cursor = Cursors.SizeNESW;
this.mutation = Mutation.ResizeNE;
} else if (e.X < handleSize && e.Y > this.Height - handleSize) {
this.Cursor = Cursors.SizeNESW;
this.mutation = Mutation.ResizeSW;
} else if (e.X > this.Width - handleSize) {
this.Cursor = Cursors.SizeWE;
this.mutation = Mutation.ResizeE;
} else if (e.X < handleSize) {
this.Cursor = Cursors.SizeWE;
this.mutation = Mutation.ResizeW;
} else if (e.Y > this.Height - handleSize) {
this.Cursor = Cursors.SizeNS;
this.mutation = Mutation.ResizeS;
} else if (e.Y < handleSize) {
this.Cursor = Cursors.SizeNS;
this.mutation = Mutation.ResizeN;
} else {
this.Cursor = Cursors.Hand;
this.mutation = Mutation.Move;
}
}
protected override void OnMouseUp(MouseEventArgs e) {
this.mutation = Mutation.None;
this.Cursor = Cursors.Default;
}
protected override void OnMouseMove(MouseEventArgs e) {
if (this.mutation == Mutation.Move) {
int offsetX = this.lastLocation.X + e.Location.X - originalLocation.X - this.Width / 2;
int offsetY = this.lastLocation.Y + e.Location.Y - originalLocation.Y - this.Height / 2;
if (this.Left + offsetX > 0) this.Left += offsetX;
if (this.Top + offsetY > 0) this.Top += offsetY;
Parent.Refresh();
} else if (this.mutation == Mutation.ResizeNE) {
this.Top += e.Y;
this.Height += -e.Y;
this.Width = e.X;
} else if (this.mutation == Mutation.ResizeNW) {
this.Top += e.Y;
this.Height += -e.Y;
this.Left += e.X;
this.Width += -e.X;
} else if (this.mutation == Mutation.ResizeSE) {
this.Height = e.Y;
this.Width = e.X;
} else if (this.mutation == Mutation.ResizeSW) {
this.Height = e.Y;
this.Left += e.X;
this.Width += -e.X;
} else if (this.mutation == Mutation.ResizeE) {
this.Width = e.X;
} else if (this.mutation == Mutation.ResizeW) {
this.Left += e.X;
this.Width += -e.X;
} else if (this.mutation == Mutation.ResizeS) {
this.Height = e.Y;
} else if (this.mutation == Mutation.ResizeN) {
this.Top += e.Y;
this.Height += -e.Y;
} else {
if (e.X > this.Width - handleSize && e.Y > this.Height - handleSize) {
this.Cursor = Cursors.SizeNWSE;
} else if (e.X < handleSize && e.Y < handleSize) {
this.Cursor = Cursors.SizeNWSE;
} else if (e.X > this.Width - handleSize && e.Y < handleSize) {
this.Cursor = Cursors.SizeNESW;
} else if (e.X < handleSize && e.Y > this.Height - handleSize) {
this.Cursor = Cursors.SizeNESW;
} else if (e.X > this.Width - handleSize) {
this.Cursor = Cursors.SizeWE;
} else if (e.X < handleSize) {
this.Cursor = Cursors.SizeWE;
} else if (e.Y > this.Height - handleSize) {
this.Cursor = Cursors.SizeNS;
} else if (e.Y < handleSize) {
this.Cursor = Cursors.SizeNS;
} else {
this.Cursor = Cursors.Hand;
}
}
}

Tags: computer science, code

Minesweeper in a Couple Hours

Inspired by a talk on how one can use the Minesweepers game to perform any given calculation, I started playing minesweeper during the following lectures at school.

I quickly grew tired of the standard windows verison, so I started playing a version by Astatix Software called Crazy Minesweeper. This version features a mode were a mine can have different strengths, making the overal gameplay much more interesting - and challenging.

Crazy Minesweeper by Astatix Software

However, for some obsurd reason Astatix Software requires payment for a full version if you want to play on larger grids with more mines: common - it's minesweeper! I wanted to download a crack to keep playing, but really, its probably easier to implement a version yourself then install a virus infested crack.

After spending a couple hours I came up with my own version, which works really nice. It is implemented in C++ using the Qt framework. Over the past year this combination has definately become my favorite language/framework combination.

Below are some screen shots as well as the source code and Windows binary...

Small Grid

Small Grid

Menu

Custom Game Settings

Elongated Grid

Large Grid

Source Code: CrazyMinesweeper_v1_src.zip

Binary: CrazyMinesweeper_v1_bin_win.zip

If you do not have the Qt Runtime Environment installed, download the following: Binary + Libs: CrazyMinesweeper_v1_bin_win_qt.zip

Tags: computer science, code

Page 1 of 2 Next »