úterý 21. října 2014

avconv: Encoder 'aac' is experimental and might produce bad results. Add '-strict experimental' if you want to use it.

I was trying to rotate some of videos from my iPhone on ubuntu using avconv (ffmpeg).
After a quick googling, I got this command (http://askubuntu.com/a/228588):

avconv -i in.mov -vf "transpose=1" out.mov

In case of my videos, it failed with: 

Encoder 'aac' is experimental and might produce bad results. 
Add '-strict experimental' if you want to use it.

To save you some time, here is how to put the parameter correctly:

avconv -i in.mov -vf "transpose=1" -strict experimental out.mov

Note: I wasn't investigating the cause of it, or any better solutions, because this just worked for me. 

Enjoy!

pondělí 13. října 2014

Login component UX fail

My client emailed me a login information to www.whmcs.com today.
The concentration of UI/UX fails on one simple thing as a Sign in component just struck me.
It struck me so hard, that I had to write about it.
  1. On a completely first sight, you don't see a username / password fields so you have to focus a little bit and look for it.
    Ok, this is not that big deal. User already has an account. You won. But still, if I find myself looking for something in the UI, I know there is something fishy.
  2. I find the link in the top right nav. Hurray. When hovered, a login form is displayed. Sweet.
    I know, that I have the username / password in the email. I also know that i will have to copy/paste two items. And because I want to do things efficently, I don't want to hover the link again when I go with the password. (you know, keyboard is faster)
    So I click the link. Assuming, it will take me to a typical login page while I will copy the username. Nothing happened. Why the fuck would you put a pointer cursor on a thing, which does nothing when you click it. And most importantly, why on earth it does nothing?
    Even though there is a page for login herehref="javascript:void(0)". Seriously???
  3. So I was thinking little bit. Maybe the author of the widget was clever enough that if I fill in the username, the login form will not disappear and I will still be able to paste the password easily without touching my mouse.
    Nope. But what is even worse. The form disappears when you put the cursor away. Even if you have a focus on the field and typing. What the hell.
  4. I want the site to remember me, because I don't want to torture myself again next time. I click the label "remember me" and nothing happened. In that moment, I was assuming, that the UI guy never heard of a label element. I click the checkbox directly and log in.
    While writing this, I have checked again and there is actually a label element used. So clicking it works. Why it did not work before?
    I have misclicked it because the label text is small. As it was not as important as the other fields. Haha.
  5. Some of you could argument back, that if I want to do things fast on keyboard, why didn't I just Tab into the checkbox. But anyway, it doesn't justify the smaller font on it.
    But if I did use my keyboard, I would probably want to Tab onto "login" button either. You hit Tab, hit space and the page scrolls halfway through. Whaaat? After the checkbox, "Forgot password?" link is focused. 
I would like to congratulate to the guy who did it. For concentrating so many UI/UX mistakes in one tiny place.

The sad thing is that plain html login form, built without any css or javascript features, would offer better UX than this component. In which tens of minutes of work was invested for sure.

I know. Deadlines, budgets and bullshit. But it is you and your work. Either keep it simple, or do it right. I wouldn't mind the login form to be on a separate page. Nobody would.



neděle 9. prosince 2012

Python SyntaxError: unexpected EOF while parsing

This error just popped on me after my development virtual machine crashed (grr). 
All I needed to do was remove all *.pyc and *.pyo files from my project (or at least from the package which fails to import). Everything was alright then.
You can use pyclean program which is usually installed together with python on debian / ubuntu.

sobota 1. prosince 2012

Python exception handling

Exceptions are a very useful and powerful tool. But they must be used carefully, especially in the Python. Recently, my python program stopped working. Hmm. Not exactly stopped. No exception was raised, but all values that should be correclty loaded from database were set to default value. 

After few hours of debugging and head scratching, I realized that the problem is in "pythonic" approach of setting default value for object attributes that do not exist.

Instead of testing object with hasattr, we can just try to access the searched attribute directly and catch the AttributeException if it does not exist. (btw. this is exactly what hasattr does, so it is more effective approach to do it directly)

The problems occurs when you implement data descriptors __getattr__ or __getattribute__ in your objects and underlying business code raise unexpected AttributeError (or it´s subclass) during data descriptor call.



In example above, if AttributeError (or it´s subclass) is raised during self.business.do method (line 4), except statement on line 8. will catch it and the error will disappear silently. 

Of course it is logical and understandable behavior. But! If you will not wrap your implementation of __getattr__ or __getattribute__ in try ... except AttributeError (which can re raise exception other than AttributeException), you will spend a lot of time in debugging later :-)

My last tip on the Python exceptions for today: Always specify most concrete exception class that you are willing do catch. If you decide to catch all ValueErrors intead of some concrete class YourBusinessValueException(ValueError), you have to count on with possibility that not only YourBusinessValueException will be raised and catched in your try ... except statement! That except statement can catch any exception which is subclass of ValueError. There can be plenty of them!

I hope I helped you. Feel free to leave comment!

středa 22. srpna 2012

Psycopg2 UnicodeEncodeError when using "psycopg2.extensions.adapt"

Just a little trick. If you are using Psycopg2´s adapt method to adapt your classes for usage in SQL statements (like this), you may run into problems with unicode strings.

Let´s say you have object which you would like to map to some composite PG type. Any string attribute which will contain unicode chars may give you exception like this (when called adapt and getquoted on it):

       .... some traceback .... 

       return adapted.getquoted()
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 40-44: ordinal not in range(256)

It gave me few moments, but I found solution in this discussion. Because the encoding is passed through by connection object, it is necessary to call prepare methods on all adapted items (by adapted, I mean this: adapted = psycopg2.extensions.adapt(value)). 

When your class for adaptation is passed to psycopg´s execute, psycopg first try to call prepare if available. So in your class, you get real connection object by prepare method. Then, after calling adapt on attribute values, you can call prepare on them too. 

Little example. 


   1 from psycopg2.extensions import adapt, ISQLQuote
   2 
   3 class FooBarComposite:
   4         
   5         def __init__(self, foo, bar):
   6                 self.foo = foo
   7                 self.bar = bar
   8                 
   9         def __conform__(self, protocol):
  10                 if protocol is ISQLQuote:
  11                         return self
  12         
  13         def prepare(self, conn):
  14                 self._conn = conn
  15         
  16         def getquoted(self):
  17                 
  18                 adapted_foo = adapt(self.foo)
  19                 if hasattr(adapted_foo, 'prepare'):
  20                         adapted_foo.prepare(self._conn)
  21                 
  22                 adapted_bar = adapt(self.bar)
  23                 if hasattr(adapted_bar, 'prepare'):
  24                         adapted_bar.prepare(self._conn)
  25                 
  26                 result = adapt((adapted_foo, adapted_bar))
  27                 if hasattr(result, 'prepare'):
  28                         result.prepare(self._conn)
  29                 
  30                 return result.getquoted()


Now, class FooBarComposite can be used directly as an parameter of SQL statements and unicode attributes will work properly.

čtvrtek 19. dubna 2012

Recursive DOM walk with Dojo

Ever wanted to recursively walk all children of some DOM node? As far as I was looking for it, DOJO does not support this feature for now. 


With extend function from dojoj/_base/lang I was able to extend Nodelist class, so whenever I use dojo's brilliant query function, I have my custom walk method available.


Just save this as a module wherever you want and include (require) it when you need it.  

define(["dojo/query", "dojo/_base/lang", "dojo/NodeList-traverse",], function(query, lang) {

var NodeList = query.NodeList;

lang.extend(NodeList, {

_walk: function (node, func) {
func(node);
node = query(node).children().first()[0];
while(node) {
this._walk(node, func);
node = query(node).next()[0];
}
},

walk:  function (func) {
this.forEach(function (node) {
this._walk(node, func);
}, this);
}
});

return NodeList;
});

Example of usage:

define([
"dojo/query",
"dojo/NodeList-traverse",
"./NodeList-walk", // !!! your NodeList-walk module
], function(query) {
query('.selector').walk(function(elm) {
console.log(elm)
});
});


Hope you like it! If you find any bug, just let me know in comments.


You can test this on http://jsfiddle.net/joshuaboshi/qpvMn/

středa 18. dubna 2012

Setting "blockNodeForEnter" for EnterKeyHandling plugin of Dijit/Editor

You might run into a problem when you would like to change default behaviour of Dijit Editor. By default, when user presses enter key in Dijit Editor, only <br /> tag is generated. What if you want to encapsulate text into <p> tags or <div> tags?


Writing post filter was not helpful (probably because EnterKeyHandling plugin processing was fired after post filter, but I am not sure about this). So I opened /dijit/_editor/plugins/EnterKeyHandling.js and the solution was right in front of me:
// This plugin has three modes:
//
// * blockNodeForEnter=BR
// * blockNodeForEnter=DIV
// * blockNodeForEnter=P
//
// In blockNodeForEnter=P, the ENTER key starts a new
// paragraph, and shift-ENTER starts a new line in the current paragraph.
Now the only problem: How to change default mode from BR to P? Where can it be done?


Constructor of EnterKeyHandling states:

if("blockNodeForEnter" in args){
args.blockNodeForEnter = args.blockNodeForEnter.toUpperCase();
}
We are close to finish! How to pass custom args when invoking this plugin? The Dijit Editor has array "plugins" in which are stored all plugins for editor. Invocation of these plugins is done in "addPlugin" method.


The magic is done on this line:
var args=lang.isString(plugin)?{name:plugin}:lang.isFunction(plugin)?{ctor:plugin}:plugin;
This line can be translated as: if passed plugin is string, us it as plugin name (for plugins like "bold", "italic" etc.). If passed plugin is function, it is considered to be dojo Class and is used as constructor ({ctor:plugin} part). And anything else is just passed as is.
The args variable is passed to plugin constructor few lines later. 


So! We can add custom object to plugins array of editor that will look somehow like this:

{
ctor: EnterKeyHandlingPlugin,
blockNodeForEnter: 'P'
}
Easy huh? Very unfortunate that this is undocumented or not easy to find :/
Complete example:


require([
"dijit/Editor",
"dijit/_editor/plugins/EnterKeyHandling",
        "dijit/_editor/plugins/LinkDialog",
        "dijit/_editor/plugins/FullScreen"
], function( DijitEditor, DijitEditorEnterKeyHandling) {
var editorConfig = {
plugins: [
"bold",
"italic",
"|",
"cut",
"copy",
"paste",
"|",
"createLink",
"fullscreen",
{
ctor: DijitEditorEnterKeyHandling,
blockNodeForEnter: 'P'
}
]
};
var editor = new DijitEditor(editorConfig);
});