28 May 2010

When Google locked the door

When Google locked the door

Google logo with a door (election doodle)
This is the story of how Google, for a period of three years, locked me out of their groups service, how I eventually found my way back in, and what it cost me.

Yeah, I guess this is a bit off topic regarding to software development. However these days many of us store important data and value in services like Google's. Services with terms like: "Google may stop providing the Services to you at Google’s sole discretion, without prior notice to you". I guess I never took it too seriously, as the companies would probably get seriously bad PR, if they did something like that. Deleting emails for billions. My error was forgetting the case where you are the only person being locked out.

I my case the lock wasn't from email, but from Google Groups. Not as critical as email would have been, but still, well, rather inconvenient. The lockout meant that I was unable to manage the PyChess mailing list. I was unable to fight the, at that time, increasing spam level; and more importantly I couldn't reply anybody in my community.


December 2006 I sat up a Google group for the PyChess project. It made it possible to have a dual web/mailing-list forum for the project, and as we already used Google for a lot of things it was an easy choice. It was easy as magic to set up and start using, and so we did, heavily.
After about a years use, I started feeling a problem. When I wrote mails to the list, no one would answer. And when I answered other peoples post, they seamed to ignore them and press for new answers. As I tried to check the online group to see what was happening, I got a 403 Forbidden error. After a short while I realised that this error was given for any page one the groups.google.com sub domain.

My first thought was naturally to clear all cookies and history and sign off. The last thing helped, but as soon as I signed in to my group the error was back: Username. Password. Enter. 403.
I tried cookie cleaner tools, other browsers and even other peoples computers, but every single time, the result was the same:


Slowly (maybe I was too naive) I began to realize how things were set. I had been locked out. The question was why? and what now? I tried to recall if any action of mine could be linked to the time of the lockout, but no. I hadn't really used other groups than my own, and I had certainly not participated in threads more aggressive than the typical comp.lang.python. I checked my 'recent posts' to see if there had been abuse, but no.
My guess was some kind of database error. If it was an intended ban I hope they'd at least have sent a warning as well as giving a more informative error message. All I saw was the black on white 403.

What now? I couldn't go to the usual fora. The problem was too specific. I had to get to somebody who could actually help me. (Naivly?) I went to the Google homepage looking for something like 'Support'. The page was easy to scan, and I found nothing. There was however an 'About Google' link, which after a few steps lead me to a page named Help.
The help page has been improved a bit since then, but the result is the same. It is made to keep you away from actual supporters and feed you canned or crowd sourced material. Where ever you click, you are sent to a support paged hosted at - you guessed it - groups.google.com. No go.
After a few hours of finding an actual email adress I gave up and went to sleep.

The next morning problems had found me. A group member wrote to me about a spammer, who I, as the admin, was the only one who could kick. I had banned a spammer once before, and it wasn't hard. Well - if I actually had access to the control panel.

I had to go back to the support. I went through the hassle of creating a new account, so I could post to the help forum. I expressed my problems and asked if anybody knew of a way to get to Google.

I didn't get the impression that Google were reading the forum, and as I had expected nobody could really help me.
A guy gave me a link to a help page, that looked a bit different from the others. The page was mostly for people who had forgotten their passwords, which was clear when I had to specify an alternate write back email address. As the error message expressed it: "Please enter an email address, that you have access to". Sigh. But it was my best shot.
As I was well aware, that they were probably busy, I took care to specify my problem as specific as possible, what I thought caused it, and how they could help me. It should be an easy fix.


After a year I received an answer:
10/31/09
Hello,

We've received your report that you're having trouble accessing a Google
product. After completing our investigation, we've found a violation of
our Terms of Service: http://google.com/accounts/TOS -OR- product-specific
program policies:
http://www.google.com/support/accounts/bin/answer.py?answer=147806. As a
result, we're unable to grant you access to this product.

Regards,
The Google Team

Arg. This led nowhere. I instantly asked back for more details, and after a month of no reply I tried with a long begging letter, but neither were ever answered.


At this time I was near the break point. Should I just change to another group service? That would require me to manually migrate any wanted archive material, rebuild the community, and it would take a way some of the nice integration between the services we used.
At that time migration was the only option on
However at that time it was the easiest solution, competing only with one point on my list: Visit the nearest Google office. In Norway.

My laziness postponed the decision for a couple of months, but then suddently something happened. For a long time I had wanted the pychess.org domain name for the project, but it was controlled by 'Pensylvania Youth Chess'. They never had actually got up a website though, and now they gave up and transfered me the domain.
Being a great Google fan, I sat the domain up with Apps for Domains. When I first logged in I saw this:


My eyes were flickering for a bit. Then staring right below the 'Try Premier Edition Free' link: '24/7 Phone support'. What I had been looking for for so long was suddently standing in front of me in a nice blue box.
I knew that Premier Edition would give me Google groups directly for my domain, however the project being voluntary work, I couldn't affort paying 50$ a month per member. But did it matter? No! The trail period should give me plenty of support time, so I could go back using my current group.
After staring amazed at nothing for about a minute I clicked the button.

By an act of magic, the support I had been looking for for 3 years was suddently at my fingertips. All that stood between my and accessing groups.google.com now, was to convince the support team, that unblocking my personal Google account was important for my use of Apps for Domains.
I figured that if I had wanted to switch to the Google groups in Premier Edition, I would've wanted to transfer emails from my old group. For this I would need access. (I don't think there actually is a 'transfer archive to new group' option, but it was the best I could come up with).
At first the support was a bit confused and tried to help me by enabling the groups feature for my domain. (Which was of course already enabled), but after a few mails where I kept insisting, I got a mail from another supporter:

Hello Thomas,

Thank you for confirming your error message. I've manually reset your old admin account, which should resolve this issue.

Please let me know if you still encounter errors, and I will continue troubleshooting.

Sincerely,

[Name]
Enterprise Support


Bing! This was all the magic I needed. Now it worked.
I suppose it took the second supporter about a minute to reset the account, given it is not something they do too often. For me it had been a three years splinter.

This concludes the tutorial: How to get access back to Google.
As the services are free of charge, I never really expected any support options. However I also never really expected the need for them. When things failed, I saw no way to buy the missing support, and the friendly facade suddenly seamed like a tall dark wall.
Perhaps the grief of this issue is in its rareness, but how can we know how often this kind of thing happens? If any admin can lock you all out by a sloppy click, and give you no option to defend yourself, then it is bound to happen once in a while.


Postscript:
A month after my sucess, my bank acc was billed 50$ for a renewal of Premier Apps. This happened regardless of the checked option 'Do not automatically renew my subscription'. I wrote the support in detailes, and they replied to me, that if I didn't want it to happen again, I should check 'Do not automatically renew my subscription'. Doh.

Now that I had paied expensively for the support, I also wanted to use it. So I gave it my best show and wrote back. Eventually luck was on my side:

Hello Thomas,

Thank you for your message. It is our policy that there is not refunds after a charge has been processed. However, under your special circumstance, I have authorized to issue you a refund of your Premier Renewal charges.

You should see the refund in your bank account if a few days. In addition, your account was downgraded to the Standard Edition.

I hope this resolves your issue. Please contact me if you required additional assistance.

Sincerely,

[name]
Enterprise Support

Thank you Google! I never lost trust in you.

Update: Slashdotted


Thanks for all the great comments at http://yro.slashdot.org/story/10/05/31/0151202/Where-Do-You-Go-When-Google-Locks-You-Out . Especially thanks to IamTheRealMike for his insight into Googles strategy and thoughts on these kinds of things.

05 April 2008

PyDock

In the last week I've been working on a better dock than gdl. Gdl is unflexible, broken (at least for python) and ugly. There are still a few problem with widgets having an xwindow, like textviews and treeviews, but I hope to find a way to get it into PyChess Stauton.

04 November 2007

PyChess Philidor beta

After the by far longest PyChess development cycle, version Philidor beta, codenamed 0.8 beta, has been released!

Download from: http://gnomefiles.org/app.php/PyChess

Screenshots: http://pychess.googlepages.com/screenshots

The long development time covers a close to total rewrite, the most throughout testing for a PyChess release yet, and a massive new base of features. Many of which users have been screaming since the first alpha of PyChess.

The new features includes, but are not restricted to:

  • FICS online Internet play.
  • Undo and pause functions.
  • Support for UCI engines like Fruit, Glaurung and Shredder.
  • Ability to turn analysers on/off, and to decide which engines should be used.
  • An "Enter game" in pgn dialog.
  • New fast start greeting screen.
  • A 30x faster built in python engine.
  • Internationalized or figure pieces in notation .
  • Optional sounds.
  • A comments side panel that helps you understand the moves made.
  • Pychess now use Launchpad Rosetta for i18n

It should be noted, however, that even though the FICS implementation is generally very stable, it hasn't yet got support for chatting and console communication. Thus it should be used with some care.

We encourage everyone to try out the release, and report the bugs (if any) you find.

If you'd like to see PyChess translated into your language, you can help us from the web interface at https://translations.launchpad.net/pychess/trunk/+pots/pychess

And remember: "The Game of Chess is not merely an idle amusement; Several very valuable qualities of the mind, useful in the course of human life, are to be acquired and strengthened by it" - Benjamin Franklin, 1779

Thanks, Pychess team

03 September 2007

Update: CSBoard resurected

I tried to download the new csboard 0.8 from http://download.berlios.de/csboard/csboard-0.8.tar.gz. It requires quite a lot of mono libraries, and you have to manually edit some "/home/ravi/cvs/csboard" lines to where ever you unpack it, but other than that, its a simple "make; su -c 'make install'; csboard" install.

After installation it gave me the little unpolished csboard look I remembered. I was also disapointed that even though CSBoard now supports more than one engine, it still has one C# file for each one. I tried looking at them in meld, and they were actually quite different. But I'd watched the movie, and quickly clicked the "Play Online" button. I logged in as a guest, as I wasn't sure how stable it was yet and wanted to save the goodwill on my fics account for testing my own code.

FICS also looked a bit unpolished, but it used an interesting multiline, onecolumn approach for gamelists, as opposed to PyChess' oneline, multicolumn. I'm not sure which one is the best, but a mix might be good. I very much liked the way CSBoard easily lets you show only e.g. "rated games with blitz".

I tried to play a short 2min game, but I forgot the time and ran out. CSBoard displayed "GuestXYZT has fortified by time". I'm not sure if this means CSBoard automatically sent "resign", but it certanly shouldn't kill me before my opponent calls flag.

Even though CSBoard lets you look at the fics communication, it didn't help much as it was inactive during the game. It didn't let me chat either.

The last feature I remembered from the film was the "Game Viewer" which is a chess database. It has features for tagging, searching, opening books and printing and generally seams very nice. Sure some of those features could find their way in to PyChess within in a distant future. All for now though, theming, engine configuration and fics will be the main areas of development.

02 September 2007

CSBoard resurected

Now will you look at this. Just as I was doing some random surfing - looking at other chess clients to "inspire" from - I reached CSBoard at GnomeFiles. The page wasn't updated since 2006, as well as all the other "search:chess" applications on GnomeFiles, so I just thought I'd have a look at the old chess client that inspired me in the first days of PyChess.

Well, when I clicked the screenshots link, instead of getting to some old image, I was sent to the CSBoard homepage in the screencasts section, and I must say I was extraordinary surprised. The client I thought of as old, simple and dead had suddenly got its gui multiplied by 10 and support for ICS, Chess Databases, More engines and you name it.

So definitely something to try out. Tomorrow at least. It looks like they have a pretty good grab on ICS gui.

13 April 2007

0.6.0.beta5 > 0.6

I've always wondered how package managers like yum and apt determines the latest version of a package.

Inside me I've hoped that they used an alternative version name, which simply adds one up for every small update, but most of the time it works, so you don't care.

Yesterday when we released PyChess 0.6, I noticed that after installing it, yum updated it back to 0.6.0.beta5. I didn't really think much about it before the debian packager, Varun Hiremath, noticed me about the problem: 0.6.0.beta5 is considered newer than 0.6.. Sigh.

Now I didn't want to call the new version 0.6.1, so we decided to call it 0.6.0.final, what worked.

Now I just fear how similar problems could totally crash a system, if you use packages from multiple sources..

12 April 2007

Got 0.6 out the door, and still much progress

At last we managed to fix the critical problems in 0.6. It seemed that there was a problem with idle_add on debian/ubuntu kernels, which could eventually be tracked down to the 2.4 -> 2.6 kernel switch. Luckily an easy workaround was pressent.

In the meantime lots of work is going on for the upcoming version of PyChess. Atm. I'm just hacking on this replacement for the smoke/stone start up scene. I hope you like it :)

25 March 2007

New PyChess Icon?

A nice guy calling himself Karlo just filled an issue report attaching an svg for a new PyChess logo.

Always nice to hear from the artists in the free software community, instead of just all of us boring programmers.

27 February 2007

Flags and ImageMagick

This day I was looking for some flags to use for chess engines. A quick locate flag |grep .png gave me a lot of lines like /usr/share/locale/ l10n/us/flag.png. The flags looked like this and were originally created as a part of kdebase.

I quickly colleckted the files by

for i in `ls /usr/share/locale/l10n/`
do
    cp /usr/share/locale/l10n/$i/flag.png $i.png
done

I found out however, that those flags were boring. They also didn't look enough like computers. I used to use this icon: (stock_notebook) for engnines, and I figured that it would be fun to merge the flags and the notebooks.

I tried out stuff in the gimp, and it worked well. Now the problem was that I had 229 flags for which I had to open in gimp, resize, copy to the notebook image and save. A quick calculation based on the amount of time spent on the first flag, showed me that this would take me 1.9 hours. If of course I ever managed to complete it.

This brought up ImageMagick to my mind. ImageMagick which is a command line utility for doing quite a lot of advanced image operations. I found the "Usage guide" and read these two parts of it: resize, composite, which described exactly the functions I needed: Resizing and merging.

In the beginning the guide was looking kinda messy, mostly because of the length of the command lines. However it quickly became clear that it was really very well built, with screenshots all over it, white made it easy to find what I was looking for.

The resize command looked like the following: convert in.png -resize 14x7\! out.png. The ! told ImageMagick that I didn't care about the original aspect of the image, which is necessary since all flags have different formats.

Next the scaled image had to be merged on the notebook image. The command for such an operation was: composite -geometry +5+6 2.png stock_notebook.png 3.png. The geometry option told how much to translate the upper image (2.png) on the lower.

At this point I already had a nearly perfect image. The only missing part was the border around the screen being too thin. To help this problem I decided to merge again with a half transparent white rectangle: The operation was mostly like the previous, only without the translation as the rectangle image had the same size as the notebook image. composite -geometry +0+0 light.png 3.png 4.png did its thing.

In the end I put down everything to these few lines:

for i in `ls`
do
    echo "doing $i"
    convert $i -resize 14x7\! $i
    composite -geometry +5+6 $i ../stock_notebook.png $i
    composite -geometry +0+0 ../light.png out.png out2.png
done
And in 10 seconds, ImageMagick did what would otherwise have taken me hours. Nice :)

25 February 2007

engines.ini

I just found a file in the Arena chess client download section called engines.ini. The file is in 1264 lines, and contains a description of an engine for each line. For CECP engines as well as UCI engines.

A snip from the cecp part, looks like:

Gnuchess4=EMPO
Gnuchess5=EO
Faile=O

The letters right to the equal signs are keys to the options in the dialog at the right, which means the gnuchess 4 supports "Edit mode", "Move now", "Ponder" and "Opening Book". Gnuchess 5 on the other hand only supports "Edit mode" and "Opening Book". Faile won't even be able to load a game, as it supports no such feature.

Of course it is a a weakness that there is no field to tell if the engine supports "protover 2", which means that we still have to base the loading on timeouts.

But still there are a lot of good things. Now we've actually got _some_ information for those engines which does not support "protover 2", and we will be able to send a proper error message when trying to load a game, playing against Faile or similar.

Also the file can be used to identify engines at the users PATH. It might however require some smart text comparison algorithms, as I can see no absolute way to tell that the binname of "Gnuchess4" is "gnuchess" and the name of "Shredder Classic" is "ShredderClassicLinux". At least on linux ;)

I plan to convert the file into the engine xml format

23 February 2007

An old post: Engine Managers

Intro: While working on a more recent post, I stumped upon an old draft from feb 07 that had seemingly never been posted. Perhaps because it describes an engine system for PyChess, that was in fact never implemented.
I thought it would be nice to post it now, as PyChess Staunton nears final, and work may begin on the new vision for PyChess 2. I can ensure you however, that the mockup named 'Final try' in the article, is miles away from what will find its way into PyChess 2 :)

In the past few days, I've been looking into creating better engine management in PyChess.

That this comes now, and not after FICS support is complete, is mostly due to the debian problems which have been reported in the last time. I hope that better engine management will make this problem easier to debug.

The engine management in preferences (which is still only glade template), looks like the image on the right. When the user wants to Add or Edit an engine, another dialog appears.

This new dialog is what has been causing troubles.

The dialog mainly let you specify
the path of the engine, the (optional custom) name of the engine and the engine protocol.
It also lets you specify the engine difficulty at specific levels.

In the
good old CECP days, this was easy: Everything user wanted to change for each level of difficulty was the maximum search level, and wether the engine should be allowed to think in the opponents' time.

Now in these UCI times engines has many more configuration parameters. And of cource some people might even want to change additional, engine specific parameters like hashsize.

In UCI engine parameters are given, and has to be set, at init time
Example.
At first sight these options greatly appears like a target for an IDE/gnome-settings like treeview with the left colum
n used for keys and the right one filled with the corresponding widget cellrenderer. Like gtk.CellRendererSpin, Combo, Text and Toggle.

First try

However a closer look at the pygtk specs, and a couple of hours af talking and coding with the great people at the pygtk irc channgel, it bekame clear to me, that I'd have to choose another path. The glade path.

I can almost hear people cry: "But the glade approcah looks so ugly!" And sure it does. So I did some research and I found Arena. Arena which seems like a great chess application, but is windows only.

I did the screenshot using wine. And I tell you: Don't try this at home - Arena is slow like eclipse when run under wine!

Arena has two dialogs for engine management (and some tabs): The one at the left and the UCI property configuration one above.

When users want to add an engine they can use the left dialog or a Wizzard. When later the engine is started (like in the analyzing panel) the user can bring up the UCI properties dialog and will afterwards of cource have to restart the engine.

As you see, Arena uses a modified glade approach. As in glade they don't use a treeview, but use real widgets. As a change however, they display the data in two columns, which is actually a good idea, when looking at how wide glade streches its property widgets.

Based on the Arena screenshot, I crafted the following glade template.
While it looks kinda okay, it has to major problems: It is not homogen in width, and more important: It will require 3 of those dialogs to support the three difficulty levels.

And what is perhaps most important: I found another interesting approach, and this time even a mac one.

Second try

This screenshot I found from the sigma chess user manual. And as all good sources of inspiration for gnomeapplications: It's a mac application.

The most interesting thing about this dialog is the way the data in the treeview is edited. This is neiter the gconf-editer method nor the glade approach, it is an entirely new one.

When an item in the list is selected, the lower label and control changes, to mach the content in the selected row.
On the shot a int is selected, and the dialog shows a slider.

Based on this new idea, I went to back to gazpacho to craft a new version.

Personally I find this version much more pleasing. It is not as crowded, and it takes up less space.

However there is still one major issue left to solve: Applying different values for each level of difficulty.

Final Try

Thinking of the different difficulty boxes (I didn't want to use tabs or simmilar) I came to think of those user iterfaces with a lot of lists and arrows to move arround values.
That was when I came to think of a very well known user interface, which does exatcly the same thing as I need: The adwanced email contact selection dialogs.

From the left list you can choose contacts and place them in one of the three receiver boxes. In PyChess gazpacho templates this method, combined with the value editing approach from sigma chess, this looks the following:

Well, this of cource is to be put into that dialog from before. Probably in some extender.

13 February 2007

Designing the perfect Internet Chess GUI

In the last few days I've spent a lot of time thinking on how the perfect GUI for Internet chess should be made.

I've taken inspiration from eboard, jin and most of all knights on how this could look.

First for the log on dialog, this is what I've come up with.

Good Points:

  • I like the fact that you have easy access to creating new accounts.

Concerns:

  • Users should probably not need to set the port, as it can be loaded from XML. The server "item" just looks so empty without it.
  • There are no buttons in the lower right corner. The "Log On" button is in the middle..
  • You have to open a website to create new accounts. Sadly, as far as I know, no servers offer a web service for this, which would make pychess able to have its own GUI for this.

Next for the actual game browsing dialog. This is a screenshot of the console hidden.

I'm not yet sure if this should be put inside a pychess game tab, like in knights and eboard, or it should be a window of its own, like the current "new game" dialog.

When you choose to join or observe a/some game(s) we will either replace the current tab (if we chose the tab way) or open a new tab. In both cases with a console sidepanel. This sidepanel can til we get gdl-dock implemented have a "show in window" button, for to view a full 80 column width console.

On the right you see the fully expanded edition. In the eventual version, there will be more icons on the buttons.

Good Points:

  • I'm very glad with the current simple look, that should make people able to join a game in one or two clicks.
  • Power chess gamers still have full console access.

Concerns:

  • Currently when you want to create/seek a new game with a special time setting, you choose -> Advanced -> Time -> Custom, which will bring up a dialog for you. This is perhaps a little to many clicks.
  • The join button is not in the lower right corner..

05 February 2007

Stupid epd opcodes

I've been rather angry at the fen format for a while. As the broadest used format for storing chess positions, it is used in the UCI chess engine protocol, and in the PGN format as a header tag.

To store a chess position, it takes more than just storing the location of the pieces. You also have to store the castling state, the cord which can be attacked by enpassant, the number of moves since last capture or pawn move (to judge draw on the fifty moves rule) and last, but very importantly, you have to somehow store the position repetition state.

In chess a game can be declared a draw if the same position has been repeated three times in a row. This rule can sometimes be used to force your opponent, which is in head of the game, to a draw.

A typical fen string could look like: nbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2 which is the board position, the castling state, the enpassant square, the fifty moves counter and the full move number.

The fen format nicely takes care of the first 4 parts, but it has no way of storing the repetition count. This might not sound as a big deal to most people, but imagine that a certain move will draw a game, and the engine is being told of the position using fen, then, if the move was good, the engine will play it, totally unaware of the draw danger.

Now, you can perhaps imagine how interested I was when I saw that the epd format, which is very much like the fen format, contains a so called opcode for storing the repetition count.

An epd string may look something like: r1b1kb1r/pppp1ppp/8/1nN5/1n1p2Pq/5P2/PPPPP2P/R1BQKB1R w KQkq - hmvc 1; fmvn 7; rc 1; The two last fen fields have been moved to opcodes, and we have added a field called rc.

The repetition field is good, but it only solves half the problem. Consider this case:

Initial position # Repetition count is now 1
1. Nf3 # Repetition count is now 1
... Nf6 # Repetition count is now 1
2. Ng1 # Repetition count is now 1
... Ng8 # Repetition count is now 2
3. Nf3 # Repetition count is now 2
... Nf6 # Repetition count is now 2
4. Ng1 # Repetition count is now 2
... Ng8 # Repetition count is now 3

You see, we've got four positions with repetition count 1 and four positions with repetition count 2. How the heck is a software program going to decide whether a move is going to put it in repetition count 3 and there by a draw?

13 September 2006

Pychess 0.2

I've released version 0.2 of Pychess, and created a googlepages page for it: http://pychess.googlepages.com/

From the page:

I've released version 0.2 of Pychess. It is not 100% bug free, but perhaps 95% :). I'll close anything posted in the issue tracker, but I will now also look forward into some of the purposed features.

I thought of giving it some kind of cool name, like the big projects, but couldn't think of anything better than "the flying beaver", which google tells me is already taken, so I skipped it.

Feel very free to post bug reports and to join the project :D

13 August 2006

Svg in python

After hours of googling, and finding stuff like import cairo.svg, import rsvg and import gtk.librsvg, I found out, that the newest versions of gdk simply understand svg out of the box.

pixbuf = gtk.gdk.pixbuf_new_from_file("black-bishop.svg") will do it all! Sweet.

Conclusion

After I've tried BloGTK, Drivel and gnome-blog-poster, I must say none of them did the job satisfying.

None of them were able to do a title, and none of them could do English spellchecking.

Drivel had a nice "edit old post" function, but gnome-blog-poster had a gnome-panel applet, so I find that it might be the best solution of the three.

However as none of them were able to post titles to posts, they weren't really useful. I guess I have to just use the web interface for now.

Print piping in python

print > open("file","w"), "File content" print >> sys.stdout, "Hello world" What a nice feature of pythons. Had thought this might be possible for a while, but never got the actual syntax. Having programmed php for a time, it is good to be back to python, where you haven't even got to put "\n" in the end of the prints :)

04 July 2006

System colors in gtk

I just found out, after an hour of searcing, how to get "system colors" in gtk. Only tried with pygtk, but I guess bindings does it the same.

First you have to get a GtkStyle object. If you extend GtkWidget you can get it using self.get_style(), else you can get it from e.g. a button: gtk.Button().get_style()

The GtkStyle object contains a lot of GtkStyleHelper objects. More precisely it has: ['base', 'base_gc', 'bg', 'bg_gc', 'bg_pixmap', 'dark', 'dark_gc', 'fg', 'fg_gc', 'light', 'light_gc', 'mid', 'mid_gc', 'text', 'text_aa', 'text_aa_gc', 'text_gc']

Each of these has different colors depending on the state. The states are ['STATE_ACTIVE', 'STATE_INSENSITIVE', 'STATE_NORMAL', 'STATE_PRELIGHT', 'STATE_SELECTED']

When you know that, you should be able to draw with a system color, e.g. with cairo.

context = widget.window.cairo_create()
context.rectangle(0,0,10,10)
context.set_source_color(self.get_style().dark[gtk.STATE_NORMAL])
context.fill_preserve()

Some other code:

>>> [m for m in dir(gtk) if m.startswith("STATE")]
['STATE_ACTIVE', 'STATE_INSENSITIVE', 'STATE_NORMAL', 'STATE_PRELIGHT', 'STATE_SELECTED']
>>> [m for m in dir(gtk.Button().get_style()) if type(getattr(gtk.Button().get_style(), m)) == type(gtk.Button().get_style().light)]
['base', 'base_gc', 'bg', 'bg_gc', 'bg_pixmap', 'dark', 'dark_gc', 'fg', 'fg_gc', 'light', 'light_gc', 'mid', 'mid_gc', 'text', 'text_aa', 'text_aa_gc', 'text_gc']

Update:

I've crafted a small pygtk script bringing up a palete of the colors of the current gtktheme. Code is:

#!/usr/bin/python

import pygtk
pygtk.require("2.0")
import gtk, pango

w = gtk.Button()

states = [m for m in dir(gtk) if m.startswith("STATE_")]
styles = [m for m in dir(w.get_style()) if type(getattr(w.get_style(), m)) == type(w.get_style().light)]
styles = [m for m in styles if not m.endswith("_gc")]

class CairoBoard(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.connect("expose_event", self.expose)

    def expose(self, widget, event):
        context = widget.window.cairo_create()
        context.rectangle(event.area.x, event.area.y,
                          event.area.width, event.area.height)
        context.clip()
        self.draw(context)
        return False
    
    def draw (self, context):
        for x in range(len(states)):
            for y in range(len(styles)):
                style = getattr(self.get_style(), styles[y])
                state = getattr(gtk, states[x])
                
                color = style[state]
                if color == None or type(color) != gtk.gdk.Color:
                    color = gtk.gdk.Color(0,0,0)
                context.set_source_color(color)
                context.rectangle(x*150,y*50,150,50)
                
                context.fill_preserve()
                context.new_path()
                
                if color.red + color.green + color.blue < 65535:
                    context.set_source_color(gtk.gdk.Color(65535,65535,65535))
                else: context.set_source_color(gtk.gdk.Color(0,0,0))
                
                cstring = "#"
                for col in color.red, color.green, color.blue:
                    h = hex(col/256)[2:]
                    if len(h) == 1:
                        h = "0"+h
                    cstring += h
                    
                layout = self.create_pango_layout ("%s\n%s\n%s" % \
                        (styles[y], states[x][6:], cstring))
                layout.set_font_description(pango.FontDescription("Sans Serif 10"))
                context.move_to(x*150,y*50)
                context.show_layout(layout)

                context.fill_preserve()
                context.new_path()

window = gtk.Window()
window.connect("destroy", gtk.main_quit)
window.add(CairoBoard())
window.set_default_size(len(states)*150, len(styles)*50)
window.show_all()
gtk.main()