2 # -*- coding: utf-8 -*-
4 #Copyright 2008-2011 Carl Gherardi
5 #This program is free software: you can redistribute it and/or modify
6 #it under the terms of the GNU Affero General Public License as published by
7 #the Free Software Foundation, version 3 of the License.
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 #GNU General Public License for more details.
14 #You should have received a copy of the GNU Affero General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #In the "official" distribution you can find the license in agpl-3.0.txt.
19 _
= L10n
.get_translation()
34 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
35 log
= logging
.getLogger("maintdbs")
44 # columns in liststore:
56 # columns in listview:
66 def __init__(self
, config
, mainwin
, dia
):
68 self
.main_window
= mainwin
72 #self.dia.set_modal(True)
73 self
.vbox
= self
.dia
.vbox
74 self
.action_area
= self
.dia
.action_area
75 #gtk.Widget.set_size_request(self.vbox, 700, 400);
77 h
= gtk
.HBox(False, spacing
=3)
79 self
.vbox
.pack_start(h
, padding
=3)
81 vbtn
= gtk
.VBox(True, spacing
=3)
83 h
.pack_start(vbtn
, expand
=False, fill
=False, padding
=2)
85 # list of databases in self.config.supported_databases:
86 self
.liststore
= gtk
.ListStore(str, str, str, str, str
87 ,str, str, str, str, str)
88 # dbms, name, comment, user, passwd, host, "", default_icon, status, icon
89 # this is how to add a filter:
91 # # Creation of the filter, from the model
92 # filter = self.liststore.filter_new()
93 # filter.set_visible_column(1)
95 # # The TreeView gets the filter as model
96 # self.listview = gtk.TreeView(filter)
97 self
.listview
= gtk
.TreeView(model
=self
.liststore
)
98 self
.listview
.set_grid_lines(gtk
.TREE_VIEW_GRID_LINES_NONE
)
102 self
.scrolledwindow
= gtk
.ScrolledWindow()
103 self
.scrolledwindow
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
104 self
.scrolledwindow
.add(self
.listview
)
105 h
.pack_start(self
.scrolledwindow
, expand
=True, fill
=True, padding
=0)
107 add_button
= SideButton(_("_Add"), gtk
.STOCK_ADD
)
108 add_button
.connect("clicked", self
.addDB
, None)
109 vbtn
.pack_start(add_button
, False, False, 3)
111 refresh_button
= SideButton(_("_Refresh"), gtk
.STOCK_REFRESH
)
112 refresh_button
.connect("clicked", self
.refresh
, None)
113 vbtn
.pack_start(refresh_button
, False, False, 3)
115 col
= self
.addTextColumn(_("Type"), 0, False)
116 col
= self
.addTextColumn(_("Name"), 1, False)
117 col
= self
.addTextColumn(_("Description"), 2, True)
118 col
= self
.addTextColumn(_("Username"), 3, True)
119 col
= self
.addTextColumn(_("Password"), 4, True)
120 col
= self
.addTextColumn(_("Host"), 5, True)
121 col
= self
.addTextObjColumn(_("Open"), 6, 6)
122 col
= self
.addTextObjColumn(_("Status"), 7, 8)
124 #self.listview.get_selection().set_mode(gtk.SELECTION_SINGLE)
125 #self.listview.get_selection().connect("changed", self.on_selection_changed)
126 self
.listview
.add_events(gtk
.gdk
.BUTTON_PRESS_MASK
)
127 self
.listview
.connect('button_press_event', self
.selectTest
)
132 #self.dia.connect('response', self.dialog_response_cb)
134 err
= traceback
.extract_tb(sys
.exc_info()[2])[-1]
135 print 'guidbmaint: '+ err
[2] + "(" + str(err
[1]) + "): " + str(sys
.exc_info()[1])
137 def dialog_response_cb(self
, dialog
, response_id
):
138 # this is called whether close button is pressed or window is closed
139 log
.info('dialog_response_cb: response_id='+str(response_id
))
146 def get_dialog(self
):
149 def addTextColumn(self
, title
, n
, editable
=False):
150 col
= gtk
.TreeViewColumn(title
)
151 self
.listview
.append_column(col
)
153 cRender
= gtk
.CellRendererText()
154 cRender
.set_property("wrap-mode", pango
.WRAP_WORD_CHAR
)
155 cRender
.set_property('editable', editable
)
156 cRender
.connect('edited', self
.edited_cb
, (self
.liststore
,n
))
158 col
.pack_start(cRender
, True)
159 col
.add_attribute(cRender
, 'text', n
)
160 col
.set_max_width(1000)
161 col
.set_spacing(0) # no effect
162 self
.listcols
.append(col
)
163 col
.set_clickable(True)
164 col
.connect("clicked", self
.sortCols
, n
)
168 def edited_cb(self
, cell
, path
, new_text
, user_data
):
169 liststore
, col
= user_data
170 log
.info('edited_cb: col = '+str(col
))
172 name
= self
.liststore
[path
][self
.COL_NAME
]
174 # Validate new value (only for dbms so far, but dbms now not updateable so no validation at all!)
175 #if col == self.COL_DBMS:
176 # if new_text not in Configuration.DATABASE_TYPES:
180 self
.liststore
[path
][col
] = new_text
182 self
.config
.set_db_parameters( db_server
= self
.liststore
[path
][self
.COL_DBMS
]
184 , db_desc
= self
.liststore
[path
][self
.COL_DESC
]
185 , db_ip
= self
.liststore
[path
][self
.COL_HOST
]
186 , db_user
= self
.liststore
[path
][self
.COL_USER
]
187 , db_pass
= self
.liststore
[path
][self
.COL_PASS
] )
191 def check_new_name(self
, path
, new_text
):
193 for i
,db
in enumerate(self
.liststore
):
194 if i
!= path
and new_text
== db
[self
.COL_NAME
]:
196 #TODO: popup an error message telling user names must be unique
199 def addTextObjColumn(self
, title
, viewcol
, storecol
, editable
=False):
200 col
= gtk
.TreeViewColumn(title
)
201 self
.listview
.append_column(col
)
203 cRenderT
= gtk
.CellRendererText()
204 cRenderT
.set_property("wrap-mode", pango
.WRAP_WORD_CHAR
)
205 col
.pack_start(cRenderT
, False)
206 col
.add_attribute(cRenderT
, 'text', storecol
)
208 cRenderP
= gtk
.CellRendererPixbuf()
209 col
.pack_start(cRenderP
, True)
210 col
.add_attribute(cRenderP
, 'stock-id', storecol
+1)
212 col
.set_max_width(1000)
213 col
.set_spacing(0) # no effect
214 self
.listcols
.append(col
)
216 col
.set_clickable(True)
217 col
.connect("clicked", self
.sortCols
, viewcol
)
220 def selectTest(self
, widget
, event
):
221 if event
.button
== 1: # and event.type == gtk.gdk._2BUTTON_PRESS:
222 pthinfo
= self
.listview
.get_path_at_pos( int(event
.x
), int(event
.y
) )
223 if pthinfo
is not None:
224 path
, col
, cellx
, celly
= pthinfo
226 if col
== self
.listcols
[self
.COL_DFLT
]:
227 if self
.liststore
[row
][self
.MODEL_STATUS
] == 'ok' and self
.liststore
[row
][self
.MODEL_DFLTIC
] is None:
228 self
.setDefaultDB(row
)
230 def setDefaultDB(self
, row
):
231 print "set new defaultdb:", row
, self
.liststore
[row
][self
.MODEL_NAME
]
232 for r
in xrange(len(self
.liststore
)):
234 self
.liststore
[r
][self
.MODEL_DFLTIC
] = gtk
.STOCK_APPLY
237 self
.liststore
[r
][self
.MODEL_DFLTIC
] = None
240 self
.config
.set_db_parameters( db_server
= self
.liststore
[r
][self
.COL_DBMS
]
241 , db_name
= self
.liststore
[r
][self
.COL_NAME
]
242 , db_desc
= self
.liststore
[r
][self
.COL_DESC
]
243 , db_ip
= self
.liststore
[r
][self
.COL_HOST
]
244 , db_user
= self
.liststore
[r
][self
.COL_USER
]
245 , db_pass
= self
.liststore
[r
][self
.COL_PASS
]
254 self
.liststore
.clear()
256 dia
= InfoBox( parent
=self
.dia
, str1
=_('Testing database connections ... ') )
257 while gtk
.events_pending():
261 # want to fill: dbms, name, comment, user, passwd, host, default, status, icon
262 for name
in self
.config
.supported_databases
: #db_ip/db_user/db_pass/db_server
263 dbms
= self
.config
.supported_databases
[name
].db_server
# mysql/postgresql/sqlite
264 dbms_num
= self
.config
.get_backend(dbms
) # 2 / 3 / 4
265 comment
= self
.config
.supported_databases
[name
].db_desc
270 user
= self
.config
.supported_databases
[name
].db_user
271 passwd
= self
.config
.supported_databases
[name
].db_pass
272 host
= self
.config
.supported_databases
[name
].db_ip
273 default
= (name
== self
.config
.db_selected
)
275 if default
: default_icon
= gtk
.STOCK_APPLY
277 status
, err_msg
, icon
= GuiDatabase
.testDB(self
.config
, dbms
, dbms_num
, name
, user
, passwd
, host
)
281 iter = self
.liststore
.append( (dbms
, name
, comment
, user
, passwd
, host
, "", default_icon
, status
, icon
) )
283 dia
.add_msg( _("finished."), False, True )
285 self
.scrolledwindow
.show()
287 self
.dia
.set_focus(self
.listview
)
292 err
= traceback
.extract_tb(sys
.exc_info()[2])[-1]
293 print _('loadDbs error: ')+str(dbms_num
)+','+host
+','+name
+','+user
+','+passwd
+' failed: ' \
294 + err
[2] + "(" + str(err
[1]) + "): " + str(sys
.exc_info()[1])
296 def sortCols(self
, col
, n
):
298 log
.info('sortcols n='+str(n
))
299 if not col
.get_sort_indicator() or col
.get_sort_order() == gtk
.SORT_ASCENDING
:
300 col
.set_sort_order(gtk
.SORT_DESCENDING
)
302 col
.set_sort_order(gtk
.SORT_ASCENDING
)
303 self
.liststore
.set_sort_column_id(n
, col
.get_sort_order())
304 #self.liststore.set_sort_func(n, self.sortnums, (n,grid))
305 log
.info('sortcols len(listcols)='+str(len(self
.listcols
)))
306 for i
in xrange(len(self
.listcols
)):
307 log
.info('sortcols i='+str(i
))
308 self
.listcols
[i
].set_sort_indicator(False)
309 self
.listcols
[n
].set_sort_indicator(True)
310 # use this listcols[col].set_sort_indicator(True)
311 # to turn indicator off for other cols
313 err
= traceback
.extract_tb(sys
.exc_info()[2])
314 print _("***sortCols error: ") + str(sys
.exc_info()[1])
315 print "\n".join( [e
[0]+':'+str(e
[1])+" "+e
[2] for e
in err
] )
316 log
.info(_('sortCols error: ') + str(sys
.exc_info()) )
318 def refresh(self
, widget
, data
):
321 def addDB(self
, widget
, data
):
322 adb
= AddDB(self
.config
, self
.dia
)
323 (status
, err_msg
, icon
, dbms
, dbms_num
, name
, comment
, user
, passwd
, host
) = adb
.run()
328 iter = self
.liststore
.append( (dbms
, name
, comment
, user
, passwd
, host
, "", None, status
, icon
) )
330 # keep config save code in line with edited_cb()? call common routine?
333 # Validate new value (only for dbms so far, but dbms now not updateable so no validation at all!)
334 #if col == self.COL_DBMS:
335 # if new_text not in Configuration.DATABASE_TYPES:
339 self
.config
.add_db_parameters( db_server
= dbms
350 def testDB(config
, dbms
, dbms_num
, name
, user
, passwd
, host
):
355 sql
= SQL
.Sql(db_server
=dbms
)
356 db
= Database
.Database(config
, sql
= sql
, autoconnect
= False)
357 # try to connect to db, set status and err_msg if it fails
359 # is creating empty db for sqlite ... mod db.py further?
360 # add noDbTables flag to db.py?
361 log
.debug("testDB: " + _("trying to connect to:") + " %s/%s, %s, %s/%s" % (str(dbms_num
),dbms
,name
,user
,passwd
))
362 db
.connect(backend
=dbms_num
, host
=host
, database
=name
, user
=user
, password
=passwd
, create
=False)
364 log
.debug(_("connected ok"))
366 icon
= gtk
.STOCK_APPLY
367 if db
.wrongDbVersion
:
369 icon
= gtk
.STOCK_INFO
371 log
.debug(_("not connected but no exception"))
372 except Exceptions
.FpdbMySQLAccessDenied
:
373 err_msg
= _("MySQL Server reports: Access denied. Are your permissions set correctly?")
375 icon
= gtk
.STOCK_CANCEL
376 except Exceptions
.FpdbMySQLNoDatabase
:
377 err_msg
= _("MySQL client reports: 2002 or 2003 error. Unable to connect - ") \
378 + _("Please check that the MySQL service has been started")
380 icon
= gtk
.STOCK_CANCEL
381 except Exceptions
.FpdbPostgresqlAccessDenied
:
382 err_msg
= _("PostgreSQL Server reports: Access denied. Are your permissions set correctly?")
384 except Exceptions
.FpdbPostgresqlNoDatabase
:
385 err_msg
= _("PostgreSQL client reports: Unable to connect - ") \
386 + _("Please check that the PostgreSQL service has been started")
388 icon
= gtk
.STOCK_CANCEL
390 # add more specific exceptions here if found (e.g. for sqlite?)
391 err
= traceback
.extract_tb(sys
.exc_info()[2])[-1]
392 err_msg
= err
[2] + "(" + str(err
[1]) + "): " + str(sys
.exc_info()[1])
394 icon
= gtk
.STOCK_CANCEL
396 log
.info( _('db connection to %s, %s, %s, %s, %s failed: %s') % (str(dbms_num
), host
, name
, user
, passwd
, err_msg
))
398 return( status
, err_msg
, icon
)
401 class AddDB(gtk
.Dialog
):
403 def __init__(self
, config
, parent
):
404 log
.debug(_("AddDB starting"))
405 self
.dbnames
= { 'Sqlite' : Configuration
.DATABASE_TYPE_SQLITE
406 , 'MySQL' : Configuration
.DATABASE_TYPE_MYSQL
407 , 'PostgreSQL' : Configuration
.DATABASE_TYPE_POSTGRESQL
410 # create dialog and add icon and label
411 super(AddDB
,self
).__init
__( parent
=parent
412 , flags
=gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
413 , title
=_("Add New Database")
414 , buttons
= (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
415 ,gtk
.STOCK_SAVE
, gtk
.RESPONSE_ACCEPT
)
417 self
.set_default_size(450, 280)
418 #self.connect('response', self.response_cb)
420 t
= gtk
.Table(5, 3, True)
421 self
.vbox
.pack_start(t
, expand
=False, fill
=False, padding
=3)
423 l
= gtk
.Label( _("DB Type") )
424 l
.set_alignment(1.0, 0.5)
425 t
.attach(l
, 0, 1, 0, 1, xpadding
=3)
426 self
.cb_dbms
= gtk
.combo_box_new_text()
427 for s
in ('Sqlite',): # keys(self.dbnames):
428 self
.cb_dbms
.append_text(s
)
429 self
.cb_dbms
.set_active(0)
430 t
.attach(self
.cb_dbms
, 1, 3, 0, 1, xpadding
=3)
431 self
.cb_dbms
.connect("changed", self
.db_type_changed
, None)
433 l
= gtk
.Label( _("DB Name") )
434 l
.set_alignment(1.0, 0.5)
435 t
.attach(l
, 0, 1, 1, 2, xpadding
=3)
436 self
.e_db_name
= gtk
.Entry()
437 self
.e_db_name
.set_width_chars(15)
438 t
.attach(self
.e_db_name
, 1, 3, 1, 2, xpadding
=3)
439 self
.e_db_name
.connect("focus-out-event", self
.db_name_changed
, None)
441 l
= gtk
.Label( _("DB Description") )
442 l
.set_alignment(1.0, 0.5)
443 t
.attach(l
, 0, 1, 2, 3, xpadding
=3)
444 self
.e_db_desc
= gtk
.Entry()
445 self
.e_db_desc
.set_width_chars(15)
446 t
.attach(self
.e_db_desc
, 1, 3, 2, 3, xpadding
=3)
448 self
.l_username
= gtk
.Label( _("Username") )
449 self
.l_username
.set_alignment(1.0, 0.5)
450 t
.attach(self
.l_username
, 0, 1, 3, 4, xpadding
=3)
451 self
.e_username
= gtk
.Entry()
452 self
.e_username
.set_width_chars(15)
453 t
.attach(self
.e_username
, 1, 3, 3, 4, xpadding
=3)
455 self
.l_password
= gtk
.Label( _("Password") )
456 self
.l_password
.set_alignment(1.0, 0.5)
457 t
.attach(self
.l_password
, 0, 1, 4, 5, xpadding
=3)
458 self
.e_password
= gtk
.Entry()
459 self
.e_password
.set_width_chars(15)
460 t
.attach(self
.e_password
, 1, 3, 4, 5, xpadding
=3)
462 self
.l_host
= gtk
.Label( _("Host Computer") )
463 self
.l_host
.set_alignment(1.0, 0.5)
464 t
.attach(self
.l_host
, 0, 1, 5, 6, xpadding
=3)
465 self
.e_host
= gtk
.Entry()
466 self
.e_host
.set_width_chars(15)
467 self
.e_host
.set_text("localhost")
468 t
.attach(self
.e_host
, 1, 3, 5, 6, xpadding
=3)
473 # hide username/password fields as not used by sqlite
474 self
.l_username
.hide()
475 self
.e_username
.hide()
476 self
.l_password
.hide()
477 self
.e_password
.hide()
480 response
= super(AddDB
,self
).run()
481 log
.debug(_("addDB.run: response is %s, accept is %s") % (str(response
), str(int(gtk
.RESPONSE_ACCEPT
))))
483 ok
,retry
= False,True
484 while response
== gtk
.RESPONSE_ACCEPT
:
485 ok
,retry
= self
.check_fields()
487 response
= super(AddDB
,self
).run()
489 response
= gtk
.RESPONSE_REJECT
491 (status
, err_msg
, icon
, dbms
, dbms_num
492 ,name
, db_desc
, user
, passwd
, host
) = ("error", "error", None, None, None
493 ,None, None, None, None, None)
495 log
.debug(_("start creating new db"))
497 master_password
= None
498 dbms
= self
.dbnames
[ self
.cb_dbms
.get_active_text() ]
499 dbms_num
= self
.config
.get_backend(dbms
)
500 name
= self
.e_db_name
.get_text()
501 db_desc
= self
.e_db_desc
.get_text()
502 user
= self
.e_username
.get_text()
503 passwd
= self
.e_password
.get_text()
504 host
= self
.e_host
.get_text()
507 # if self.cb_dbms.get_active_text() == 'Postgres':
508 # <ask for postgres master password>
510 # create_db() in Database.py or here? ... TODO
512 # test db after creating?
513 status
, err_msg
, icon
= GuiDatabase
.testDB(self
.config
, dbms
, dbms_num
, name
, user
, passwd
, host
)
514 log
.debug(_('tested new db, result=%s') % str((status
,err_msg
)))
516 #dia = InfoBox( parent=self, str1=_('Database created') )
517 str1
= _('Database created')
519 #dia = InfoBox( parent=self, str1=_('Database creation failed') )
520 str1
= _('Database creation failed')
521 #dia.add_msg("", True, True)
522 btns
= (gtk
.BUTTONS_OK
)
523 dia
= gtk
.MessageDialog( parent
=self
, flags
=gtk
.DIALOG_DESTROY_WITH_PARENT
524 , type=gtk
.MESSAGE_INFO
, buttons
=(btns
), message_format
=str1
)
527 return( (status
, err_msg
, icon
, dbms
, dbms_num
, name
, db_desc
, user
, passwd
, host
) )
529 def check_fields(self
):
530 """check fields and return true/false according to whether user wants to try again
531 return False if fields are ok
533 log
.debug(_("check_fields: starting"))
537 # checks for all db's
538 if self
.e_db_name
.get_text() == "":
539 msg
= _("No Database Name given")
541 elif self
.e_db_desc
.get_text() is None or self
.e_db_desc
.get_text() == "":
542 msg
= _("No Database Description given")
544 elif self
.cb_dbms
.get_active_text() != 'Sqlite' and self
.e_username
.get_text() == "":
545 msg
= _("No Username given")
547 elif self
.cb_dbms
.get_active_text() != 'Sqlite' and self
.e_password
.get_text() == "":
548 msg
= _("No Password given")
550 elif self
.e_host
.get_text() == "":
551 msg
= _("No Host given")
555 if self
.cb_dbms
.get_active_text() == 'Sqlite':
558 elif self
.cb_dbms
.get_active_text() == 'MySQL':
561 elif self
.cb_dbms
.get_active_text() == 'Postgres':
562 # checks for postgres
565 msg
= _("Unknown Database Type selected")
569 log
.debug(_("check_fields: open dialog"))
570 dia
= gtk
.MessageDialog( parent
=self
571 , flags
=gtk
.DIALOG_DESTROY_WITH_PARENT
572 , type=gtk
.MESSAGE_ERROR
574 , buttons
= gtk
.BUTTONS_YES_NO
578 l
= gtk
.Label( _("Do you want to try again?") )
582 #log.debug(_("check_fields: ret is %s cancel is %s") % (str(ret), str(int(gtk.RESPONSE_CANCEL))))
583 if ret
== gtk
.RESPONSE_YES
:
585 #log.debug(_("check_fields: destroy dialog"))
589 #log.debug(_("check_fields: returning ok as %s, try_again as %s") % (str(ok), str(try_again)))
592 def db_type_changed(self
, widget
, data
):
593 if self
.cb_dbms
.get_active_text() == 'Sqlite':
594 self
.l_username
.hide()
595 self
.e_username
.hide()
596 self
.e_username
.set_text("")
597 self
.l_password
.hide()
598 self
.e_password
.hide()
599 self
.e_password
.set_text("")
601 self
.l_username
.show()
602 self
.e_username
.show()
603 self
.l_password
.show()
604 self
.e_password
.show()
607 def db_name_changed(self
, widget
, event
, data
):
608 log
.debug('db_name_changed: text='+widget
.get_text())
609 if not re
.match('\....$', widget
.get_text()):
610 widget
.set_text(widget
.get_text()+'.db3')
613 #def response_cb(self, dialog, data):
618 class InfoBox(gtk
.Dialog
):
620 def __init__(self
, parent
, str1
):
621 # create dialog and add icon and label
622 btns
= (gtk
.BUTTONS_OK
)
624 # messagedialog puts text in inverse colors if no buttons are displayed??
625 #dia = gtk.MessageDialog( parent=self.main_window, flags=gtk.DIALOG_DESTROY_WITH_PARENT
626 # , type=gtk.MESSAGE_INFO, buttons=(btns), message_format=str1 )
627 # so just use Dialog instead
628 super(InfoBox
,self
).__init
__( parent
=parent
629 , flags
=gtk
.DIALOG_DESTROY_WITH_PARENT
630 , title
="" ) # , buttons=btns
632 h
= gtk
.HBox(False, 2)
634 i
.set_from_stock(gtk
.STOCK_DIALOG_INFO
, gtk
.ICON_SIZE_DIALOG
)
636 h
.pack_start(i
, padding
=5)
637 h
.pack_start(l
, padding
=5)
638 self
.vbox
.pack_start(h
)
642 def add_msg(self
, str1
, run
, destroy
):
644 self
.vbox
.pack_start( gtk
.Label(str1
) )
647 if run
: response
= self
.run()
648 if destroy
: self
.destroy()
652 class SideButton(gtk
.Button
):
653 """Create a button with the label below the icon"""
655 # to change label on buttons:
656 # ( see http://faq.pygtk.org/index.py?req=show&file=faq09.005.htp )
657 # gtk.stock_add([(gtk.STOCK_ADD, _("Add"), 0, 0, "")])
660 # button = gtk.Button(stock=gtk.STOCK_CANCEL)
662 # alignment = button.get_children()[0]
663 # hbox = alignment.get_children()[0]
664 # image, label = hbox.get_children()
665 # label.set_text('Hide')
667 def __init__(self
, label
=None, stock
=None, use_underline
=True):
668 gtk
.stock_add([(stock
, label
, 0, 0, "")])
670 super(SideButton
, self
).__init
__(label
=label
, stock
=stock
, use_underline
=True)
671 alignment
= self
.get_children()[0]
672 hbox
= alignment
.get_children()[0]
673 image
, label
= hbox
.get_children()
674 #label.set_text('Hide')
677 v
= gtk
.VBox(False, spacing
=3)
678 v
.pack_start(image
, 3)
679 v
.pack_start(label
, 3)
680 alignment
.remove(hbox
)
686 if __name__
=="__main__":
688 config
= Configuration
.Config()
690 win
= gtk
.Window(gtk
.WINDOW_TOPLEVEL
)
691 win
.set_title(_("Log Viewer"))
692 win
.set_border_width(1)
693 win
.set_default_size(600, 500)
694 win
.set_resizable(True)
696 dia
= gtk
.Dialog(_("Log Viewer"),
698 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
699 (gtk
.STOCK_CLOSE
, gtk
.RESPONSE_OK
))
700 dia
.set_default_size(500, 500)
701 log
= GuiLogView(config
, win
, dia
.vbox
)
703 if response
== gtk
.RESPONSE_ACCEPT
: