I'm having trouble passing a variable defined in one class instance to another class instance. I am relatively new to using classes, but it is my understanding that variables can be passed from one instance to another by simply defining as part of the class instance (such as in the following example). While I've used this model in the past I've never attempted to do this using a GUI framework such as wxPython.
class Foo(object):
def __init__(self, var):
self.var = var
class Bar(object):
def do_something(self, var):
print var*3
if __name__ == '__main__':
f = Foo(3)
b = Bar()
b.do_something(f.var)
The problem I'm having is that the wxPython instance seems to be predefined and will not accept any additional parameters (allowing me to only pass things like title, size, etc.) to the class instance.
The other issue I'm facing is I'm attempting to pass the variable three classes deep by calling a dialog window, and from the dialog I call a separate class designed to start a worker thread.
So my questions are:
How can I pass the variable from the first class instance to the third class instance?
How can I override the wxPython instance to allow for the definition of additional variables?
OR, Could it be possible to create a custom event handler to pass the necessary data?
To clarify...
I'm using Python and would like to think that I understand the fundamentals of programming using Classes and a GUI with frameworks such as Tkinter and wxPython (used in this project). I've written a main class/instance that gets some data from the user and I would like to be able to pass the information stored in self.main_instance_var and pass it along to a second class/instance (in this case a Progress Dialog window called from the first class.
When I attempted to use the above model in my Progress Dialog I got a very uninformative syntax error ('non-keyword arg after keyword arg'). Preventing me from further passing the variable from the Progress Dialog window on to the worker thread. If I had gotten an exception that would have been one thing but the syntax error I don't understand. Look below for a short example:
class ProgressDialog(wx.Dialog):
def __init__(self, parent, title, myVar): # Generates syntax error on this line
super(ProgressDialog, self).__init__(parent=parent,
title=title, size=(500, 110))
self.var = myVar
Basic Source (by request, sorry its so dirty):
import time
import os, sys, wx
from ftplib import FTP_TLS
from threading import Thread
from wx.lib.pubsub import Publisher
########################################################################
class FtpValues(object):
""" Returns a property attribute - called by FtpFileTransfer
Used to set values/variables for Host, USERID, PASSWD, FILE """
#----------------------------------------------------------------------
def __init__(self):
self.varList = None
#----------------------------------------------------------------------
def GetValues(self):
return self.varList
#----------------------------------------------------------------------
def SetValues(self, HOST, USERID, PASSWD, FILE):
self.varList = [HOST, USERID, PASSWD, FILE]
#----------------------------------------------------------------------
def DelValues(self):
del self.valList
Values = property(GetValues, SetValues, DelValues, "Set/Get FtpValues")
# http://docs.python.org/library/functions.html#property
########################################################################
class FtpFileTransfer(Thread):
"""Test Worker Thread Class."""
#----------------------------------------------------------------------
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.StartTransfer() # start the thread
#----------------------------------------------------------------------
def StartTransfer(self): # was named run - started automatically
"""Run Worker Thread.""" # when called by the start method
# This is the code executing in the new thread.
HOST, USERID, PASSWD, FILE = FtpValues.Values
BLOCKSIZE = 57344
try:
ftp = FTP_TLS(HOST)
ftp.login(USERID, PASSWD)
ftp.prot_p()
ftp.voidcmd("TYPE I")
f = open(FILE, 'rb')
datasock, esize = ftp.ntransfercmd(
'STOR %s' % os.path.basename(FILE))
size = os.stat(FILE)[6]
bytes_so_far = 0
while 1:
buf = f.read(BLOCKSIZE)
if not buf:
break
datasock.sendall(buf)
bytes_so_far += len(buf)
msg = [bytes_so_far, size]
Publisher().sendMessage("update", msg)
except: raise
finally:
try:
datasock.close()
f.close()
ftp.voidresp()
ftp.quit()
print 'Complete...'
except: pass
wx.CallAfter(Publisher().sendMessage, "update", "Database Transfer Complete!")
########################################################################
class ProgressDialog(wx.Dialog):
def __init__(self, parent, title):
super(ProgressDialog, self).__init__(parent=parent,
title=title, size=(500, 110))
self.displayLbl = wx.StaticText(self, -1, 'Verifying Database Files... ', (20, 20)) #Preparing for Transfer...
self.gauge = wx.Gauge(self, -1, 100, (20, 45), (370, 24))
self.btn = btn = wx.Button(self, -1, 'Cancel', (400, 45), (-1, 25))
btn.Bind(wx.EVT_BUTTON, self.OnClose)
# listens for response from worker thread
Publisher().subscribe(self.updateDisplay, "update")
FtpFileTransfer()#.StartTransfer(HOST, USERID, PASSWD, FILE) #Start the FTP Worker Thread
#self.OnStart()
#----------------------------------------------------------------------
def run(self):
FtpFileTransfer(HOST, USERID, PASSWD, FILE)
#----------------------------------------------------------------------
def OnClose(self, event):
""" Place Holder """
if self.btn.GetLabel() == 'Finish':
# Do Something!
pass
return None
#----------------------------------------------------------------------
def updateDisplay(self, msg):
""" Receives data from thread and updates the display """
if isinstance(msg.data, list):
bytes_so_far, size = msg.data
k = 100 * bytes_so_far / size
self.displayLbl.SetLabel("Sent %d of %d bytes %.1f%%" % (bytes_so_far, size, 100 * bytes_so_far / size))
self.gauge.SetValue(k)
else:
self.displayLbl.SetLabel("%s" % msg.data)
#self.btn.Enable()
self.btn.SetLabel('Finish')
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here")
self.btn = btn = wx.Button(panel, label="Start Thread")
self.gauge = wx.Gauge(panel, -1, 100, size=(370, 24))
btn.Bind(wx.EVT_BUTTON, self.onButton)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.displayLbl, 0, wx.ALL|wx.CENTER, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
sizer.Add(self.gauge, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
self.VarData()
# create a pubsub receiver
Publisher().subscribe(self.updateDisplay, "update")
#----------------------------------------------------------------------
def onButton(self, event):
"""
Runs the thread
"""
chgdep = ProgressDialog(None, title='File Transfer. . .')
chgdep.ShowModal()
#chgdep.Destroy()
#----------------------------------------------------------------------
def updateDisplay(self, msg):
"""
Receives data from thread and updates the display
"""
if isinstance(msg.data, list):
bytes_so_far, size = msg.data
k = 100 * bytes_so_far / size
self.displayLbl.SetLabel("Sent %d of %d bytes %.1f%%" % (bytes_so_far, size, 100 * bytes_so_far / size))
self.gauge.SetValue(k)
else:
self.displayLbl.SetLabel("%s" % msg.data)
self.btn.Enable()
#----------------------------------------------------------------------
def VarData(self):
HOST = '127.0.0.1'
USERID = 'test'
PASSWD = 'P#ssw0rd'
FILE = r'F:\Programming\temp\Test.zip'
varList = [HOST, USERID, PASSWD, FILE]
FtpValues.Values = HOST, USERID, PASSWD, FILE
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
Personally, I like using wx.lib.pubsub to pass information between classes. I do it all the time in my applications. You can read about it here: http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
If you need to post data from a thread, you will need to use a thread-safe method like wx.CallAfter, wx.CallLater or wx.PostEvent. You can combine these with pubsub by calling your pubsub publisher inside one of the thread-safe methods. I show how to do just that here: http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
There's also a good article on threads and wxPython on their wiki: http://wiki.wxpython.org/LongRunningTasks
The approaches suggested in the question are the preferred way of doing things in Python. Statements like b.do_something(f.var) and __init__(self, parent, title, myVar) are perfectly good ways to pass information between classes.
It's common when starting out, but my guess here is that you've taken a small syntax error somewhere and are thinking it's implying that you're taking the wrong general approach. Instead, your general approach looks fine, but you just need to figure out what's causing the specific error.
Comments on other answers:
1) Set/get functions work well too, but Python prefers the properties approach. (Personally, I still use set/get methods out of habit, but it's not as Pythonic.)
2) pubsub is great, but it's not a general tool for "passing information between classes", e.g., one wouldn't want to use pubsub for i = int("2"). Pubsub is more for cases where one has, for example, two wxFrames that need to communicate a bit of information. There's a reason it's a part of wxPython and not Python.
In OOP if you want to pass data into and out of an object you should define a set/get function in that class so that you can get data from that object as well as set data in that object. So in your case each instance of your object would call the respective get/set function to pass the data back and forth between your objects.
You may have worked through this months ago, but I just ran into the very same issue with a wxPython dialog, and got it to work by using informing a global within the function:
elusive_variable="" # declare outside class & function
class MyForm(wx.Frame):
def onOpenFile(self, event):
global elusive_variable
# ...other stuff here
elusive_variable="Oh hi Mark"
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
print elusive_variable # variable is liberated!!!
There might be some more enlightned way but this works...
Heres an example:
class One:
param1 = ['a', 'b']
class Two:
i = One()
print i.param1
Save the above code in .py file and run it . You should see the output
I guess this can be a simple way to exchange variables from one class to another
Related
One post right before mine, I found some Code I would like to use. There is an ComboPopup which has checkboxes in it. If one of These checkboxes is activated, I want to pass the selected text back to my class (i.e. MyForm). There is an StaticText called self.text. I want to Change the Label with the choosen Text of the ComboPopup.
I tried it with:
test = MyForm()
MyForm.OnUpdate(test,item.GetText())
as I thought that self.text is parent from MyForm(). But that doesn't work. No errors, but also no changes of the text.
What is self in this case? Is there a good way to find out what self is ? Like print the Name or anything :-)
My Code:
import wx
import wx.stc
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
def __init__(self, parent):
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |
wx.SUNKEN_BORDER)
CheckListCtrlMixin.__init__(self)
ListCtrlAutoWidthMixin.__init__(self)
self.SetSize(-1, -1, -1, 50)
def OnCheckItem(self, index, flag):
item = self.GetItem(index)
if flag:
what = "checked"
else:
what = "unchecked"
print(f'{item.GetText()} - {what}')
test = MyForm()
MyForm.OnUpdate(test,item.GetText())
class ListViewComboPopup(wx.ComboPopup):
def __init__(self):
wx.ComboPopup.__init__(self)
self.lc = None
def AddItem(self, txt):
self.lc.InsertItem(0, txt)
def Init(self):
self.value = -1
self.curitem = -1
def Create(self, parent):
self.lc = CheckListCtrl(parent)
self.lc.InsertColumn(0, '', width=90)
return True
def GetControl(self):
return self.lc
def OnPopup(self):
wx.ComboPopup.OnPopup(self)
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.ComboPopup.GetAdjustedSize(
self, minWidth, 110, maxHeight)
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Popup Menu")
self.panel = wx.Panel(self)
vsizer = wx.BoxSizer(wx.VERTICAL)
comboCtrl = wx.ComboCtrl(self.panel, wx.ID_ANY, "Select Text")
popupCtrl = ListViewComboPopup()
comboCtrl.SetPopupControl(popupCtrl)
popupCtrl.AddItem("Text One")
self.txt = wx.StaticText(self.panel,-1,style = wx.ALIGN_LEFT)
self.txt.SetLabel("Startup Text")
vsizer.Add(comboCtrl,1,wx.EXPAND)
vsizer.Add(self.txt,1,wx.EXPAND)
self.panel.SetSizer(vsizer)
def OnUpdate(self, txt):
self.txt.SetLabel(txt)
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()
Your wx.Frame subclass instance does not have a parent. You explicitly create it without one:
wx.Frame.__init__(self, None, title="Popup Menu")
You create an instance of MyForm in your __name__ == '__main__' block:
frame = MyForm().Show()
# Note: your name 'frame' holds the return value of the method Show(), i.e. a boolean
# This probably should rather read:
# frame = MyForm()
# frame.Show()
This is the MyForm instance you show in your app.
What you do here:
test = MyForm()
is creating a new instance of MyFrame (that has nothing to do with the one your app shows). You then call onUpdate on that new instance of your MyForm class
MyForm.OnUpdate(test,item.GetText())
Since you never Show() that new instance, you can't see the effect of your operation. However, you probably don't want/need that new instance anyway.
You need your instance from the main block.
There is a parent argument on the CheckListCtrl initializer. This might contain a chain of objects which you probably can ascend until you reach your MyForm instance.
I can't tell for sure, since it is not visible where and how this is called in the ListViewComboPopup:
def Create(self, parent):
self.lc = CheckListCtrl(parent)
Do a print(self.Parent) in OnCheckItem to see what it contains and then add another .Parent to self.Parent until you hopefully end up on a <__main__.MyForm instance [...]>. This is where you want to call the onUpdate Method. That should look similar to this:
self.Parent.Parent.Parent.OnUpdate(item.GetText())
# the number of '.Parent' my vary, based on where in the chain you find your MyForm instance
Edit
As per the OP's comment, the parent attribute on wx objects is spelled with a capital P. The respective code snippets have been updated accordingly.
I don't know what wx library does but there is a way to check where .text is.
You want vars() mixed with pprint():
from pprint import pprint
pprint(vars(your_object))
pprint(your_object) # this is OK too
Suggestion 2
type(x).__name__
This gets you the class name of an instance. You could insert this line before self.text. And give self as argument instead of x.
Original: Link
I have issue with GTK's TreeView with ListStore. Records are updated, but sometime only when I hover it.
Bigger problem are new records - It's like it stops to displaying new ones unless I scroll to bottom all the time - which is weird.
I use Glade.
My code (slightly simplified)
class UI(SampleUI):
gladefile = 'ui.glade'
iterdict = {}
def __init__(self, module):
super().__init__(module)
def start(self):
self.fetch_widgets()
self.connect_events()
self.window.show()
def fetch_widgets(self):
self.window = self.builder.get_object('window')
self.liststore = self.builder.get_object('store')
def connect_events(self):
handlers = {
"on_window_close" : Gtk.main_quit,
"on_start_working": self.start_working,
"on_stop_working": self.stop_working
}
self.builder.connect_signals(handlers)
self.module.url_pending_action = self.url_pending
self.module.url_succeed_action = self.url_update
def start_working(self, button):
self.module.start()
def stop_stop(self, button):
self.module.stop()
def url_pending(self, data):
self.iterdict[data['url']] = self.liststore.append([0, data['url'], 0, '', data['text']])
def url_update(self, data):
_iter = self.iterdict[data['url']]
self.liststore[_iter][1] = data['some_field1']
self.liststore[_iter][2] = data['some_field2']
Methods self.url_pending and self.url_update are called by threads (at most 30 running at the same time) created in self.module
I checked and new records are correctly appended into ListStore - I can read data from it. Window is working fine, but there are no new items at the bottom.
Ideas?
Ok, I made research and I figured out that GTK don't like being called from outside of main thread.
There was methods in Gdk to lock GTK calls
Gtk.threads_enter()
...
Gtk.threads_leave()
But now it's deprecated and GTK documentation says that every GTK call should be in main thread.
My workaround:
# [...]
def start(self):
# [...]
GObject.timeout_add(100, self.query_do)
# [...]
def query_do(self):
while len(self.gtk_calls):
self.gtk_calls.pop()()
GObject.timeout_add(100, self.query_do)
And I'm just adding into query
self.gtk_calls.insert(0, lambda: anything())
The code isn't as clear as it was before, but works perfect.
I'm coding a python script using several commanline tools like top, so i need a proper visual feedback. Now it is time to give it a menu, so here comes the problem.
I found here a great approach of what i need, but every try to display a feedback before come back to previous menu is futile.
I just need menus, submenus, launch commands, terminate it, and back to previous menu. a GREAT bonus would be to run them in a split of the term.
Is there any pattern/skeleton/stuff/whatever to use as template in order to display several kind of widget with a predictable output?
here is a example of code,which two examples of functions to run:
#!/usr/bin/env python2
import curses
from curses import panel
class Menu(object):
def __init__(self, items, stdscreen):
self.window = stdscreen.subwin(0,0)
self.window.keypad(1)
self.panel = panel.new_panel(self.window)
self.panel.hide()
panel.update_panels()
self.position = 0
self.items = items
self.items.append(('exit','exit'))
def navigate(self, n):
self.position += n
if self.position < 0:
self.position = 0
elif self.position >= len(self.items):
self.position = len(self.items)-1
def display(self):
self.panel.top()
self.panel.show()
self.window.clear()
while True:
self.window.refresh()
curses.doupdate()
for index, item in enumerate(self.items):
if index == self.position:
mode = curses.A_REVERSE
else:
mode = curses.A_NORMAL
msg = '%d. %s' % (index, item[0])
self.window.addstr(1+index, 1, msg, mode)
key = self.window.getch()
if key in [curses.KEY_ENTER, ord('\n')]:
if self.position == len(self.items)-1:
break
else:
self.items[self.position][1]()
elif key == curses.KEY_UP:
self.navigate(-1)
elif key == curses.KEY_DOWN:
self.navigate(1)
self.window.clear()
self.panel.hide()
panel.update_panels()
curses.doupdate()
######################################################### !#
######################################################### !#
############# HERE MY FUNCTIONS examples
############ Everithing works OK, but displays it awfully
def GetPid(name):
import subprocess
command= str(("""pgrep %s""") % name )
p = subprocess.Popen(command, shell = True, stdout = subprocess.PIPE)
procs = []
salida = p.stdout
for line in salida:
procs.append(str.strip(line))
return procs
def top():
os.system("top")
def menuGP():
print GetPid("top")
######################################################### !#
class MyApp(object):
def __init__(self, stdscreen):
self.screen = stdscreen
curses.curs_set(0)
submenu_items = [
('beep', curses.beep),
('top', top)
]
submenu = Menu(submenu_items, self.screen)
main_menu_items = [
('get PID', GetPid),
('submenu', submenu.display)
]
main_menu = Menu(main_menu_items, self.screen)
main_menu.display()
if __name__ == '__main__':
curses.wrapper(MyApp)
Thanks in advise (and sorry for my rough english)
You really have two choices. One you can leave curses mode, execute your program, then resume curses. Two, you can execute your program asynchronously, parse its output and write it to the screen.
The good news on the first option is that you don't actually need to write any fancy save_state / load_state methods for the ui. Curses does this for you. Here's a simple example to show my point
import curses, time, subprocess
class suspend_curses():
"""Context Manager to temporarily leave curses mode"""
def __enter__(self):
curses.endwin()
def __exit__(self, exc_type, exc_val, tb):
newscr = curses.initscr()
newscr.addstr('Newscreen is %s\n' % newscr)
newscr.refresh()
curses.doupdate()
def main(stdscr):
stdscr.addstr('Stdscreen is %s\n' % stdscr)
stdscr.refresh()
time.sleep(1)
with suspend_curses():
subprocess.call(['ls'])
time.sleep(1)
stdscr.refresh()
time.sleep(5)
curses.wrapper(main)
If you run the example, you will notice that the screen created by curses.wrapper and the one created in curses.initscr when resuming are the same object. That is, the window returned by curses.initscr is a singleton. This lets us exit curses and resume like above without having to update each widget's self.screen references each time.
The second option is much more involved but also much more flexible. The following is just to represent the basic idea.
class procWidget():
def __init__(self, stdscr):
# make subwindow / panel
self.proc = subprocess.Popen(my_args, stdout=subprocess.PIPE)
def update(self):
data = self.proc.stdout.readline()
# parse data as necessary
# call addstr() and refresh()
Then somewhere in your program you will want to call update on all your procWidgets on a timer. This gives you the option of making your subwindow any size/place so you can have as many procWidgets as will fit. You will have to add some handling for when the process terminates and other similar events of course.
I have an array of data called msg.data[2] where i have used pubsub i now want to pass this data into the PlotCanvasExample Class
Here is where i call the class
self.pubsubText.SetLabel("This is the Contact Map for the chain "+msg.data[0]+" in the PDB file "+msg.data[1])
frame = self.GetParent()
sizer = wx.BoxSizer(wx.VERTICAL)
self.canvas = PlotCanvasExample(self,0, size=(100,100))
sizer.Add(self.canvas,1,wx.EXPAND,0)
Here is the class itself
class PlotCanvasExample(plot.PlotCanvas):
def __init__(self,parent,id,size):
plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_NONE, size=(300,200))
self.data = [(1,2),(23,2)]
line = plot.PolyMarker(self.data)
gc = plot.PlotGraphics([line],"CM view", "x-axis","y axis")
self.Draw(gc, xAxis=(0,50), yAxis=(0,50))
How can i pass in the variable msg.data into this class
I am new to python so an explanation of how to do it would be nice so i can understand how to do it next time
you may add setData method to PlotCanvasExample class and use this method maybe?
def setData(data):
self.data = data
or if you need this parameter on object creation, you may add this parameter to init method of PlotCanvasExample.
class PlotCanvasExample(plot.PlotCanvas):
def __init__(self,parent,id,size, data):
class P...:
def __init__(self, parent, id, size, data):
self.data = data
...
Arguments which need to be passed to a class should be placed in the __init__ function.
Here is the Python documentation on Classes
the line of code def __init__(self,parent,id,size): is what you need to change to add the new parameter in.
When you create this class next time you can assign it in the self.canvas - PlotCanvasExample()
Example:
Class:
class PlotCanvasExample(plot.PlotCanvas):
def __init__(self,parent,id,size,data):
Code:
self.canvas = PlotCanvasExample(self,0, size=(100,100), msg.data[2])
If your new to Python I would suggest taking a look through the docs. Also take a look at PEP8 it will help when posting code on here.
:)
Try this code...
self.pubsubText.SetLabel("This is the Contact Map for the chain "+msg.data[0]+" in the PDB file "+msg.data[1])
frame = self.GetParent()
sizer = wx.BoxSizer(wx.VERTICAL)
self.canvas = PlotCanvasExample(self,0, size=(100,100), msg.data)
sizer.Add(self.canvas,1,wx.EXPAND,0)
class PlotCanvasExample(plot.PlotCanvas):
def __init__(self,parent,id,size, msgdata):
self.msgdata = msgdata
plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_NONE, size=(300,200))
self.data = [(1,2),(23,2)]
line = plot.PolyMarker(self.data)
gc = plot.PlotGraphics([line],"CM view", "x-axis","y axis")
self.Draw(gc, xAxis=(0,50), yAxis=(0,50))
Hope this helps.
Andrew
I am working on an application using PyQt4 and the designer it provides. I have a main window application that works fine, but I wanted to create custom message dialogs. I designed a dialog and set up some custom signal/slot connections in the __init__ method and wrote an if __name__=='__main__': and had a test. The custom slots work fine. However, when I create an instance of my dialog from my main window application, none of the buttons work. Here is my dialog:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
import encode_dialog_ui
# Ui_EncodeDialog is the python class generated by pyuic4 from the Designer
class EncodeDialog(encode_dialog_ui.Ui_EncodeDialog):
def __init__(self, parent, in_org_im, txt_file, in_enc_im):
self.qd = QDialog(parent)
self.setupUi(self.qd)
self.qd.show()
self.message = (txt_file.split("/")[-1] + " encoded into " +
in_org_im.split("/")[-1] + " and written to " +
in_enc_im.split("/")[-1] + ".")
QObject.connect(self.view_image_button, SIGNAL("clicked()"),
self.on_view_image_button_press)
self.org_im = in_org_im
self.enc_im = in_enc_im
self.encoded_label.setText(self.message)
def on_view_image_button_press(self):
print "hello world"
if __name__ == '__main__':
app = QApplication(sys.argv)
tmp = QMainWindow()
myg = EncodeDialog(tmp,'flower2.png','b','flower.png')
app.exec_()
If I run this class it works fine, and pressing the view_image_button prints hello world to the console. However when I use the call
#self.mw is a QMainWindow, the rest are strings
EncodeDialog(self.mw, self.encode_image_filename,
self.encode_txt_filename,
self.encode_new_image_filename)
in my main window class, the dialog displays correctly but the view_image_button does nothing when clicked. I have googled for a solution, but couldn't find anything useful. Let me know if you need any more information. Any help on this would be appreciated!
As requested below is some more code from my main window class for brevity's sake I have added ellipses to remove code that seemed irrelevant. If no one can think of anything still, I will add more. (If indenting is a little off, it happened in copy-pasting. The orignal code is correct)
class MyGUI(MainWindow.Ui_MainWindow):
def __init__(self):
self.mw = QMainWindow()
self.setupUi(self.mw)
self.mw.show()
self.encode_red_bits = 1
self.encode_blue_bits = 1
self.encode_green_bits = 1
self.decode_red_bits = 1
self.decode_blue_bits = 1
self.decode_green_bits = 1
self.encode_image_filename = ""
self.encode_new_image_filename = ""
self.encode_txt_filename = ""
self.decode_image_filename = ""
self.decode_txt_filename = ""
# Encode events
...
QObject.connect(self.encode_button, SIGNAL("clicked()"),
self.on_encode_button_press)
# Decode events
...
# Encode event handlers
...
def on_encode_button_press(self):
tmp = QErrorMessage(self.mw)
if (self.encode_image_filename != "" and
self.encode_new_image_filename != "" and
self.encode_txt_filename != ""):
try:
im = Steganography.encode(self.encode_image_filename, self.encode_txt_filename,
self.encode_red_bits, self.encode_green_bits,
self.encode_blue_bits)
im.save(self.encode_new_image_filename)
encode_dialog.EncodeDialog(self.mw, self.encode_image_filename,
self.encode_txt_filename,
self.encode_new_image_filename)
except Steganography.FileTooLargeException:
tmp.showMessage(self.encode_txt_filename.split("/")[-1] +
" is to large to be encoded into " +
self.encode_image_filename.split("/")[-1])
else:
tmp.showMessage("Please specify all filenames.")
# Decode event handlers
...
if __name__ == '__main__':
app = QApplication(sys.argv)
myg = MyGUI()
app.exec_()
It feels like the signal is just not getting passed from the parent down to your child QDIalog.
Try these suggestions:
Use the new method for connecting signals
Instead of extending the classes pyuic created, extend the actual QT classes and call the ones generated by pyuic
Your new code will look something like this:
class MyGUI(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.mw = MainWindow.Ui_MainWindow()
self.mw.setupUi(self)
self.mw.show()
...
self.encode_button.clicked.connect(self.on_encode_button_press)
...
class EncodeDialog(QDialog):
def __init__(self, parent, in_org_im, txt_file, in_enc_im):
QDialog.__init__(self, parent)
self.qd = encode_dialog_ui.Ui_EncodeDialog()
self.qd.setupUi(self)
self.qd.show()
...
self.view_image_button.clicked.connect(self.on_view_image_button_press)
...