Python PyQt/PySide QMdiArea subwindows scroll not working in TabbedView - python-2.7

I have setup simple example using PyQt designer.See below.
I have mdiarea in in which i am adding a form as subwindow. I made form a bit lengthier than mainwindow to see if scroll-bar appears for child sub-window.
PROBLEM:
If i set mdiarea to setViewMode(QtGui.QMdiArea.TabbedView) scrollbars stop working and disappear. Howeevr If i dont use TabbedView, scrollbars work fine.
Can anyone tell me whats wrong ? I need TabbedView of mdiarea with working scrollbars.
I am using Python 2.7,PyQT 4.8.4/PySide 1.2.1 on win7.
Python Sample Code:
Comment the line self.mdiArea.setViewMode to see example working.
import sys
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName( "MainWindow" )
MainWindow.resize(500, 400)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName( "centralwidget" )
self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName( "verticalLayout" )
self.mdiArea = QtGui.QMdiArea(self.centralwidget)
self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.mdiArea.setActivationOrder(QtGui.QMdiArea.CreationOrder)
self.mdiArea.setViewMode(QtGui.QMdiArea.TabbedView)
self.mdiArea.setTabsClosable(True)
self.mdiArea.setTabsMovable(True)
self.mdiArea.setObjectName( "mdiArea" )
self.verticalLayout.addWidget(self.mdiArea)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 508, 21))
self.menubar.setObjectName( "menubar" )
self.menuAdd = QtGui.QMenu(self.menubar)
self.menuAdd.setObjectName( "menuAdd" )
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName( "statusbar" )
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menuAdd.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle( "MainWindow" )
self.menuAdd.setTitle( "&Add Form" )
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName( ("Form"))
Form.resize(400, 800)
self.gridLayout = QtGui.QGridLayout(Form)
self.gridLayout.setObjectName( ("gridLayout"))
self.plainTextEdit = QtGui.QPlainTextEdit(Form)
self.plainTextEdit.setMinimumSize(QtCore.QSize(0, 731))
self.plainTextEdit.setObjectName( ("plainTextEdit"))
self.gridLayout.addWidget(self.plainTextEdit, 0, 0, 1, 1)
self.buttonBox = QtGui.QDialogButtonBox(Form)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName( ("buttonBox"))
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle( "Lengthy subwindow" )
self.plainTextEdit.setPlainText( "Lengthy Form" )
class MyApp(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def Add_Subwindow(self):
widget = QtGui.QWidget()
self.subwin_abq = Ui_Form()
self.subwin_abq.setupUi(widget)
self.subwindow = QtGui.QMdiSubWindow(self.ui.mdiArea)
widget.setParent(self.subwindow)
self.subwindow.setWidget(widget)
self.subwindow.setWindowTitle("testing")
self.ui.mdiArea.addSubWindow(self.subwindow)
widget.show()
self.subwindow.show()
self.subwindow.widget().show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
window.Add_Subwindow()
sys.exit(app.exec_())

Just wanted to say thanks for the code in OP - was looking for a simple MDI example in PyQT, and yours helped I lot! I don't exactly have an answer, but this is what I can note so far: I have Python 2.7,PyQT 4.8.3, and just with commenting the setTabsClosable and setTabsMovable line, I could get your example to show like this:
I downloaded designer-qt4 and looked there about QMdiArea, there seems to be nothing called TabbedView. So I found this:
QtWidgets 5.0: QMdiArea Class | Documentation | Qt Project
enum ViewMode { SubWindowView, TabbedView }
This enum describes the view mode of the area; i.e. how sub-windows will be displayed.
SubWindowView 0 Display sub-windows with window frames (default).
TabbedView 1 Display sub-windows with tabs in a tab bar.
documentMode: This property holds whether the tab bar is set to document mode in tabbed view mode.
The way I read this: either you get to display subwindows in MDI fashion (so they can be larger than the window, with scrollbars) or the subwindows become tabs in tabbed view - and there the size of the subwindow doesn't matter anymore, so it expands to take up the available tabbed area. Also, in your code, self.ui.mdiArea.documentMode() returns False in both cases.
I also added this snippet at end of your MyApp.Add_Subwindow():
sp = self.subwindow.sizePolicy()
print sp.__dict__
#print dir(sp)
for attr in dir(sp):
try:
print "obj.%s = %s" % (attr, getattr(sp, attr))
except: pass
This dumps some interesting data (I'm not sure if those are object properties, though):
obj.ButtonBox = 2
obj.CheckBox = 4
obj.ComboBox = 8
obj.ControlType = <class 'PyQt4.QtGui.ControlType'>
obj.ControlTypes = <class 'PyQt4.QtGui.ControlTypes'>
obj.DefaultType = 1
obj.ExpandFlag = 2
obj.Expanding = 7
obj.Fixed = 0
obj.Frame = 16
...
... but also these don't change in running tabbed vs. MDI mode.
So, maybe this is the intended behavior? If that is so, that would mean you'd have to find something like a "lone" tab display widget; add programmatically several QMdiAreas; hide all of them but the default one at start; and then bind a click on respective tabs to show "their" QMdiArea and hide the others (but needless to say, I haven't tested it).

Related

How can I execute different callbacks for different Tkinter [sub-] Menus?

I have a Tkinter GUI with a main menubar, using Tkinter's Menu widget. I want to execute code prior to posting a submenu (another Menu item cascaded from it via .add_cascade()), so that I can dynamically change its contents before it's shown. I have this working using Menu's postcommand argument, but I noticed a huge inefficiency using that; the postcommand callback for all submenus are called when clicking on any one submenu, not just the specific submenu that was created to have the callback. Even clicking on the menu bar where there are no menu items also executes all callbacks, even though no submenus are created.
Is this expected behavior from the Menu module and its postcommand argument? I don't understand why this still happens after creating separate Menu instances for the dropdowns.
I've tried hooking into Tk.Menu's native methods, but none of them are called when simply clicking on the menubar items to bring up a cascaded Menu. And even though .add_cascade() accepts a 'command' argument, it only calls the callable provided by that if .add_cascade()'s 'menu' argument is not included or if it's a lambda expression (both of which result in no submenu being displayed when you click on the item). (You can see this using the test() function, below.)
Here's a simple app showing this behavior:
import Tkinter as Tk
import time
def test(): print 'test'
class firstMenu( Tk.Menu ):
def __init__( self, parent, tearoff=False ):
Tk.Menu.__init__( self, parent, tearoff=tearoff, postcommand=self.repopulate )
def repopulate( self ):
print 'repopulating firstMenu'
time.sleep( 2 ) # Represents some thinking/processing
# Clear all current population
self.delete( 0, 'last' )
# Add the new menu items
self.add_command( label='Option 1.1' )
self.add_command( label='Option 1.2' )
class secondMenu( Tk.Menu ):
def __init__( self, parent, tearoff=False ):
Tk.Menu.__init__( self, parent, tearoff=tearoff, postcommand=self.repopulate )
def repopulate( self ):
print 'repopulating secondMenu'
time.sleep( 2 ) # Represents some thinking/processing
# Clear all current population
self.delete( 0, 'last' )
# Add the new menu items
self.add_command( label='Option 2.1' )
self.add_command( label='Option 2.2' )
class Gui( object ):
def __init__( self ): # Create the TopLevel window
root = Tk.Tk()
root.withdraw() # Keep the GUI minimized until it is fully generated
root.title( 'Menu Test' )
# Create the GUI's main program menus
menubar = Tk.Menu( root )
menubar.add_cascade( label='File', menu=firstMenu( menubar ), command=test )
menubar.add_cascade( label='Settings', menu=secondMenu( menubar ) )
root.config( menu=menubar )
root.deiconify() # Brings the GUI to the foreground now that rendering is complete
# Start the GUI's mainloop
root.mainloop()
root.quit()
if __name__ == '__main__': Gui()
If you click on anywhere on the menubar, BOTH postcommand callbacks are called. I need (and would expect) only one of them to be called when you click on the respective Menu item.
I'm not sure if it's relevant, but I also use the same menu items as context-menus over another widget. So their .post() method also needs to be able to trigger the same callback before the menu is displayed.
Thanks in advance if you have any insight.
This was a really tricky problem, but I finally found a solution. After a lot of searching, numerous failed experiments, and more searching, I came across the virtual event <<MenuSelect>> and this pivotal line of code: print tk.call(event.widget, "index", "active"), pointed out by Michael O' Donnell, here.
The first weird part about trying to use this, is that event.widget isn't an instance of a widget object in this case, it's a tcl/tk path name string, e.g. '.#37759048L'. (This seems to be a bug in Tkinter, as even other virtual events I've tested -TreeviewSelect and NotebookTabChanged- include actual widget instances, as expected.) Regardless, the tcl/tk string can be used by the print tk.call(event.widget, "index", "active") command; that returns the index of the currently active menu item, which is huge.
The second issue that comes up with using the MenuSelect event is that it's called multiple times when traversing the menus normally. Clicking on a menu item calls it twice, and moving the mouse to a neighboring menu item, or moving the mouse to a submenu and then back to the main menu item, will also call it twice. Leaving the menu can as well. But this can be cleaned up nicely by adding a flag to the Menu classes and a little logic to the event handler. Here's the full solution:
import Tkinter as Tk
import time
class firstMenu( Tk.Menu ):
def __init__( self, parent, tearoff=False ):
Tk.Menu.__init__( self, parent, tearoff=tearoff )
self.optionNum = 0 # Increments each time the menu is show, so we can see it update
self.open = False
def repopulate( self ):
print 'repopulating firstMenu'
# Clear all current population
self.delete( 0, 'last' )
# Add the new menu items
self.add_command( label='Option 1.' + str(self.optionNum+1) )
self.add_command( label='Option 1.' + str(self.optionNum+2) )
self.optionNum += 2
class secondMenu( Tk.Menu ):
def __init__( self, parent, tearoff=False ):
Tk.Menu.__init__( self, parent, tearoff=tearoff )
self.optionNum = 0 # Increments each time the menu is show, so we can see it update
self.open = False
def repopulate( self ):
print 'repopulating secondMenu'
# Clear all current population
self.delete( 0, 'last' )
# Add the new menu items
self.add_command( label='Option 2.' + str(self.optionNum+1) )
self.add_command( label='Option 2.' + str(self.optionNum+2) )
self.optionNum += 2
class Gui( object ):
def __init__( self ): # Create the TopLevel window
self.root = Tk.Tk()
self.root.withdraw() # Keep the GUI minimized until it is fully generated
self.root.title( 'Menu Tests' )
# Create the GUI's main program menus
self.menubar = Tk.Menu( self.root )
self.menubar.add_cascade( label='File', menu=firstMenu( self.menubar ) )
self.menubar.add_cascade( label='Settings', menu=secondMenu( self.menubar ) )
self.root.config( menu=self.menubar )
self.root.deiconify() # Brings the GUI to the foreground now that rendering is complete
# Add an event handler for activation of the main menus
self.menubar.bind( "<<MenuSelect>>", self.updateMainMenuOptions )
# Start the GUI's mainloop
self.root.mainloop()
self.root.quit()
def updateMainMenuOptions( self, event ):
activeMenuIndex = self.root.call( event.widget, "index", "active" ) # event.widget is a path string, not a widget instance
if isinstance( activeMenuIndex, int ):
activeMenu = self.menubar.winfo_children()[activeMenuIndex]
if not activeMenu.open:
# Repopulate the menu's contents
activeMenu.repopulate()
activeMenu.open = True
else: # The active menu index is 'none'; all menus are closed
for menuWidget in self.menubar.winfo_children():
menuWidget.open = False
if __name__ == '__main__': Gui()
The end result is that each menu's code to generate its contents, via .repopulate(), is only called once, and only if that particular menu is actually going to be shown. The method isn't called again until the whole main menu is left and re-opened. Works when navigating via keyboard too.

Tkinter windows decoration

I am using Tkinter for Building GUI for my python module but i don't want default windows title bar and border. I used "root.overrideredirect(True)" but with "overrideredirect()" I am losing control from my window like resizing and shifting from one place to another place. When ever I run my GUI its shows on top-left corner of my window.
from Tkinter import *
version = "v0.1"
def getinfo():
lab1 = Label(fram, text = "Your name :")
lab2 = Label(fram, text = "Your Password : ")
lab1.grid(row =1,sticky=W)
lab2.grid(row =2,sticky=W)
def Exit():
sys.exit(1)
def btn2():
btn_show = Button(fram,text = "Show")
btn_show.grid(row = 9, sticky = W)
btn_hide = Button(fram, text = "Hide")
btn_hide.grid(row = 9,column = 2, sticky = W)
root = Tk()
root.overrideredirect(True)
root.geometry("450x300")
fram = Frame(root)
fram.grid()
default_labels()
btn2()
root.mainloop()
Here is a basic example of how you can build your title bar and be able to move your window around. It is not perfect but should serve as a good starting point for your.
import Tkinter as tk
root = tk.Tk()
root.overrideredirect(True)
root.geometry("450x300")
root.config(background="darkblue")
root.columnconfigure(0, weight=1)
def move_event(event):
root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
title_frame = tk.Frame(root)
title_frame.grid(row=0, column=0, sticky="ew")
title_frame.columnconfigure(0, weight=1)
title_frame.bind('<B1-Motion>', move_event)
tk.Label(title_frame, text="Custom title bar").grid(row=0, column=0, sticky="w")
tk.Button(title_frame, text="-").grid(row=0, column=1, sticky="e")
tk.Button(title_frame, text="[]").grid(row=0, column=2, sticky="e")
tk.Button(title_frame, text="X", command=root.destroy).grid(row=0, column=3, sticky="e")
tk.Label(root, text="Test window!", fg="white", bg="darkblue").grid(row=1, column=0)
root.mainloop()
Results:
The resulting window can be dragged around though a little bit off as the window will want to move relative to the mouse position.
Yes, that's exactly what overrideredirect does. You will have to add your own bindings to allow for interactively moving and resizing the window.
The answer to the question Tkinter: windows without title bar but resizable shows how to add resizing.
The answer to the question Python/Tkinter: Mouse drag a window without borders, eg. overridedirect(1) shows how to handle the moving of the window.

How to enable autoscroll in Qtableview like in Qtextedit?

I'm using QtableView and QStandardItemModel to display logs on GUI to maintain proper spacing and filter logs. I created model and inserted data into it. Used QSortFilterProxyModel for filter strings.
self.tableView = QtGui.QTableView(self)
self.model = QtGui.QStandardItemModel(self)
self.proxy = QtGui.QSortFilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)
In a sec, nearly 100 logs are expected and should be shown on GUI. When new logs are appended, the view isn't auto scrolling and the slider stays only at the top. It doesn't give live feel for logging and user need to scroll manually to the end. So to overcome this, i used following syntax,
self.model.rowsInserted.connect(lambda: QtCore.QTimer.singleShot(5, self.tableView.scrollToBottom))
It gives live feel for logs, but the slider remains always in bottom and i'm not able to scroll up to see previous logs. Whenever i try to move the slider, it immediately comes down to bottom again. So this syntax doesn't meet my requirement. In QTextEdit, auto scrolling is proper and user friendly. I want the same scenario here on QtableView. Is there any alternative for auto scrolling which resembles like QTextEdit ?
To get the required behaviour, you can auto-scroll only when the previous scroll position is at the bottom. That way, whenever the user scrolls away from the bottom, auto-scrolling will be disabled; but when they scroll back to the bottom, auto-scrolling will be re-enabled. (NB: to quickly re-enable auto-scroll, right-click the scrollbar and select "Bottom" from the context menu).
Here is a simple demo:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.table = QtGui.QTableView(self)
self.model = QtGui.QStandardItemModel(self)
self.table.setModel(self.model)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table)
self._scroll = True
self.model.rowsAboutToBeInserted.connect(self.beforeInsert)
self.model.rowsInserted.connect(self.afterInsert)
def beforeInsert(self):
vbar = self.table.verticalScrollBar()
self._scroll = vbar.value() == vbar.maximum()
def afterInsert(self):
if self._scroll:
self.table.scrollToBottom()
def addRow(self):
self.model.appendRow([QtGui.QStandardItem(c) for c in 'ABC'])
if __name__ == '__main__':
app = QtGui.QApplication([''])
window = Window()
window.setGeometry(500, 50, 400, 300)
window.show()
timer = QtCore.QTimer()
timer.timeout.connect(window.addRow)
timer.start(200)
app.exec_()

Tabbed GUI keeps hanging on entry box

I have a very primitive GUI built with Tkinter. This is my first attempt at a GUI so I am struggling to understand what is going on (and with syntax). Here is what I have:
from __future__ import division, print_function
import os, ttk, tkFileDialog, Tkconstants
import Tkinter as tk
import datetime as dt
Tkinter = tk
# define OS version
version = '0.0.2'
class TestGUI(tk.Tk):
def __init__(self,parent):
tk.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
# try building list of instruments and sites
if os.path.isfile('config'):
with open(''config','r') as config:
config = dict( [(r.split('=')[0].strip(), r.split('=')[1].strip()) for r in config.read().split('\n') if r[0]<>'#'] )
self.datapath = config['datapath']
else:
self.datapath = '../'
def initialize(self):
self.grid()
# set up tabs
self.geometry( "%dx%d+%d+%d" % (1500, 900, 200, 50) )
nb = ttk.Notebook(self)
nb.pack(fill='both',expand='yes')
f1 = tk.Frame(bg='green')
f2 = tk.Frame(bg='blue')
f3 = tk.Frame(bg='red')
f1.grid()
f2.grid()
f3.grid()
nb.add(f1, text='General'.ljust(12,' '))
nb.add(f2, text='Plot'.ljust(12,' '))
nb.add(f3, text='Analysis'.ljust(12,' '))
button = tk.Button(f2,text='I AM A BUTTON!')
button.pack(side='left', anchor='nw', padx=3, pady=5)
# insert button and text box for specifying data location
path_button = tk.Button(f1,text='Browse',command=self.askdirectory).pack(side='left', anchor='nw', padx=10, pady=15)
self.path_entry = tk.StringVar()
self.entry = tk.Entry(f1,textvariable=self.path_entry)
self.entry.grid(column=12,row=8,columnspan=10)
self.entry.bind("<Return>", self.OnPressEnter)
self.path_entry.set(u"Sites directory path...")
def OnButtonClick(self):
print("You clicked the button !")
def OnPressEnter(self,event):
print("You pressed enter !")
def askdirectory(self):
"""Returns a selected directoryname."""
self.datapath = tkFileDialog.askdirectory()
self.path_entry.set(self.datapath)
if __name__ == "__main__":
app = TestGUI(None)
app.title(version)
app.mainloop()
My problem is centered around the addition of an entry box here:
self.path_entry = tk.StringVar()
self.entry = tk.Entry(f1,textvariable=self.path_entry)
self.entry.grid(column=12,row=8,columnspan=10)
self.entry.bind("<Return>", self.OnPressEnter)
self.path_entry.set(u"Sites directory path...")
If I run the code as-is, it just hangs (it also hangs if I use "f2"; I suspect it is getting caught in the infinite loop without actually doing anything). However, if I change the parent from "f1" to "f3" or it works (the entry box is now in frame 3 instead of frame 1, but it at least does not hang on me). There is another issue even when f3 is used: the entry box's width/position never change despite my changing of column/row/columnspan values.
Does anyone know why the code is hanging when I specify "f1" or "f2" and how to fix it?
Does anyone know why my entry box position/size is not changing?
You have put widgets in f1 and f2 using the pack geometry manager:
button = tk.Button(f2,text='I AM A BUTTON!')
button.pack(side='left', anchor='nw', padx=3, pady=5)
#and
path_button = tk.Button(f1,text='Browse',command=self.askdirectory).pack(side='left', anchor='nw', padx=10, pady=15)
Mixing geometry managers can lead to your program hanging, so using grid to put in the Entry does exactly that.
From effbot.org:
Warning: Never mix grid and pack in the same master window. Tkinter will happily spend the rest of your lifetime trying to negotiate a solution that both managers are happy with. Instead of waiting, kill the application, and take another look at your code. A common mistake is to use the wrong parent for some of the widgets.
The problem you describe of the Entry not changing position is because it is the only widget there, so the row(s) and column(s) in which the entry is are the only ones which do not have a width and height of 0. To make rows and columns without widgets take up space, use grid_rowconfigure(index, weight=x) where x is non-zero. An example is given in this answer.
Again from effbot.org:
weight=
A relative weight used to distribute additional space between rows. A row with the weight 2 will grow twice as fast as a row with weight 1. The default is 0, which means that the row will not grow at all.

Custom QDockWidget display

How would you get a display of dockwidgets/centralwidget in which the dockwidget in the Qt::BottomDockWidgetArea or Qt::TopDockWidgetArea doesn't take Qt::LeftDockWidgetArea nor Qt::RighDockWidgetArea space?
This is the actual display, with 2 dockwidgets and the central widget at the top right:
This would be the preferred display:
you probably should use the QMainWindow's corner functionality to get the behavior you wanted.
Something like this may work (can't test whether its compiles, sorry):
mainWindow->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
mainWindow->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
mainWindow->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
mainWindow->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
See:
* QMainWindow::setCorner(...)
It seems that the (slightly bizarre) trick to get this working is to set a QMainWindow as the central widget of your main window.
Here's a PyQt port of this Qt FAQ example:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setWindowTitle('Extended Side Dock Areas')
self.window = QtGui.QMainWindow(self)
self.window.setCentralWidget(QtGui.QTextEdit(self.window))
self.window.setWindowFlags(QtCore.Qt.Widget)
self.setCentralWidget(self.window)
self.dock1 = QtGui.QDockWidget(self.window)
self.dock1.setWidget(QtGui.QTextEdit(self.dock1))
self.window.addDockWidget(
QtCore.Qt.BottomDockWidgetArea, self.dock1)
self.dock2 = QtGui.QDockWidget(self)
self.dock2.setAllowedAreas(
QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.RightDockWidgetArea)
self.dock2.setWidget(QtGui.QLabel('Left Dock Area', self.dock2))
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.dock2)
self.dock3 = QtGui.QDockWidget(self)
self.dock3.setWidget(QtGui.QLabel('Right Dock Area', self.dock3))
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock3)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())