This is my code:
def configure_event(self, widget):
if self.is_hiding:
self.window.present()
else:
self.window.iconify()
def delete_event(self, widget, data=None):
gtk.main_quit()
return True
def popup_menu(self):
self.menu = gtk.Menu()
self.about = gtk.MenuItem("about")
if self.is_hiding:
self.expand = gtk.MenuItem("show")
else:
self.expand = gtk.MenuItem("hide")
self.quit = gtk.MenuItem("quit")
self.about.connect("activate", self.about_monitor)
self.expand.connect("activate", self.configure_event)
self.quit.connect("activate", self.delete_event)
self.menu.popup(None, None, gtk.status_icon_position_menu, event_button, event_time, self.tray_icon)
self.menu.append(self.about)
self.menu.append(self.expand)
self.menu.append(self.monitor)
self.menu.append(self.quit)
self.menu.show_all()
delete_event works, but configure_event does not. Why?
look at the signatures of both of them:
def configure_event(self, widget):
def delete_event(self, widget, data=None):
delete_event has a third argument data (that has a default of None) but configure_event only has two.
although i don't know what the exception was i bet that the exception was:
TypeError: configure_event() takes exactly 2 arguments (3 given)
if so changing configure_event's signature to:
def configure_event(self, widget, data):
would fix it. note that i think the default value of None is unneeded as gtk will pass something in always.
Related
I want to test a module in my project, but I have a some problem with it. I don't know how I can check the correct opening of the window after the button click.
I have the windows SelectAddingBookTypeWindow and AddSchoolBookWindow.
I want to check that AddSchoolBookWindow will open after I have clicked on the button called selectBtn.
My current test (I have a problem with the last string because I don't know which method I must use):
#pytest.fixture
def open_window(qtbot):
def _press_button(window):
widget = window()
qtbot.addWidget(widget)
widget.show()
qtbot.wait_for_window_shown(widget)
sleep(3)
return widget
return _press_button
class TestSelectAddingBookTypeWindow:
def test_select_schoolbook(self, open_window, qtbot):
widget = open_window(SelectAddingBookTypeWindow)
qtbot.mouseClick(widget.schoolbookSelector, QtCore.Qt.LeftButton)
qtbot.mouseClick(widget.selectBtn, QtCore.Qt.LeftButton)
assert widget.close()
assert AddSchoolBookWindow.isActiveWindow()
My classes:
A. SelectAddingBookTypeWindow
class SelectBookTypeWindow(QDialog, Ui_selectBookTypeWindow):
#logger.catch
def __init__(self, widget1, widget2):
super().__init__()
self.setupUi(self)
self.selectBtn.clicked.connect(lambda x: self.open_new_window(widget1, widget2))
#logger.catch
def open_new_window(self, schoolbook_widget, fictionbook_widget):
if self.schoolbookSelector.isChecked():
widget = schoolbook_widget()
elif self.fictionbookSelector.isChecked():
widget = fictionbook_widget()
widget.show()
self.close()
class SelectAddingBookTypeWindow(SelectBookTypeWindow):
#logger.catch
def __init__(self):
super().__init__(AddSchoolBookWindow, AddFictionBookWindow)
B. AddSchoolBookWindow
class AddSchoolBookWindow(QDialog, Ui_AddSchoolbookWindow):
#logger.catch
def __init__(self):
super().__init__()
self.setupUi(self)
self.widgets = [
self.schoolbookAuthor, self.schoolbookTitle, self.schoolbookPublishHouse,
self.schoolbookPublishYear, self.schoolbookInvoiceNum, self.schoolbookInvoiceDate, self.schoolbookClass,
self.schoolbookCount, self.schoolbookPrice
]
self.schoolbookSaveBtn.clicked.connect(lambda _: self.save_book())
self.schoolbookClearBtn.clicked.connect(lambda _: Utils.clear_fields(self.widgets, self.schoolbookInvoiceDate))
self.schoolbookCancelBtn.clicked.connect(lambda _: self.close())
#logger.catch
def save_book(self):
field_names = Config.COLUMNS['SchoolBook']
user_data = list(map(lambda x: x.text(), self.widgets))
book_sum = Utils.calculate_book_sum(user_data)
user_data.append(str(book_sum))
book = dict(zip(field_names[1:], user_data))
db = DatabaseManager(Config.PATHES['TO_DATABASE']['PROD'])
model = Schoolbook('SchoolBook', db.connection)
model.insert_book(book)
logger.success(f'The {book} success add in the database')
model.model.select()
This is a little part of full code but full code doesn't solve with this problem.
The problem in your case is that the widget is not accessible since it is a local variable, I also doubt that the window will be shown as it should be removed a moment later, so to access it I will create a property:
class SelectBookTypeWindow(QtWidgets.QDialog, Ui_selectBookTypeWindow):
def __init__(self, widget1, widget2):
super().__init__()
self.setupUi(self)
self.selectBtn.clicked.connect(lambda x: self.open_new_window(widget1, widget2))
self._selected_window = None
#property
def selected_window(self):
return self._selected_window
def open_new_window(self, schoolbook_widget, fictionbook_widget):
if self.schoolbookSelector.isChecked():
self._selected_window = schoolbook_widget()
elif self.fictionbookSelector.isChecked():
self._selected_window = fictionbook_widget()
if self.selected_window is not None:
self.selected_window.show()
self.close()
And then in the test you should verify that selected_window is not None and its base class is AddSchoolBookWindow, also avoid using time.sleep(), in the case of the tests use QTest.qWait()
import pytest
from PyQt5 import QtCore, QtTest
from your_package import AddSchoolBookWindow, SelectAddingBookTypeWindow
#pytest.fixture
def open_window(qtbot):
def callback(window):
widget = window()
qtbot.addWidget(widget)
widget.show()
qtbot.wait_for_window_shown(widget)
QtTest.QTest.qWait(3 * 1000)
return widget
return callback
class TestSelectAddingBookTypeWindow:
def test_select_schoolbook(self, open_window, qtbot):
widget = open_window(SelectAddingBookTypeWindow)
assert widget.isVisible()
qtbot.mouseClick(widget.schoolbookSelector, QtCore.Qt.LeftButton)
qtbot.mouseClick(widget.selectBtn, QtCore.Qt.LeftButton)
assert widget.isHidden()
assert widget.selected_window is not None
assert isinstance(widget.selected_window, AddSchoolBookWindow)
assert widget.selected_window.isVisible()
I have a Toplevel widget which asks the User what widget he want to spawn and then asks for cnf. Whatfor and stuff is not important.
The cnf will be asked in an Scrollframe-Table-something (I don't really know how to describe it ^^'). Extra for that Scrollframe and its Scrollbar I made a Frame, so I can easily pack it left and right. But somehow the Scrollframe is taking the Tk window (root of my toplevel) as master.
Here is the code - I can't find my mistake:
from tkinter import _cnfmerge as cnfmerge
from tkinter import *
class Scrollframe(Frame):
def __init__(self,master=None,height=200,width=200,**kw):
if 'yscrollcommand' in kw:
self.ysc=kw['yscrollcommand']
del kw['yscrollcommand']
else: ysc=None
if 'pad' in kw:
self.pad=kw['pad']
del kw['pad']
else: self.pad=0
Frame.__init__(self,height=height,width=width)
self.scrollframe=Frame(self,**kw)
self.scrollframe.place(x=0,y=0,relwidth=1)
self.config(bg=self.scrollframe['bg'])
self.bind('<Configure>',self.adopt)
self.widgets,self.scrollable={},False
def adopt(self,event=None):
if self.scrollframe.winfo_height()>self.winfo_height():
self.scrollable=True
self.scrollframe.place(y=0)
self.ysc(0,0)
else:
self.scrollable=False
self.ysc(0,1)
def addItem(self,widget=None,cnf={},**kw):
if widget:
cnf=cnfmerge((cnf,kw))
if 'width' in cnf: del cnf['width']
obj=widget(self.scrollframe,cnf)
if len(self.widgets)==0 and self.pad!=0: obj.pack(fill=X)
else: obj.pack(fill=X,pady=(self.pad,0))
id_=str(id(obj))+widget.__name__
obj.bind('<Destroy>',lambda event: self.delItem(id_),'+')
self.widgets[id_]=obj
return id_
def getItem(self,id):
return self.widgets[id]
def delItem(self,id):
try: self.widgets[id].destroy()
except TclError: del self.widgets[id]
except KeyError: pass
def yview(self,*args):
try: delta=int(args[1])
except ValueError: delta=float(args[1])
maxnegscroll=self.winfo_height()-self.scrollframe.winfo_height()
if isinstance(delta,float):
if maxnegscroll<0: self.scrollframe.place(y=int(maxnegscroll*delta))
delta=abs(int(self.scrollframe.place_info()['y'])/maxnegscroll)
self.ysc(delta,delta)
else:
delta=-delta*3
if int(self.scrollframe.place_info()['y'])+delta<maxnegscroll: self.scrollframe.place(y=maxnegscroll)
elif int(self.scrollframe.place_info()['y'])+delta>0: self.scrollframe.place(y=0)
else: self.scrollframe.place(y=int(self.scrollframe.place_info()['y'])+delta)
delta=abs(int(self.scrollframe.place_info()['y'])/maxnegscroll)
self.ysc(delta,delta)
class CreateWindow(Toplevel):
def __init__(self,master=None):
Toplevel.__init__(self,master,height=458,width=400)
self.grab_set()
self.resizable(False,False)
self.title('Neues Item')
self.vars,self.cnf,self.cnfids={},{},{}
cnf=create_dict(bg='gainsboro',width=380)
Frame(self,cnf=cnf,height=39).place(x=10,y=10)
Frame(self,cnf=cnf,height=103).place(x=10,y=59)
Frame(self,cnf=cnf,height=220).place(x=10,y=172)
bottom=Frame(self,cnf=cnf,height=46)
bottom.pack_propagate(False)
bottom.place(x=10,y=402)
var,values,self.oldwidget=StringVar(value='Frame'),list(_tkinter_widgets.keys())[2:],'Frame'
for i in range(len(values)): values[i]=values[i].__name__
Spinbox(self,values=values,textvar=var,state=READONLY,cursor='arrow',command=self.refresh,buttonuprelief=FLAT,buttondownrelief=FLAT,wrap=True).place(x=20,y=20)
self.vars['widget']=var
Label(self,text='Höhe:',bg='gainsboro',anchor=W,bd=1).place(x=20,y=69)
var=StringVar()
Entry(self,textvar=var,justify=CENTER,width=40).place(x=136,y=69)
self.vars['height']=var
Label(self,text='Breite:',bg='gainsboro',anchor=W,bd=1).place(x=20,y=98)
var=StringVar()
Entry(self,textvar=var,justify=CENTER,width=40).place(x=136,y=98)
self.vars['width']=var
var=BooleanVar(value=True)
Checkbutton(self,onvalue=True,offvalue=False,text='Farbe übernehmen (falls vorhanden)',variable=var,cursor='hand2',bg='gainsboro',activebackground='gainsboro').place(x=20,y=127)
self.vars['takecolor']=var
cnfsframe=Frame(self,height=200,width=360)
cnfsframe.pack_propagate(0)
cnfsframe.place(x=20,y=182)
sb=Scrollbar(cnfsframe)
sb.pack(fill=Y,side=RIGHT)
self.cnfs=Scrollframe(master=cnfsframe,width=360-17,height=200,yscrollcommand=sb.set)
self.cnfs.pack(fill=Y,side=LEFT)
sb.config(command=self.cnfs.yview)
for arg in _tkinter_widgets[Frame]:
id=self.cnfs.addItem(Frame,height=19,width=360)
obj=self.cnfs.getItem(id)
var=StringVar()
Entry(obj,width=35,justify=CENTER,textvar=var).place(x=146,y=0)
Label(obj,text=arg,bd=1).place(x=0,y=0)
self.cnf[arg],self.cnfids[arg]=var,id
Button(bottom,text='Bestätigen',command=self.confirm,width=12,height=1).pack(side=LEFT,padx=10,pady=10)
Button(bottom,text='Abbrechen',command=self.destroy,width=12,height=1).pack(side=RIGHT,padx=(0,10),pady=10)
def refresh(self):
self.vars['height'].set(''),self.vars['width'].set(''),self.vars['takecolor'].set(True)
for arg in _tkinter_widgets[eval(self.oldwidget)]:
self.cnfs.delItem(self.cnfids[arg])
del self.cnfids[arg],self.cnf[arg]
for arg in _tkinter_widgets[eval(self.vars['widget'].get())]:
id=self.cnfs.addItem(Frame,height=19,width=360)
obj=self.cnfs.getItem(id)
obj.pack_propagate(False)
var=StringVar()
Entry(obj,width=35,justify=CENTER,textvar=var).pack(side=RIGHT)
Label(obj,text=arg,bd=1).pack(fill=X,side=LEFT)
self.cnf[arg],self.cnfids[arg]=var,id
self.oldwidget=self.vars['widget'].get()
self.focus()
def confirm(self):
raise NotImplementedError #first I'll have to fix that scrollframe issue xD
if __name__=='__main__':
t=Tk()
cw=CreateWindow(t)
Before someone asks what self.scrollable in Scrollframe is for: Its for the MouseWheel binding I'll implement later.
In this line, you are not passing the master to the super-class' __init__:
Frame.__init__(self,height=height,width=width)
Just change it to:
Frame.__init__(self,master=master, height=height,width=width)
That said, it is a general Python recommendation to use super() instead of hardcoding the superclass name:
super().__init__(master=master, height=height, width=width)
A QPushButton is set 'asCheckable'. Whence toggled, a class bool is changed.
This altered bool allows a method in a different class to proceed, and upon completion of this outside method I need to return the button to its initial state, 'setChecked(False)'.
While I am able to return the class housed bool to its default state at the end of this external method, I am unable to externally access a method which un-clicks the button.
I assume its due to the arguments in the classes init, but these are necessary - and I'm wondering if there is another means to achieve the described workflow.
Related code snips below:
(command in question is distinguished at bottom of 'Class 2')
Class 1:
class shapeCSVeditor(QtGui.QDialog, QtGui.QWidget):
valueShare = []
rowOverride = False# <<=== equivalent to 'override' in 'Class 2'
def __init__(self, iface, fileName, editorType, parent=None):
super(shapeCSVeditor, self).__init__(parent)
self.iface = iface
self.editorType = editorType
self.fileName = filename
self.pushButtonSetBase = QtGui.QPushButton(self)
self.pushButtonSetBase.setText("Set Base Shape")
self.pushButtonSetBase.setCheckable(True)
self.pushButtonSetBase.toggled.connect(self.on_pushButtonSetBase_toggled)
self.layoutHorizontal.addWidget(self.pushButtonSetBase)
#some other things here...
#QtCore.pyqtSlot()
def on_pushButtonSetBase_toggled(self):
shapeCSVeditor.rowOverride = True
pass
def on_BaseRow_Changed(self):
self.pushButtonSetBase.setChecked(False)
return
Class 2:
class CSVModel(QtCore.QAbstractTableModel):
# Establish inital settings and branch processes
def __init__(self, iface, fileName, editorType, parent=None):
super(CSVModel,self).__init__()
self.propertiesFile = r'some file'
self.areaStressFile = r'some other file'
self.iface = iface
self.rows = []
self.editorType = editorType
self.loadCSV()
self.iface.mapCanvas().selectionChanged.connect(self.addRow)
# add rows to the TableView based on object selection(s) in Qgis.mapCanvas
def addRow(self):
override = shapeCSVeditor.rowOverride
selectedFeatures = selectedLayer.selectedFeatures()
if override:
for feature in selectedFeatures:
self.rows.pop(0)
feat_Attributes = []
feat_Attributes.extend([self.iface.activeLayer().name()+'_'+str(feature.id())])
feat_Attributes.extend(['',]*(len(self.header)-1))
self.beginResetModel()
self.rows.insert(0,feat_Attributes)
shapeCSVeditor.rowOverride = False
self.endResetModel()
shapeCSVeditor.on_BaseRow_Changed# <<<=== wrong-diddily!
break
PS - if parentheticals are added to the 'shapeCSVeditor()' 3 arguments are requisite as referenced in the Button class, and if parentheticals are added to 'on_BaseRow_Changed', the return is;
TypeError: unbound method on_BaseRow_Changed() must be called with
shapeCSVeditor instance as first argument (got nothing instead)
What you are doing is strange.
In python, the first argument of a class method is always the object itself.
So, in your:
def on_BaseRow_Changed(self):
self.pushButtonSetBase.setChecked(False)
# return => This return is useless
if you don't provide an object then you can't access the pushbutton.
You didn't gave us all the code but I think you should provide your addRow with the shapeCSVeditor object that you want to update:
def addRow(self, shapeCSVObj):
override = shapeCSVObj.rowOverride
if override:
for feature in selectedFeatures:
self.rows.pop(0)
feat_Attributes = []
feat_Attributes.extend([self.iface.activeLayer().name()+'_'+str(feature.id())])
feat_Attributes.extend(['',]*(len(self.header)-1))
self.beginResetModel()
self.rows.insert(0,feat_Attributes)
shapeCSVObj.rowOverride = False
self.endResetModel()
shapeCSVObj.on_BaseRow_Changed()
break
Somewhere you must have a shapeCSVeditor that is created. You should provide it to you outside class.
Hope this helps.
class shapeCSVeditor(QtGui.QDialog, QtGui.QWidget):
valueShare = []
rowOverride = False
def __init__(self, iface, fileName, editorType, parent=None):
super(shapeCSVeditor, self).__init__(parent)
self.iface = iface
self.editorType = editorType
self.fileName = fileName
self.tableView = QtGui.QTableView(self)
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
self.tableData = CSVModel(self,iface,fileName,editorType)
^^==not implementing 'self' (shapeCSVeditor object) was the problem!
self.tableView.setModel(self.tableData)
...
self.pushButtonSetBase = QtGui.QPushButton(self)
self.pushButtonSetBase.setText("Set Base Shape")
self.pushButtonSetBase.setCheckable(True)
self.pushButtonSetBase.clicked.connect(self.on_pushButtonSetBase_toggled)
...
#QtCore.pyqtSlot()
def on_pushButtonSetBase_toggled(self):
self.rowOverride = True
#QtCore.pyqtSlot()
def on_BaseRow_Changed(self):
self.rowOverride = False
self.pushButtonSetBase.setChecked(False)
///////////////////////////////////////////////////////////////////////////////////////
class CSVModel(QtCore.QAbstractTableModel):
def __init__(self, shapeCSVeditor, iface, fileName, editorType):
super(CSVModel,self).__init__()
self.propertiesFile = r'foo'
self.areaStressFile = r'bar'
self.tableView = shapeCSVeditor <<== proper passing of shapeCSVeditor object! (?)
self.iface = iface
self.rows = []
self.editorType = editorType
self.loadCSV()
self.iface.mapCanvas().selectionChanged.connect(self.addRow)
...
def addRow(self):
selectedFeatures = selectedLayer.selectedFeatures()
if self.tableView.rowOverride:
for feature in selectedFeatures:
self.rows.pop(0)
feat_Attributes = []
feat_Attributes.extend([self.iface.activeLayer().name()+'_'+str(feature.id())])
feat_Attributes.extend(['',]*(len(self.header)-1))
self.beginResetModel()
self.rows.insert(0,feat_Attributes)
self.endResetModel()
self.tableView.rowOverride = False
self.tableView.on_BaseRow_Changed()
Radical. Works for the current needs.
Now the question is if its proper to python 'standards'.
Quite new to writing, so its possible more needs fixed.
High thanks to Plouff for the clues.
I'm setting up a custom PlotDataItem to recieve mouseDragEvents. I've adjusted this answer to my needs. For now I've just added a simple setData to the event to check if it's working. The custom PlotDataItem is as:
class CustomPlotItem(pg.PlotDataItem):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def setParentItem(self, parent):
super().setParentItem(parent)
self.parentBox = self.parentItem().parentItem()
def mouseDragEvent(self, ev):
if ev.button() != QtCore.Qt.LeftButton:
ev.ignore()
return
if ev.isStart():
if self.parentBox.curveDragged != None or not self.mouseShape().contains(ev.pos()):
ev.ignore()
return
self.parentBox.curveDragged = self
elif ev.isFinish():
self.parentBox.curveDragged = None
return
elif self.parentBox.curveDragged != self:
ev.ignore()
return
self.setData([40,50,60,200],[20,50,80,500])
ev.accept()
The PlotDataItem is added to a custom ViewBox this implements curveDragged, so I know which curve is being dragged, if any. I've also disabled the ViewBox's mouseDragEvents for debugging purposes.
However when try to drag the line in the ViewBox, nothing happens. Also if I add an exception at the top of the mouseDragEvent nothing happens. This leads me to believe mouseDragEvent is not being called at all.
Im using Python 3.3 (Anaconda Distribution) and the develop version (0.9.9) of pyqtgraph.
I hope someone can help me with this :). Thanks in advance.
PlotDataItem is a wrapper around a PlotCurveItem and a ScatterPlotItem. As such, it does not actually have any graphics or a clickable shape of its own. I would try making a subclass of PlotCurveItem instead. If you really need to use PlotDataItem, then it is possible to modify it such that it inherits its shape from the wrapped curve:
class CustomPlotItem(pg.PlotDataItem):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
# Need to switch off the "has no contents" flag
self.setFlags(self.flags() & ~self.ItemHasNoContents)
def mouseDragEvent(self, ev):
print("drag")
if ev.button() != QtCore.Qt.LeftButton:
ev.ignore()
return
if ev.isStart():
print("start")
elif ev.isFinish():
print("finish")
def shape(self):
# Inherit shape from the curve item
return self.curve.shape()
def boundingRect(self):
# All graphics items require this method (unless they have no contents)
return self.shape().boundingRect()
def paint(self, p, *args):
# All graphics items require this method (unless they have no contents)
return
def hoverEvent(self, ev):
# This is recommended to ensure that the item plays nicely with
# other draggable items
print("hover")
ev.acceptDrags(QtCore.Qt.LeftButton)
I'm wanting to use an NSOpenPanel for an application I'm designing. Here's what I have so far:
#objc.IBAction
def ShowOpenPanel_(self, sender):
self.panel = NSOpenPanel.openPanel()
self.panel.setCanChooseFiles_(False)
self.panel.setCanChooseDirectories_(True)
NSLog(u'Starting OpenPanel')
self.panel.beginForDirectory_file_types_modelessDelegate_didEndSelector_contextInfo_(
self.defaults.objectForKey_(u'projpath'),
objc.nil,
objc.nil,
self,
objc.selector(self.OpenPanelDidEnd_returnCode_contextInfo_,
signature='v:#ii'),
objc.nil)
NSLog(u'OpenPanel was started.')
def OpenPanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context):
NSLog('Panel ended.')
if (returnCode == NSOKButton):
NSLog(u'User selected OK')
path = self.panel.filenames()[0]
self.defaults.setObject_forKey_(path, u'projpath')
del self.panel
The main two lines I'm concerned about are:
objc.selector(self.OpenPanelDidEnd_returnCode_contextInfo_,
signature='v:#ii'),
objc.nil) #this is the argument that gets passed as the void pointer
The third argument is supposed to be a void pointer. Since I don't intend to use that data, I'd rather just leave it empty. I've tried making the signature 'v:#iv' and tried using objc.NULL and python's None, and just about every combination of all these things. What is the best way to handle this?
I think you don't need to use objc.selector at all; try this instead:
#objc.IBAction
def ShowOpenPanel_(self, sender):
self.panel = NSOpenPanel.openPanel()
self.panel.setCanChooseFiles_(False)
self.panel.setCanChooseDirectories_(True)
NSLog(u'Starting OpenPanel')
self.panel.beginForDirectory_file_types_modelessDelegate_didEndSelector_contextInfo_(
self.defaults.objectForKey_(u'projpath'),
objc.nil,
objc.nil,
self,
self.OpenPanelDidEnd_returnCode_contextInfo_,
objc.nil)
NSLog(u'OpenPanel was started.')
I've also found that I need to decorate the end-of-panel function with PyObjCTools.AppHelper.endSheetMethod:
#PyObjCTools.AppHelper.endSheetMethod
def OpenPanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context):
NSLog('Panel ended.')
if (returnCode == NSOKButton):
NSLog(u'User selected OK')
path = self.panel.filenames()[0]
self.defaults.setObject_forKey_(path, u'projpath')
del self.panel
Here's how I would write what you have:
#objc.IBAction
def showOpenPanel_(self, sender):
panel = NSOpenPanel.openPanel()
panel.setCanChooseFiles_(False)
panel.setCanChooseDirectories_(True)
NSLog(u'Starting openPanel')
panel.beginForDirectory_file_types_modelessDelegate_didEndSelector_contextInfo_(
self.defaults.objectForKey_(u'projpath'), #forDirectory
None, #file
None, #types
self, #modelessDelegate
self.openPanelDidEnd_returnCode_contextInfo_, #didEndSelector
None) #contextInfo
NSLog(u'openPanel started')
#PyObjCTools.AppHelper.endSheetMethod
def openPanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context):
NSLog(u'Panel ended')
if returnCode != NSOKButton:
return
NSLog(u'User selected OK')
path = panel.filenames()[0]
self.defaults.setObject_forKey_(path, u'projpath')
Explanation of changes: I always use None rather than objc.nil and it hasn't messed me up yet; I don't think your panel needs to be a property of self since you get it in your return function; objc convention is to have the first letter of your function in lower case.
The right way to open the panel is:
#objc.IBAction
def showOpenPanel_(self, sender):
panel = NSOpenPanel.openPanel()
panel.setCanChooseFiles_(False)
panel.setCanChooseDirectories_(True)
NSLog(u'Starting openPanel')
panel.beginForDirectory_file_types_modelessDelegate_didEndSelector_contextInfo_(
self.defaults.objectForKey_(u'projpath'), #forDirectory
None, #file
None, #types
self, #modelessDelegate
'openPanelDidEnd:returnCode:contextInfo:', #didEndSelector
None) #contextInfo
NSLog(u'openPanel started')
Dan's code works as well, but IMHO my variant is slighly clearer: you don't pass the actual method but the name of the method that should be called.