ImageMagick is the sort of thing I cannot fathom. It seems to me that it follows
this sort of idea:
{binaryname} [global options] <filename> [[operator or global options] <other filename>, ...] <output filename>
That is, you choose the command from the ImageMagick suite that you're going to
use as your binaryname, then at the most basic level you list all the filenames
you are using as inputs and finally the filename you want as output.
Before the filenames you can set things like gravity or background, which apply
to the entire canvas at the time (the canvas can be resized independently of the
image, as with any image editor), and after each filename you can specify an
operator, which will perform an operation on the entire in-memory image and the
file specified.
Basically I have discovered that you need to put the operations *after* the
image filename, not before. That way, you can string multiple operations to
multiple files and produce a single output file, which goes on the end.
Just yesterday I got a new monitor and it is widescreen. Its vertical pixel
size is lower than the old one and its horizontal is larger. This posed a
problem to my script, because my script basically just tiles two images together.
With the new monitor setup, the images will no longer be centred.
The background switcher you get with KDE doesn't feature in Gnome. Instead you
have to set up a cron script. Originally I used the montage program from
the ImageMagick suite, which worked quite simply like this:
This was part of a script that took two filenames from my wallpapers directory,
joined them together, and set them as my desktop background. I had to do this
because in Gnome there is no way of setting a different desktop background for
each monitor. So I create a single background as wide as both monitors and use
that.
The script sets the geometry to use for all images to 1280x1024>. The
> means that the geometry is only applied if the current geometry is
greater in either dimension than the one specified. The geometry argument itself
will resize the image; thus we only resize images that are bigger than the screen.
Here is my finished background-changer cron script.
I have left it up to the reader to decide how to find the two image filenames
to join together, and also to determine what the wallpaper filenames will be.
I use $HOME/wallpaper.png as the wallpaper itself and
$HOME/wallpaper.tmp.png for the file output by ImageMagick.
The script works by using magick. Instead of an image filename you can provide
a section in parentheses that works with another image (or the same image again),
and outputs a virtual image that is then used as though you had given it a
filename.
Here you can see that the first background image is centred (-gravity center)
for the operations that follow. Those operations are first to shrink the image
if either dimension exceeds the monitor size, as we did in the original script
above. Then the -extent option sets the canvas to the size of the monitor.
Note that in each case we use -background none to make sure the extra
canvas is transparent.
This then ensures that there is a virtual image the size of the monitor. Its
background is transparent, and it has an image in the exact centre. The image is
shrunk to fit the monitor if necessary.
We do this twice, once for each monitor. Two virtual canvases are floating in
memory. Then we simply set our gravity to 'center' again for the main canvas,
set the background to 'none' again, and append the first image to the second.
If we don't set the gravity to center, the two images will have their uppermost
edges aligned by default. A bit of trial and error shows that the whole wallpaper
will be centred vertically on the desktop, so we use the gravity option to make
sure that the individual wallpapers are also centred vertically. You can use
this knowledge to do various other things with the images, in case you were not
intending to use it as a wallpaper changer in the first place.
You can use the command convert <filename> <filename> +append to simply stick
one image to the other, horizontally. We have replaced the <filename>s with
virtual canvases and added a couple of controls to make sure the output is nice.
The option -append sticks them together vertically.
Here you can see the complete script, including a stop file which you can
create (touch ~/.nodesktopchange) in order to prevent the image magick
happening. This is advisable if you're doing something important because the
process is fairly processor-intensive.
Note the addition of the compiz sections. This is supposed to change the skydome
at the same time but it doesn't seem to have the desired effect. I left it in
anyway for some reason.
The wallpaper-cache file is simply a text file containing a list of all
the images available, meaning the script doesn't have to search the entire
wallpapers directory for two images to use.
jQuery is the Javascript library to use these days. Ain't nothin' it can't do.
In this series we will look at the plugins and core things it can do that make
your own Javascript code nothing but one(ish)-liners.
This series was originally going to be entitled "jQuery *One-Liners* You Should
Know About", but unfortunately one line of Javascript is what we call a mess,
so we're going to allow ourselves a few lines.
Plus, the shortest version is only a line or two long, but many jQuery things
can take a whole bunch of parameters and options, so yours could end up long.
But, in spirit, it is one line; one function; a short piece of code.
(I have cut out the $(function(){}) from here. Newcomers to jQuery should be
aware of this function from the
last time
we talked about jQuery. It simply runs the function as soon as the document is
ready to be modified.)
The HTML:
<input type="button" id="togglebut" value="Click"><div id="hiddendiv" style="display:none; border: 1px solid grey;"><p>Lo and behold! A div!</p></div>
As you can see, the way I specified this behaviour was first to find the button
using the jQuery CSS-style selector $('#togglebut'), and then to call the
click function on that.
Remember that if your selector finds more than one element, the click function
will be applied to them all. This can be either a hint or a warning, depending
on what you want.
Anyway, the click function takes a function as its parameter. This parameter
is the function that will be run when the item is clicked on. In our function,
we have used the same selector style to find the div, and then called toggle
on that. The parameter 'fast' is optional and determines the speed at which the
div is toggled. You can use 'fast', 'slow' or any number of milliseconds as the
duration.
This use of toggle can also be given a function as the second parameter, after
the speed parameter. If you do, you will find that it is run when the object has
been displayed. Observe:
<input type="button" id="togglebut2" value="Click"><div id="hiddendiv2" style="display:none; border: 1px solid grey;"><p>Lo and behold! A div!</p></div>
All I did was change the HTML IDs so that the jQuery selectors found the second
edition, and added the function to the toggle() call.
The other useful toggle
That is not all toggle does. If you provide it with multiple functions, it will
run each function in turn every time it is clicked, until all the functions
have been run, when it starts again.
Click me!
Let's look at the code for the above.
Again, note that this code is put in
the document ready function, $(function(){}). We won't mention this again,
as it is assumed that you know where you want it to go. Just because it is
common that you will set up your click bindings as soon as possible, does not
mean you don't want to set them up as the result of some other operation!
This edition of toggle only works for clicking, and can take as many functions
as you fancy, cycling through them as you click.
It is at this point we lament a stylistic misfortune of Javascript. You see, in
Javascript, functions are data types too. As we have seen, we can pass a
whole function into another function as a parameter. It does not pass in the
return value from the function: it passes in the function itself! Because this
is such a powerful and useful tool, it is used a lot in jQuery, as we have seen.
Callback functions and functions as a list of things to do, in order. The
document ready function takes a function as a thing to do when the document is
ready. So it is a shame that the syntax for creating a function is so verbose.
Not only does it require the keyword 'function', but it has a formal parameter
list, which, by the way, can be given parameters that your function expects.
This all culminates in jQuery code being very simple to write but a bit of an
eyestrain to read because of the many levels of indentation it creates. But
with the power it offers, your web page is plain old HTML without it.
Stylesheets seem to be a non-trivial problem with javascript. The problem I
found was that even though HTML provisions for the "alternate stylesheet" value
to the rel attribute, browsers do not tend to pay attention when this
changes.
I therefore cobbled together a quick demonstration of how to change your site's
skin with some simple jQuery. I wanted to do this so that I could have a
work-in-progress stylesheet but easily switch to the live stylesheet so I could
also see what new things will look like when they go live.
However, it is not beyond the realms of feasibility that you might actually
just have more than one stylesheet and they all work and you'd like to allow
the visitor to select their preferred skin without reloading the page.
Let's say you have two stylesheets called /static/main.css and
/static/wip.css. The easiest way to allow the switch between these is to
first set up the <link> for the main stylesheet, and then to duplicate the
href to the stylesheet in another <link> tag that is specified as an
alternate stylesheet. Set up the other as another alternate stylesheet, and
give both alternate stylesheets a title:
This is repeating yourself a bit, but if you think about it semantically, these
are all actually alternate stylesheets, and one of them just happens to be the
current stylesheet.
Next, I set up a single link to do the switching. You might prefer to have one
link per stylesheet, or a link that toggles. Either way, give all the links the
same class name so we can recognise them.
Observe that the rel in the <a> matches the title in the
link.
I made mine toggle since there are only two stylesheets. This same bit of jQuery
should work regardless of how many links you use, and it will make the link you
used switch back again.
Line 1 should be recognisable to anyone but those new to jQuery as the
document-ready function. The function we run here ends on line 9, so
we won't mention line 9 again.
This function is run when the document is ready to be
played with - probably the most useful event in the page event cycle, and
certainly the most useful feature of jQuery. (Getting the document ready event
without jQuery is a nightmare, thanks to IE.)
Line 2 assigns a function to be run whenever a matching link is clicked. I have
used the class ss-switch to denote such a link.
The jQuery selector a.ss-switch means the same as the CSS selector: all a
elements with the class ss-switch. Change this if you want a different class
name. Every matched element has its click handler set to the given function,
which starts here and ends on line 8.
The magic of Javascript is that inside the click handler, the keyword this
will refer to the element that we clicked on when this function is run. That
means that it is possible to create this generic function and assign it to
several elements: we can use this inside the function to find out at
runtime which one we are actually using.
On line 3 we get the href of the current stylesheet. We'll use this later to
find the title of this stylesheet in the alternate stylesheets. For this reason
you should make sure that you have created an alternate stylesheet with the same
href as the default one.
On line 4 we can see this funky combination of stuff. Briefly, this function
finds the URL of the alternate stylesheet that matches the rel of the
link we clicked.
Let's pick it apart. First
we see $(this).attr('rel') buried deep in the middle. This will return the
rel attribute of the link we clicked (this). We put the $() around it to
make sure it is a jQuery object. It should be, but this construct will not do
any harm. The rel of the href is inserted using string concatenation into
'link[title=foo]', which is then used in $()again, resulting in a jQuery
handle on the alternate stylesheet whose title matches the rel of the link we
clicked. On this we can call attr('href') to get the URL of the stylesheet we
are changing to, and save this in the new_sheet variable.
On line 5, we set the href attribute of the stylesheet to be the new one. The
effect of this should be immediately apparent, but be aware that it may cause a
brief second where the browser has not yet got that stylesheet and the browser's
default stylesheet shows through. You might want to consider using AJAX to load
it, and then swap it out when you have done so. Or something.
Then on line 6 we reverse the idea that we had for line 4, and swap the rel
of the link with the title of the stylesheet we are swapping from.
We use the curr_sheet variable we set up to find the <link>
tag based on its href.
There should be only one link tag with the same href as the original stylesheet,
so it should be trivial to find it. We take the title tag of that link tag and
set it to be the rel attribute of the link we clicked. That means that the
next time we click the link, the whole process should go around again, except
this time the other way around.
Most importantly, on line 7 we return false. This stops the browser following
the link on the <a> tag in the first place. You can use this behaviour to
set up a default for people without Javascript enabled: simply set instead of
the # as the href some URL that will send the browser to the same page with
the selected CSS as the default.
A blog about things that are interesting. Alastair McGowan-Douglas and his
wife Dee often stumble upon things on the internet that other people should
know about and write it down so that then they do.
If you like my blog and want to help, I would very much appreciate something
from my Amazon wishlist!
If you don't like my blog and want to help, I would very much appreciate
feedback to al@podcats.in. In fact, if you want to leave feedback of any sort,
send it along!