<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" ><channel><title>Do Know Evil - A Blog by Tyler Mulligan &#187; Python</title> <atom:link href="http://www.doknowevil.net/category/computers/software/programming/python/feed/" rel="self" type="application/rss+xml" /><link>http://www.doknowevil.net</link> <description>Tips and Tricks About Computers, Web Development, Linux, the Internet and the Like</description> <lastBuildDate>Sat, 16 Jul 2011 01:25:35 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.0.5</generator> <item><title>Generating CSS Based on Images, also SEO-Friendly</title><link>http://www.doknowevil.net/2011/02/20/generating-css-based-on-images-also-seo-friendly/</link> <comments>http://www.doknowevil.net/2011/02/20/generating-css-based-on-images-also-seo-friendly/#comments</comments> <pubDate>Sun, 20 Feb 2011 17:19:05 +0000</pubDate> <dc:creator>Tyler Mulligan</dc:creator> <category><![CDATA[Bash]]></category> <category><![CDATA[Computers]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[Images]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[Software]]></category> <category><![CDATA[Web Development]]></category> <category><![CDATA[css]]></category> <category><![CDATA[generation]]></category> <category><![CDATA[tricks]]></category><guid isPermaLink="false">http://www.doknowevil.net/?p=977</guid> <description><![CDATA[I&#8217;ve typed out a lot of CSS that&#8217;s focused on using a background image to replace text. I&#8217;ve been using a cross-browser CSS trick that&#8217;s SEO-friendly, I&#8217;m not sure if it&#8217;s documented anywhere. I came upon the solution myself when I was searching for a clever way to do this years ago and it&#8217;s become]]></description> <content:encoded><![CDATA[<p>I&#8217;ve typed out a lot of CSS that&#8217;s focused on using a background image to replace text.  I&#8217;ve been using a cross-browser CSS trick that&#8217;s SEO-friendly, I&#8217;m not sure if it&#8217;s documented anywhere.  I came upon the solution myself when I was searching for a clever way to do this years ago and it&#8217;s become the standard method for me.  However, I dislike the tedious task of typing out the CSS and referencing the image pixels and at work, I don&#8217;t want my developers wasting that time either.</p><p>I figured it&#8217;d be best to come up with a simple solution, so I started coding something out in bash and it worked:</p><pre class="brush:bash">#!/bin/bash
# css.sh - generate common css and html
# Tyler Mulligan (z@interwebninja.com)
# Last Update: 02/16/2011
# MIT License

create_block_image() {
    feh -lr ${1:-.} | awk &#039;{ print $3&quot; &quot;$4&quot; &quot;$8 }&#039; | sed &#039;1d&#039; | while read l; do N=$((N+1));
        cbi_css $l
    done
    feh -lr ${1:-.} | awk &#039;{ print $3&quot; &quot;$4&quot; &quot;$8 }&#039; | sed &#039;1d&#039; | while read l; do N=$((N+1));
        cbi_html_a $l
    done
    feh -lr ${1:-.} | awk &#039;{ print $3&quot; &quot;$4&quot; &quot;$8 }&#039; | sed &#039;1d&#039; | while read l; do N=$((N+1));
        cbi_html_div $l
    done
}
cbi_css() {
    f=${3##*/}
    echo &quot;#${f%.*} {
    display: block;
    background: url(&#039;$3&#039;) no-repeat 0 0;
    width: 0;
    height: $2px;
    padding-left: $1px;
    overflow: hidden;
}&quot;
}
cbi_html_a() {
    f=${3##*/}
    echo &quot;&lt;a href=\&quot;#\&quot; id=\&quot;${f%.*}\&quot; alt=\&quot;$f\&quot; title=\&quot;$f\&quot;&gt;${f%.*}&lt;/a&gt;&quot;
}
cbi_html_div() {
    f=${3##*/}
    echo &quot;
&lt;div id=\&quot;${f%.*}\&quot;&gt;&lt;/div&gt;

&quot;
}

s=$1; shift; case $s in
  --image-block|--cbi|-i) create_block_image $@;;
  *) help help;;
esac</pre><p>Which ouputs something like:</p><pre class="brush:bash">
z@zygon:~/scripts/css$ ./css.sh -i img/
#dumbtubes-fav {
    display: block;
    background: url(&#039;img/dumbtubes-fav.png&#039;) no-repeat 0 0;
    width: 0;
    height: 14px;
    padding-left: 16px;
    overflow: hidden;
}
#submit_new {
    display: block;
    background: url(&#039;img/submit_new.png&#039;) no-repeat 0 0;
    width: 0;
    height: 106px;
    padding-left: 244px;
    overflow: hidden;
}
#dumbtubes-logo {
    display: block;
    background: url(&#039;img/dumbtubes-logo.png&#039;) no-repeat 0 0;
    width: 0;
    height: 70px;
    padding-left: 274px;
    overflow: hidden;
}
&lt;a href=&quot;#&quot; id=&quot;dumbtubes-fav&quot; alt=&quot;dumbtubes-fav.png&quot; title=&quot;dumbtubes-fav.png&quot;&gt;dumbtubes-fav&lt;/a&gt;
&lt;a href=&quot;#&quot; id=&quot;submit_new&quot; alt=&quot;submit_new.png&quot; title=&quot;submit_new.png&quot;&gt;submit_new&lt;/a&gt;
&lt;a href=&quot;#&quot; id=&quot;dumbtubes-logo&quot; alt=&quot;dumbtubes-logo.png&quot; title=&quot;dumbtubes-logo.png&quot;&gt;dumbtubes-logo&lt;/a&gt;
&lt;div id=&quot;dumbtubes-fav&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;submit_new&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;dumbtubes-logo&quot;&gt;&lt;/div&gt;
</pre><p>To explain the way this CSS works, it uses the padding-left as the actual width, setting the width 0 and then pushes whatever content you have out of view using overflow:hidden to hit it from view.  This makes it easy for search engines, keeping your HTML clean and CSS simple.</p><p>The bash script relies of &#8220;feh&#8221; a lightweight image viewer for linux that outputs a list of images and their dimensions.  It also generates some sample HTML to quickly drop in and modify.</p><p>This is not an elegant solution and I&#8217;m not happy with the fact that it relies on feh.  I&#8217;ve been slowly getting into python and I was amazed at how fast I was able to recreate this script in Python.</p><pre class="brush:python">#!/usr/bin/python
# css.py - generate common css and html
# Tyler Mulligan (z@interwebninja.com)
# Last Update: 02/17/2011
# MIT License
from PIL import Image
import sys
import os.path

CSS_FORMAT = &quot;&quot;&quot;#%s {\n\
    display: block;\n\
    background: url(&#039;%s&#039;) no-repeat 0 0;\n\
    height: %spx;\n\
    width: 0;\n\
    padding: %spx;\n\
    overflow: hidden;\n\
}\n&quot;&quot;&quot;

HTML_A_FORMAT = &quot;&quot;&quot;&lt;a href=&quot;#&quot; id=&quot;%s&quot; alt=&quot;%s&quot; title=&quot;%s&quot;&gt;%s&lt;/a&gt;\n&quot;&quot;&quot;
HTML_DIV_FORMAT = &quot;&quot;&quot;
&lt;div id=&quot;%s&quot;&gt;%s&lt;/div&gt;

\n&quot;&quot;&quot;

css=&quot;&quot;
html_a=&quot;&quot;
html_div=&quot;&quot;
for tf in os.listdir(sys.argv[1]):
    f = os.path.join(sys.argv[1],tf)
    if os.path.isfile(f) == True :
        img = Image.open(f)
        fid = os.path.splitext(tf)[0]
        (width, height) = img.size[0:2]
        css += CSS_FORMAT % (fid,f,height,width)
        html_a += HTML_A_FORMAT % (fid,f,fid,fid)
        html_div += HTML_DIV_FORMAT % (fid,fid)

print css
print html_a
print html_div </pre><p>This is just the basic idea, I didn&#8217;t spend more than 15 minutes on this rewrite, it does what I need it to do so far.  It was suggested by friends if I make it any bigger, to look into a templating engine, such as mako.  I hope this script can be useful to someone :). PS, HTML_DIV_FORMAT should be on one line, not sure why my syntax highlighter is trying to put it on 3.</p> ]]></content:encoded> <wfw:commentRss>http://www.doknowevil.net/2011/02/20/generating-css-based-on-images-also-seo-friendly/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>MySQL and Python &#8211; The Problems and [Some] Solutions</title><link>http://www.doknowevil.net/2010/10/15/mysql-and-python-the-problems-and-some-solutions/</link> <comments>http://www.doknowevil.net/2010/10/15/mysql-and-python-the-problems-and-some-solutions/#comments</comments> <pubDate>Fri, 15 Oct 2010 22:22:59 +0000</pubDate> <dc:creator>Tyler Mulligan</dc:creator> <category><![CDATA[Computers]]></category> <category><![CDATA[MySQL]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[Software]]></category> <category><![CDATA[bugs]]></category> <category><![CDATA[caveats]]></category> <category><![CDATA[parameterization]]></category> <category><![CDATA[suggestions]]></category><guid isPermaLink="false">http://www.doknowevil.net/?p=826</guid> <description><![CDATA[Intro I&#8217;m still relatively new to Python. I dabble once in a while. Usually when I catch myself writing something overly complicated in bash. I figured doing MySQL from a Python script would be easier and better implemented in Python. Well, I was in for a few surprises I&#8217;d like to share with others looking]]></description> <content:encoded><![CDATA[<h2>Intro</h2><p>I&#8217;m still relatively new to Python.  I dabble once in a while.  Usually when I catch myself writing something overly complicated in bash.  I figured doing MySQL from a Python script would be easier and better implemented in Python.  Well, I was in for a few surprises I&#8217;d like to share with others looking to integrate with MySQL in Python before you encounter the same problems I did.</p><h2>Getting on with it</h2><p>As a Python noob, it made sense to me to use the python-mysqldb module.  It &#8220;works&#8221; but not well.  Read through the [working] code below and see if you can find the problem and how I circumvented it.</p><pre class="brush:python">#!/bin/python
# Tyler Mulligan (tyler@doknowevil.net)
# MySQL the WRONG way in Python
import MySQLdb

def connection_settings():
    global DB_ADMIN, DB_ADMIN_PASSWORD, DB_NAME, DB_HOST, DB_USER, DB_PASSWORD
    DB_ADMIN=&quot;root&quot;
    DB_ADMIN_PASSWORD=&quot;local&quot;
    DB_NAME=&quot;foobardb&quot;
    DB_HOST=&quot;localhost&quot;
    DB_USER=&quot;foobar&quot;
    DB_PASSWORD=&quot;local&quot;

def run_admin_sql():
    conn = MySQLdb.Connection(host=DB_HOST, user=DB_ADMIN, passwd=DB_ADMIN_PASSWORD)
    cursor = conn.cursor()

    #cursor.execute(&quot;DROP USER %s@%s&quot;, (DB_USER, DB_HOST))
    #cursor.execute(&quot;DROP DATABASE %s&quot; % DB_NAME)

    cursor.execute(&quot;CREATE USER %s@%s IDENTIFIED BY %s&quot;, (DB_USER, DB_HOST, DB_PASSWORD))
    cursor.execute(&quot;CREATE DATABASE %s&quot; % DB_NAME)
    cursor.execute(&quot;GRANT USAGE ON %s.* TO %s@%s IDENTIFIED BY &#039;%s&#039;&quot; % (DB_NAME, DB_USER, DB_HOST, DB_PASSWORD))
    cursor.execute(&quot;GRANT ALL PRIVILEGES ON *.* to %s@%s IDENTIFIED BY %s&quot;, (DB_USER, DB_HOST, DB_PASSWORD))

    cursor.close()
    conn.close()

    conn = MySQLdb.Connection(db=DB_NAME, host=DB_HOST, user=DB_USER, passwd=DB_PASSWORD)
    cursor = conn.cursor()

    cat_table_sql = &quot;&quot;&quot;CREATE TABLE cats
    (
    `id` int(3) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) NOT NULL,
    `age` int(2) NOT NULL,
    `type` int(2) NOT NULL,
    PRIMARY KEY (`id`)
    )&quot;&quot;&quot;

    cursor.execute(cat_table_sql)
    cursor.execute(&quot;INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)&quot;, (&quot;fluffy&quot;, 5, 1))
    cursor.execute(&quot;INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)&quot;, (&quot;meow meow&quot;, 6, 1))
    cursor.execute(&quot;INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)&quot;, (&quot;purrfect&quot;, 3, 1))

    cursor.execute(&quot;SELECT * FROM cats&quot;)

    results = cursor.fetchall()
    cursor.close()
    conn.close()
    return results

connection_settings()
print run_admin_sql()</pre><p>The problem is parameterization.  The python-mysqldb module sucks at it.  It&#8217;s glaringly obvious when you look at the follow snippet and how I worked around it.</p><pre class="brush:python">
cursor.execute(&quot;CREATE USER %s@%s IDENTIFIED BY %s&quot;, (DB_USER, DB_HOST, DB_PASSWORD)) # GOOD
cursor.execute(&quot;CREATE DATABASE %s&quot; % DB_NAME) # BAD NEWS BEARS
cursor.execute(&quot;CREATE DATABASE %s&quot;, (DB_NAME)) # but the module won&#039;t support this
</pre><p>The difference, is that the first implementation uses the module&#8217;s parameterization to escape the variables that are passed to it, making it safe.  The second is using Python&#8217;s string formatter which means I&#8217;d have to do all the escaping and sanitization prior, which is obviously more dangerous and annoying.</p><p>The MySQLdb docs bury this little note, &#8220;Parameter placeholders can only be used to insert column values. They can not be used for other parts of SQL, such as table names, statements, etc.&#8221;</p><p>You can implement it this way but you&#8217;ll be wasting your time.  There are better modules out there to make up for this one&#8217;s shortcomings. <a href="http://packages.python.org/oursql/" target="_blank">OurSQL</a> and <a href="http://www.sqlalchemy.org/" target="_blank">SQLAlchemy</a> were the two libraries recommended to me in #python on irc.freenode.org.  I will make another post when I develop the same example above using one of these libraries.</p> ]]></content:encoded> <wfw:commentRss>http://www.doknowevil.net/2010/10/15/mysql-and-python-the-problems-and-some-solutions/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Python Multi-head X (Nvidia TwinView / Dual Monitor) Development Notes</title><link>http://www.doknowevil.net/2010/07/05/python-multi-head-x-nvidia-twinview-dual-monitor-development-notes/</link> <comments>http://www.doknowevil.net/2010/07/05/python-multi-head-x-nvidia-twinview-dual-monitor-development-notes/#comments</comments> <pubDate>Mon, 05 Jul 2010 23:29:24 +0000</pubDate> <dc:creator>Tyler Mulligan</dc:creator> <category><![CDATA[Application Management]]></category> <category><![CDATA[Computers]]></category> <category><![CDATA[Linux]]></category> <category><![CDATA[Operating Systems]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[Software]]></category><guid isPermaLink="false">http://www.doknowevil.net/?p=745</guid> <description><![CDATA[Preface The following development notes were written after researching the underlying handling of dual monitors in the X window system on Linux. I&#8217;ve included a code snippet that I built to help demonstrate behavior and create a proof of concept to show I can determine which monitor a window is on using only python and]]></description> <content:encoded><![CDATA[<h2>Preface</h2><p>The following development notes were written after researching the underlying handling of dual monitors in the <a href="http://en.wikipedia.org/wiki/X_Window_System" target="_blank">X window system</a> on Linux. I&#8217;ve included a code snippet that I built to help demonstrate behavior and create a proof of concept to show I can determine which monitor a window is on using only python and no statically set coordinates.</p><h2>Introduction</h2><p>I mentioned in my previous post that I&#8217;m using an nVidia video card with &#8220;<a href="http://www.nvidia.com/object/feature_twinview.html" target="_blank">TwinView</a>&#8221; software that outputs my video as if it were one screen, which it technically is, one X screen.  This means that the distinction between monitors is not mapped in the X tree, it&#8217;s handled by the window manager. Unlike <a href="http://en.wikipedia.org/wiki/Xinerama" target="_blank">Xinerama</a>, which has an x session per monitor and stitches them together.  Xinerama, however, has is being deprecated in favor of <a href="http://en.wikipedia.org/wiki/RandR" target="_blank">RandR</a> but regardless, TwinView is my choice and is not an option for me to change to.</p><p>With all of that said, the decision to bridge python to c that interfaces with compiz is a deadend and would be better implemented based on the new 0.9.0 C++ api.  It would be nice to be able to return a list of windows from a &#8220;monitor&#8221; object.  However, that&#8217;s beyond my current scope.  I was able to whip up some python that to show that implementing the monitor management in python using the gtk module isn&#8217;t /that/ hacky.  I emphasize because I read some posts that claimed window decorations could be an issue in accurate calculations.</p><h2>Some Code</h2><p>Read the comment on line 2. <a href="http://www.pygtk.org/docs/pygtk/" target="_blank">Learn more about pygtk here</a></p><pre class="brush:python">
#!/usr/bin/python
# Print some information about the X environment, the monitor setup, currently active window and cursor position
import gtk.gdk

screen = gtk.gdk.screen_get_default()
print &quot;X default screen size: %d x %d&quot; % (screen.get_width(), screen.get_height())
print &quot;xid of root window: %d&quot; % screen.get_root_window().xid

monitors = int(screen.get_n_monitors())
print &quot;== %d monitors ==&quot; % monitors
for m in range(0, monitors):
    print &quot; - geometry of monitor %d: %s&quot; % (m, screen.get_monitor_geometry(m))

window = screen.get_active_window()
win_x, win_y, win_w, win_h, win_bit_depth = window.get_geometry()
print &quot;active window on monitor: %d&quot; % screen.get_monitor_at_point((win_x+(win_w/2)),(win_y+(win_h/2)))
print &quot;window geometry (x,y,w,h): %d, %d, %d, %d&quot; % (win_x,win_y,win_w,win_h)

display = gtk.gdk.display_get_default()
pointer = display.get_pointer()
print &quot;cursor position (x, y): %d, %d&quot; % (pointer[1], pointer[2])
print &quot;cursor on monitor: %d&quot; % screen.get_monitor_at_point(pointer[1],pointer[2])</pre><p>thanks to those in #compiz-dev and #python on freenode who helped me come around to create this snippet.  I hope it will help others looking to develop for multi-head setups in Linux.  Please let me know if I missed anything or did something incorrectly, this is new territory for me.</p> ]]></content:encoded> <wfw:commentRss>http://www.doknowevil.net/2010/07/05/python-multi-head-x-nvidia-twinview-dual-monitor-development-notes/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>New Python eBook, Much Better, Down and Dirty</title><link>http://www.doknowevil.net/2010/07/01/new-python-ebook-much-better-down-and-dirty/</link> <comments>http://www.doknowevil.net/2010/07/01/new-python-ebook-much-better-down-and-dirty/#comments</comments> <pubDate>Fri, 02 Jul 2010 00:50:14 +0000</pubDate> <dc:creator>Tyler Mulligan</dc:creator> <category><![CDATA[Books]]></category> <category><![CDATA[Computers]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[Software]]></category><guid isPermaLink="false">http://www.doknowevil.net/?p=729</guid> <description><![CDATA[Some friends recommended a book that&#8217;s a quicker pace with some better programming practices. Learn Python The Hard way (or quick way :-P). It reads more like a walk-through tutorial / quick reference and gives you easily repeatable programming practices that will get you a stronger feel for the language. It&#8217;s probably best used in]]></description> <content:encoded><![CDATA[<p>Some friends recommended a book that&#8217;s a quicker pace with some better programming practices. <a href="http://learnpythonthehardway.org/index" target="_blank">Learn Python The Hard way</a> (or quick way :-P). It reads more like a walk-through tutorial / quick reference and gives you easily repeatable programming practices that will get you a stronger feel for the language.  It&#8217;s probably best used in combination with the <a href="http://docs.python.org/" target="_blank">Python Documentation</a>.  Also, a fair warning, <a href="http://oppugn.us/posts/1272050135.html">Dive Into Python has war declared against it</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.doknowevil.net/2010/07/01/new-python-ebook-much-better-down-and-dirty/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>scp-notifications for GNOME and Ubuntu &#8211; Expanding on My Original Python Script</title><link>http://www.doknowevil.net/2010/06/26/scp-notifications-for-gnome-and-ubuntu-expanding-on-my-original-python-script/</link> <comments>http://www.doknowevil.net/2010/06/26/scp-notifications-for-gnome-and-ubuntu-expanding-on-my-original-python-script/#comments</comments> <pubDate>Sun, 27 Jun 2010 00:11:15 +0000</pubDate> <dc:creator>Tyler Mulligan</dc:creator> <category><![CDATA[Compiz]]></category> <category><![CDATA[Computers]]></category> <category><![CDATA[Desktop Mods]]></category> <category><![CDATA[GNOME]]></category> <category><![CDATA[KDE]]></category> <category><![CDATA[Linux]]></category> <category><![CDATA[Operating Systems]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[Software]]></category> <category><![CDATA[The Internet]]></category> <category><![CDATA[Ubuntu]]></category> <category><![CDATA[Web Applications]]></category> <category><![CDATA[open source]]></category> <category><![CDATA[libnotify]]></category> <category><![CDATA[notification-daemon]]></category> <category><![CDATA[pynotify]]></category><guid isPermaLink="false">http://www.doknowevil.net/?p=698</guid> <description><![CDATA[I&#8217;ve only been coding Python for ~12 hours total, so don&#8217;t expect this to be perfect. Knowing what I know about creating/testing other software and doing my best to scour through very light pynotify documentation, I&#8217;ve begun to build out this script to be more useful / portable / configurable. As the Version 0.6 indicates,]]></description> <content:encoded><![CDATA[<p>I&#8217;ve only been coding Python for ~12 hours total, so don&#8217;t expect this to be perfect.  Knowing what I know about creating/testing other software and doing my best to scour through very light pynotify documentation, I&#8217;ve begun to build out this script to be more useful / portable / configurable.  As the Version 0.6 indicates, I&#8217;m not quite at my goal yet and there is still more to learn to bring it up to that point.</p><p>I&#8217;m releasing this early on my blog just in case I caught any people yesterday who&#8217;ve been experimenting with my research / code so far.  I&#8217;ll share it on github when I evolve it just a bit more.</p><p><a href="http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv">Video of it in Action</a></p><pre class="brush:python">
#!/usr/bin/env python
#
# Title: scp-notifications
# Author: Tyler Mulligan (tyler@detrition.net)
# Date: 06/26/2010
# Version: 0.6
# Description:
# Used in combination with an event, such as an action or cronjob, this script
# will scp the latest file from a folder to your server.
#
# Optionally, it can copy the url to your clipboard and/or show a popup with a
# link to the file after succesfully uploading
#
# Orginally developed to be piped to from compiz screenshot tool
# http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv
#
# The MIT License
#
# Copyright (c) 2010 Tyler Mulligan
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the &quot;Software&quot;), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE
#

import pygtk
pygtk.require(&#039;2.0&#039;)
import pynotify
import gtk
import sys
import os
import subprocess

# Set Variables
#####################################
user = &quot;user&quot;
host = &quot;server.com&quot;
# All should have trailing slashes
lfolder = &quot;/home/user/screenshots/&quot;
hfolder = &quot;/home/remote_user/screenshots/&quot;
httplink = &quot;http://&quot;+host+&quot;/screenshots/&quot;

# Display
#####################################
t1 = 5000 # timeout for screenshot upload dialog
t2 = 3000 # timeout for screenshot preview
t3 = 7000 # timeout for link dialog

screenshot_preview = 1 # If using with the compiz screenshot plugin, you may want this
popup_link = 1 # another popup
copy_to_clipboard = 0 # automatically copy text to keyboard

# Position
#####################################

# Get screensize &lt; &lt; used for relative positioning
display = gtk.gdk.display_get_default()
screen = display.get_default_screen()
x = screen.get_width() - 1
y = screen.get_height() - 1

# 0 for Automatic Placement
&quot;&quot;&quot;
x1 = 0
y1 = 0
x2 = 0
y2 = 0
x3 = 0
y3 = 0
&quot;&quot;&quot;
# Define Relative Position (assuming top-right)
x1 = x-1
y1 = 12
x2 = x1
y2 = y1 + 100
x3 = x-1
y3 = 12
# Define Static (1920 puts it on my second monitor)
&quot;&quot;&quot;
x1 = 1919
y1 = 12
x2 = x1
y2 = y1 + 100
x3 = 1920
y3 = 12
&quot;&quot;&quot;
#####################################

def upload_cb(n, action):
    assert action == &quot;upload&quot;

    subprocess.call([&quot;scp&quot;, os.path.join(lfolder, f), &#039;%s@%s:%s&#039; % (user, host, hfolder)])

    # setup URL in
    if copy_to_clipboard:
        clipboard = gtk.clipboard_get()
        clipboard.set_text(httplink + f)
        # make our data available to other applications
        clipboard.store()

    # Notification: Link for the clicking
    if popup_link:
        n3 = pynotify.Notification(&quot;Here is your link&quot;,&quot;&lt;a href=&#039;&quot; + httplink + f + &quot;&#039;&gt;&quot; + httplink + f + &quot;&quot;)

        helper = gtk.Button()
        icon = helper.render_icon(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_DIALOG)
        n3.set_icon_from_pixbuf(icon)

        n3.set_urgency(pynotify.URGENCY_NORMAL)
        if x3:
            n3.set_hint(&quot;x&quot;, x3)
        if y3:
            n3.set_hint(&quot;y&quot;, y3)
        n3.set_timeout(t3)
        n3.connect(&quot;closed&quot;,closen3_cb)

        if not n3.show():
            print &quot;Failed to send notification&quot;
            sys.exit(1)

        closen1_cb(n1)
        closen2_cb(n2)

    gtk.main_quit()
    sys.exit(1)

# Notification 1 was closed
def closen1_cb(n):
    n1.close()
    if screenshot_preview:
        n2.close()
    gtk.main_quit()

# Notification 2 was closed
def closen2_cb(n):
    n2.close()
    gtk.main_quit()

# Notification 2 was closed
def closen3_cb(n):
    n3.close()
    gtk.main_quit()

# The Ignore button was clicked
def ignore_cb(n, action):
    assert action == &quot;ignore&quot;

    closen1_cb(n1)
    closen2_cb(n2)
    gtk.main_quit()

# Main
def main():
    gtk.main()

# Init
if __name__ == &#039;__main__&#039;:
    if not pynotify.init(&quot;Notifier &#039;scp&#039; Option&quot;):
        sys.exit(1)

    # Get latest file and build uri
    start = os.path.abspath(lfolder)
    f = max([(os.path.getmtime(os.path.join(start,p)),p)
         for p in os.listdir(start)])[1]
    uri = lfolder + f

    # Notification: Upload to Server
    n1 = pynotify.Notification(&quot;Upload to Server?&quot;,&quot;Copy the file &#039;&quot; + f + &quot;&#039; to the server?&quot;)

    helper = gtk.Button()
    icon = helper.render_icon(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
    n1.set_icon_from_pixbuf(icon)

    n1.set_urgency(pynotify.URGENCY_NORMAL)
    if x1:
        n1.set_hint(&quot;x&quot;, x1)
    if y1:
        n1.set_hint(&quot;y&quot;, y1)
    n1.set_timeout(t1)
    n1.add_action(&quot;upload&quot;, &quot;Yes, Upload&quot;, upload_cb)
    n1.add_action(&quot;ignore&quot;, &quot;Ignore&quot;, ignore_cb)
    n1.connect(&quot;closed&quot;,closen1_cb)

    if not n1.show():
        print &quot;Failed to send notification&quot;
        sys.exit(1)

    # Notification: Screenshot Preview
    if screenshot_preview:
        n2 = pynotify.Notification(&quot;Screenshot Preview&quot;, &quot;&quot;, uri)
        n2.set_urgency(pynotify.URGENCY_LOW)
        if x2:
            n2.set_hint(&quot;x&quot;, x2)
        if y2:
            n2.set_hint(&quot;y&quot;, y2)
        n2.set_timeout(t2)
        n2.connect(&quot;closed&quot;,closen2_cb)

        if not n2.show():
            print &quot;Failed to send notification&quot;
            sys.exit(1)

    main()
</pre><p>.</p> ]]></content:encoded> <wfw:commentRss>http://www.doknowevil.net/2010/06/26/scp-notifications-for-gnome-and-ubuntu-expanding-on-my-original-python-script/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <enclosure url="http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv" length="2797703" type="video/ogg" /> </item> <item><title>Ubuntu Notifications (osd-notify) Sucks, notifications-daemon Rocks &#8211; Exploiting the Goodness with Compiz</title><link>http://www.doknowevil.net/2010/06/25/ubuntu-notifications-osd-notify-sucks-notifications-daemon-rocks-exploiting-the-goodness-with-compiz/</link> <comments>http://www.doknowevil.net/2010/06/25/ubuntu-notifications-osd-notify-sucks-notifications-daemon-rocks-exploiting-the-goodness-with-compiz/#comments</comments> <pubDate>Sat, 26 Jun 2010 04:47:40 +0000</pubDate> <dc:creator>Tyler Mulligan</dc:creator> <category><![CDATA[Compiz]]></category> <category><![CDATA[Computers]]></category> <category><![CDATA[GNOME]]></category> <category><![CDATA[KDE]]></category> <category><![CDATA[Linux]]></category> <category><![CDATA[Operating Systems]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Python]]></category> <category><![CDATA[Software]]></category> <category><![CDATA[Ubuntu]]></category> <category><![CDATA[open source]]></category> <category><![CDATA[gtk]]></category> <category><![CDATA[markedwontfix]]></category> <category><![CDATA[notifications]]></category><guid isPermaLink="false">http://www.doknowevil.net/?p=677</guid> <description><![CDATA[Introduction The long name for this blog used to be &#8220;Tyler Mulligan&#8217;s Tips and Tricks for Increasing your Efficiency&#8220;. I love finding ways to increase my efficiency and let computers do the work while I focus on more interests aspects of what the computer is providing me with. When I recognize an issue, I find]]></description> <content:encoded><![CDATA[<h2>Introduction</h2><p>The long name for this blog used to be &#8220;<a href="http://www.doknowevil.net" target="_blank">Tyler Mulligan&#8217;s Tips and Tricks for Increasing your Efficiency</a>&#8220;.  I love finding ways to increase my efficiency and let computers do the work while I focus on more interests aspects of what the computer is providing me with.  When I recognize an issue, I find a way to cut out time by streamlining the process for the most accurate repetition, like any programmer would/should/could.</p><p>I noticed myself taking a lot of screenshots with compiz&#8217; built in screenshot tool (I can hold a hotkey and drag a box to take newspaper style clippings).  This is very fast and simple, I highly recommend enabling this option and getting used to it.  However, I don&#8217;t care much for clicking around on clunky websites to upload images.</p><p>This is where my adventure starts&#8230; when I find out <a href="http://ubuntuforums.org/showthread.php?t=1517950" title="notify-osd doesn't display popup balloons">a very useful feature was deprecated and not replaced in Ubuntu</a>.  I don&#8217;t use the new notification area, it has too much I don&#8217;t need, I never liked the behavior of these new osd-notify notifications which I found out now are even more worthless (sorry team).</p><h2>Using Sane Notifications in Ubuntu</h2><p>After <a href="http://ubuntuforums.org/showpost.php?p=8559795&#038;postcount=8">reinstalling the GNOME default notifications system in Ubuntu</a> I was able to use SANE notifications that actually&#8230; KICK ASS!  I don&#8217;t understand how osd-notify is better than these which even comes with it&#8217;s own notification properties panel.  Maybe it&#8217;s an under the hood thing&#8230;</p><p>Regardless, there is no doubt in my mind that notifications that you hover, can still slightly see but click through but cannot perform any actions, even a close, are just plan stupid and annoying..</p><p><img src="http://interwebninja.com/compiz-screenshots/screenshot5.png" title="Screenshot taken with Tyler Mulligan's Notification 'scp' Option python script for ubuntu and other Linux based operating systems" /><br /> Screenshot of the script I ended up writing using the &#8220;better&#8221; notification system to ask me if I want to upload the screenshot I just took to the server.</p><h2>Linking the notifications</h2><p>I linked the notifications the following way:</p><p><img src="http://interwebninja.com/compiz-screenshots/screenshot3.png" title="Screenshot taken with Tyler Mulligan's Notification 'scp' Option python script for ubuntu and other Linux based operating systems" /></p><h2>Python Script for Notification that Prompts for File Upload to Server</h2><p>Knowing diddly squat about Python, I chugged forward with my classic notification popups that allow for interaction as it had the most activity around it and some examples available in /usr/share/doc/python-notify/examples/</p><p>I ended coming up with the following script thanks to some help from a few people in #python on irc.freenode.org</p><p>This is outside of the scope of this blogpost but this script assumes you have setup passwordless ssh to your server.</p><pre class="brush:python">
#!/usr/bin/env python
#
# Title: Notification &#039;scp&#039; Option
# Author: Tyler Mulligan (tyler@detrition.net)
# Date: 06/25/2010
# Description:
# Used in combination with an event, such as an action or cron
# the latest file from a folder will be scped to your server and will copy
# the http location of the that file to your clipboard
#
# Orginally developed to be piped to from compiz screenshot tool
#
#
# The MIT License
#
# Copyright (c) 2010 Tyler Mulligan
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the &quot;Software&quot;), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE
#

import pygtk
pygtk.require(&#039;2.0&#039;)
import gtk
import pynotify
import sys
import os
import subprocess

user = &quot;user&quot;
host = &quot;server.com&quot;
lfolder = &quot;/home/user/screenshots/&quot;
hfolder = &quot;/home/remote_user/screenshots/&quot;
httplink = &quot;http://&quot;+host+&quot;/screenshots/&quot;
timeout = 5000

# Set position
display = gtk.gdk.display_get_default()
screen = display.get_default_screen()
x = screen.get_width() - 1
y = screen.get_height() - 1
#x = 1919
#y = 12

def upload_cb(n, action):
    assert action == &quot;upload&quot;

    start = os.path.abspath(lfolder)
    f = max([(os.path.getmtime(os.path.join(start,x)),x)
          for x in os.listdir(start)])[1]

	# setup URL in clipboard
    clipboard = gtk.clipboard_get()
    clipboard.set_text(httplink + f)
    # make our data available to other applications
    clipboard.store()

    os.system(&#039;scp &quot;%s&quot; &quot;%s@%s:%s&quot;&#039; % (lfolder + f, user, host, hfolder) ).wait()

    n.close()
    gtk.main_quit()

def ignore_cb(n, action):
    assert action == &quot;ignore&quot;
    n.close()
    gtk.main_quit()

if __name__ == &#039;__main__&#039;:
    if not pynotify.init(&quot;Notifier &#039;scp&#039; Option&quot;):
        sys.exit(1)

	# Setup Popup
    n = pynotify.Notification(&quot;Upload to Server?&quot;)
    n.set_urgency(pynotify.URGENCY_NORMAL)
    n.set_timeout(timeout)
    n.set_category(&quot;device&quot;)
    n.add_action(&quot;upload&quot;, &quot;Yes, Upload&quot;, upload_cb)
    n.add_action(&quot;ignore&quot;, &quot;Ignore&quot;, ignore_cb)

    # Set position
    n.set_hint(&quot;x&quot;, x)
    n.set_hint(&quot;y&quot;, y)

    if not n.show():
        print &quot;Failed to send notification&quot;
        sys.exit(1)

    gtk.main()
</pre><p>edit: I updated the script with some position information &#8212; I love that I can put the notifications ANYWHERE on my screen.  I also variablized the timeout.</p><p>edit 2: Here is a video of my improved script in action &#8212; I&#8217;ll post the source later</p><p>http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv</p><h2>Going Beyond</h2><p>The Compiz example is just something that server my immediate needs.  The possibilities are however endless.  You can for example, link this script to a cron job asking you if you want to sync some other sort of file.  Or perhaps you&#8217;d like to add multiple buttons to give yourself a folder / server choice.</p><p>Happy hacking</p> ]]></content:encoded> <wfw:commentRss>http://www.doknowevil.net/2010/06/25/ubuntu-notifications-osd-notify-sucks-notifications-daemon-rocks-exploiting-the-goodness-with-compiz/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <enclosure url="http://cankill.us/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv" length="687321" type="video/ogg" /> <enclosure url="http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv" length="687321" type="video/ogg" /> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk
Page Caching using disk (enhanced)
Object Caching 725/779 objects using disk

Served from: www.doknowevil.net @ 2012-02-07 09:51:56 -->
