The way you are setting up the layout actually looks fine to me. Passing a widget to the constructor of the layout is effectively the same as called widget.setLayout(layout) later, so it would be redundant to add the setLayout call.
I do agree that using the global to store the single window instance is a good alternative to having to mix your code with PyMel or the cmds module if you don't really need them otherwise. The only problem you might run into is if you dynamically reload the module, which would cause you to lose the reference to the global. A 3rd approach is to just check for the object using Qt:
win = mayaPythonMainWindow()
existing = win.findChild(QtGui.QWidget, "renamerWindow")
if existing:
existing.deleteLater()
Really the only thing I see as probably the issue is what Joe pointed out, regarding the script job not getting cleaned up if it hasn't fired before, but the window is deleted. I am not 100% sure but does the scriptjob parent argument expect a Maya UI path and not a Qt widget? If so, you can't pass self. There are a couple approach to handling this as well. One way is to look up the UI path of your window and pass that to parent:
ptr = long(shiboken.getCppPointer(self)[0])
name = mui.MQtUtil.fullName(ptr)
scriptJobNum = cmds.scriptJob(event = ["SelectionChanged", self.populateList], parent=name)
Another way might be to save the scriptJobNum, and then kill the scriptJob in your __del__ method of your class.