Script doesn't stop - python

This is the code
Entrypoint.py
def Data_For_MakePdf_Db(self):
mese = self.MakePdf_Strip_Month_select.currentText()
anno = self.MakePdf_Strip_Year_select.currentText()
MakeStrip(anno, mese)
return ### Breakpoint here
and MakeStrip.py
class SpecialStyledItemDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
self._values = dict()
def add_text(self, text, row):
self._values[row] = text
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
row = index.row()
if row in self._values:
option.text = self._values[row]
option.displayAlignment = QtCore.Qt.AlignCenter
class MakeStrip(QtWidgets.QWidget):
def __init__(self, anno, mese):
super().__init__()
self.initUI(anno, mese)
def initUI(self, anno, mese):
self.title = "MAMbo - Strips di '%s' '%s' "%(mese, anno)
self.setWindowTitle(self.title)
self.setGeometry(50, 100, 900, 800)
self.callTable(anno, mese)
self.button = QtWidgets.QPushButton(self.tableWidget)
self.button.setGeometry(QtCore.QRect(440, 169, 70, 20))
self.button.setObjectName("button")
self.button.setText("close")
self.layout = QtWidgets.QHBoxLayout(self)
self.layout.addWidget(self.tableWidget)
self.show()
self.button.clicked.connect(self.on_click)
pippo = 0 ### Breakpoint here
def on_click(self):
print('Clicked')
# Functions.message_handler(message = "THIS WILL CLOSE THE APP")
return
def callTable(self, anno, mese):
# Create table
self.tableWidget = QtWidgets.QTableWidget()
self.tableWidget.move(100, 700)
self.tableWidget.setRowCount(33)
self.tableWidget.setColumnCount(3)
self.special_delegate = SpecialStyledItemDelegate()
self.tableWidget.setItemDelegate(self.special_delegate)
h_header = self.tableWidget.horizontalHeader()
h_header.hide()
for i in range(h_header.count()):
h_header.setSectionResizeMode(i, QtWidgets.QHeaderView.ResizeToContents)
v_header = self.tableWidget.verticalHeader()
v_header.hide()
v_header.setDefaultSectionSize(13)
self.tableWidget.setSpan(1, 0, 1, 3)
self.tableWidget.setSpan(0, 0, 1, 3)
...
pluto = 0
def on_click(self):
print('Clicked')
return
I use pycharm
Main problem
If I run the script I see for a fraction of second the result of Makescript even if there a push button
Alternative problem
If I debug it I need to put some breakpoints as shown in the scripts to see the result of the script
I know that the debugger keeps alive the connection but why do I need to put breakpoints in those positions to see the result?

The problem is in the following line:
MakeStrip(anno, mese)
What happens there is that you create an instance of MakeStrip which has absolutely no reference. The result is that it's created and immediately garbage collected as soon as its __init__ returns.
Imagine doing something like this:
def Data_For_MakePdf_Db(self):
list()
As soon as no reference to an object is left, python automatically destroys it. In the case above, you create an instance without any reference, so it gets immediately deleted.
Note that creating a local reference (a variable that exists only in the function) might be a solution, but since there is no locking, you'll have almost the same results, because the function will immediately returns, causing the instance to be destroyed as well:
def Data_For_MakePdf_Db(self):
strip = MakeStrip(anno, mese)
A possibility is to create a persistent reference, by making the object a member of the current instance:
def Data_For_MakePdf_Db(self):
self.strip = MakeStrip(anno, mese)
This has some, possibly unwanted, side effects: first of all, you could still switch between the current window to the new one (which will probably make things a bit confusing), and, most importantly, if you call Data_For_MakePdf_Db again while another MakeStrip window already exists, it will be probably destroyed and replaced by a new one.
A better and more consistent solution is to have MakeStrip a subclass of QDialog instead, and use its exec_(): it will make it a modal window and will return control only when the window is finally closed.
def Data_For_MakePdf_Db(self):
MakeStrip(self, anno, mese).exec_()
class MakeStrip(QtWidgets.QDialog):
def __init__(self, parent, anno, mese):
super().__init__(parent)
self.initUI(anno, mese)
def initUI(self, anno, mese):
# ...
self.button.clicked.connect(self.accept)
Note that in this specific case we can even create the dialog without the local or instance reference, thanks to the fact that exec_ creates an event loop that blocks the execution until the dialog is closed.
This is always a good idea, though, as you might need to know if the dialog was actually "accepted" or "rejected", or need access to some of the dialog's objects.
In that case, a local reference is required:
def Data_For_MakePdf_Db(self):
dialog = MakeStrip(<b>self</b>, anno, mese)
if dialog.exec_():
print(dialog.tableWidget.rowCount())
PS: capitalized names should always be used only for classes and constants, not for functions, so it should be data_for_MakePdf_Db; while you can name your function as you like, this standard convention is highly suggested, as it improves readability (it makes clear distinction between classes and functions). Read more on the official Style Guide for Python Code.

Related

Dropped customised QWidget item disappears in the QListWidget after internal drag/drop Pyside PyQt

I have adopted the code available on
Drag'N'Drop custom widget items between QListWidgets
to work with PySide6, after few modifications I have managed to run the code and it works as it should.
But there is one bug or flaw with this code.
If an item inside a QListWidget drags and drops over another item in the same QListWidget
object the items starts to disappear behind each other or get stacked over each other or sometimes being shown as a blank item.
Like below
I am not sure what is wrong or missing with the implemented code that causes the issue.
In addition I would like to disable Copying while Drag and Dropby all means, if the user holds the CTRL button while dragging+dropping internally the flash symbol changes to
changes to + symbol and items are added to the list.
This feature should get disabled and I do not know how to do it, if it is not
possible to disable the copy feature while dropping I would like to know if there is any workaround available, since no more than one of each item should exist in either of QWidgetList objects [listWidgetA & listWidgetB] no duplicates of any items is allowed in both lists.
To summarise I would like following issues to get solved.
The flaw or issue with disappearing or stacking items while dragging and dropping the items in the same list.
The possibility of disabling copying the items while holding CTRL key while dragging and dropping or suggestion for a workaround that prevents the items to get copied in the same list or other list.
Below I have enclosed the faulty code.
from PySide6 import QtGui, QtCore, QtWidgets
import sys, os, pathlib
class ThumbListWidget(QtWidgets.QListWidget):
def __init__(self, type, parent=None):
super(ThumbListWidget, self).__init__(parent)
self.setIconSize(QtCore.QSize(124, 124))
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
self.setDefaultDropAction(QtCore.Qt.MoveAction)
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.setAcceptDrops(True)
self.model().rowsInserted.connect(
self.handleRowsInserted, QtCore.Qt.QueuedConnection)
def handleRowsInserted(self, parent, first, last):
print(f"first:{first} last:{last} parent:{parent}")
for index in range(first, last + 1):
item = self.item(index)
if item is not None and self.itemWidget(item) is None:
index, name, icon = item.data(QtCore.Qt.UserRole)
widget = QCustomQWidget()
widget.setTextUp(index)
widget.setTextDown(name)
widget.setIcon(icon)
item.setSizeHint(widget.sizeHint())
self.setItemWidget(item, widget)
class Dialog_01(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.listItems = {}
myQWidget = QtWidgets.QWidget()
myBoxLayout = QtWidgets.QHBoxLayout()
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.myQListWidget = ThumbListWidget(self)
myBoxLayout.addWidget(self.myQListWidget)
images_files_dir = pathlib.Path(__file__).parent.absolute().joinpath("custom_qlistwidget")
for data in [
('No.1', 'Meyoko', pathlib.Path(images_files_dir).joinpath('among-us-small-green.png')),
('No.2', 'Nyaruko', pathlib.Path(images_files_dir).joinpath('among-us-small-yellow.png')),
('No.3', 'Louise', pathlib.Path(images_files_dir).joinpath('among-us-small-red.png'))]:
myQListWidgetItem = QtWidgets.QListWidgetItem(self.myQListWidget)
# store the data needed to create/re-create the custom widget
myQListWidgetItem.setData(QtCore.Qt.UserRole, data)
self.myQListWidget.addItem(myQListWidgetItem)
self.listWidgetB = ThumbListWidget(self)
myBoxLayout.addWidget(self.listWidgetB)
class QCustomQWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
# super(QCustomQWidget, self).__init__(parent)
super().__init__(parent)
self.textQVBoxLayout = QtWidgets.QVBoxLayout()
self.textUpQLabel = QtWidgets.QLabel()
self.textDownQLabel = QtWidgets.QLabel()
self.textQVBoxLayout.addWidget(self.textUpQLabel)
self.textQVBoxLayout.addWidget(self.textDownQLabel)
self.allQHBoxLayout = QtWidgets.QHBoxLayout()
self.iconQLabel = QtWidgets.QLabel()
self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
self.setLayout(self.allQHBoxLayout)
# setStyleSheet
self.textUpQLabel.setStyleSheet('''
color: rgb(0, 0, 255);
''')
self.textDownQLabel.setStyleSheet('''
color: rgb(255, 0, 0);
''')
def setTextUp(self, text):
self.textUpQLabel.setText(text)
def setTextDown(self, text):
self.textDownQLabel.setText(text)
def setIcon(self, imagePath):
self.iconQLabel.setPixmap(QtGui.QPixmap(imagePath))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
dialog_1.resize(480, 320)
app.exec()
Using the DragDrop flag automatically allows the possibility of duplicating internal items, since that is enabled by the Ctrl modifier, you just need to check for it in both dragMoveEvent and dropEvent, and eventually ignore it:
class ThumbListWidget(QtWidgets.QListWidget):
# ...
def dragMoveEvent(self, event):
if event.keyboardModifiers():
event.ignore()
else:
super().dragMoveEvent(event)
def dropEvent(self, event):
if event.keyboardModifiers():
event.ignore()
else:
super().dropEvent(event)
About the other point, I was able to reproduce the "empty" cell issue, but only randomly. It might be a bug caused by the interaction between the geometries of the list widget and the item widgets (especially considering that there's a QLabel with a pixmap which might cause some layout and polishing issues), but it's really difficult to find the actual cause, and for that reason it's almost impossible to find a proper solution or workaround.
If you are able to find a way to reproduce the issue consistently, I suggest you to update your question accordingly (but be aware that wouldn't mean that we could reproduce it too).
Consider that while using item widgets seems reasonable in many simple situations, a better solution is to use a custom delegate and override its paint function to draw the contents.

Is there any way to show help tab in pyqt5?

I am writing code for my collage project in pyqt5 where I need to make one help tab. I am planning to make help content-wise as most the software have as shown in the below image(the help of onlyoffice). Is there any way to write it easily?
The problem with that kind of interface, which shows multiple "tabs" embedded in the title bar, is that it's not easily doable with Qt, and you should implement the whole title bar by hand, which is not easy.
If you're looking for a simpler solution, I'd suggest to use a QTabWidget that doesn't show the tab bar if there's only one tab. If you're not already using a tabbed interface with closable tabs, you can set the tab widget to allow closable tabs and override the default methods in order to hide the close button if not really required.
class TabWidget(QtWidgets.QTabWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setDocumentMode(True)
self.setTabsClosable(True)
self.tabCloseRequested.connect(self.removeTab)
self.tabBar().hide()
def addTab(self, *args, **kwargs):
self.insertTab(-1, *args, **kwargs)
def insertTab(self, *args, **kwargs):
super().insertTab(*args)
closable = kwargs.get('closable', False)
if not closable:
index = args[0]
if index < 0:
index = self.count() - 1
for side in QtWidgets.QTabBar.LeftSide, QtWidgets.QTabBar.RightSide:
widget = self.tabBar().tabButton(index, side)
if isinstance(widget, QtWidgets.QAbstractButton):
self.tabBar().setTabButton(index, side, None)
break
self.tabBar().setVisible(self.count() > 1)
def removeTab(self, index):
super().removeTab(index)
self.tabBar().setVisible(self.count() > 1)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.tabWidget = TabWidget()
self.setCentralWidget(self.tabWidget)
self.main = QtWidgets.QWidget()
self.tabWidget.addTab(self.main, 'My program')
layout = QtWidgets.QGridLayout(self.main)
someButton = QtWidgets.QPushButton('Some button')
layout.addWidget(someButton, 0, 0)
layout.addWidget(QtWidgets.QLabel('Some label'), 0, 1)
helpButton = QtWidgets.QPushButton('Show help!')
layout.addWidget(helpButton, 0, 2)
textEdit = QtWidgets.QTextEdit()
layout.addWidget(textEdit, 1, 0, 1, 3)
self.helpTab = QtWidgets.QTextBrowser()
self.helpTab.setHtml('Hello, this is <b>help</b>!')
helpButton.clicked.connect(self.showHelp)
def showHelp(self):
for i in range(self.tabWidget.count()):
if self.tabWidget.widget(i) == self.helpTab:
break
else:
self.tabWidget.addTab(self.helpTab, 'Help!', closable=True)
self.tabWidget.setCurrentWidget(self.helpTab)
self.tabWidget.tabBar().show()
Now, since you also want some context-based help, you could hack your way through the whatsThis() feature. The "what's this" feature allows to show some context-based help in a small overlayed window when the window is in the "what's this mode" and the user clicks on a widget. We can use an event filter to detect when the user clicks on a widget and use the whatsThis() property as a context for showing the related help.
In the following example I'm using a simple dictionary that fills the QTextBrowser, but you can obviously use access to local documentation files or even the Qt Help framework.
Note that in order to use this approach, I had to install an event filter on all child widgets, and that's because Qt is able to react to "what's this" events if the widget actually has a whatsThis() property set. The trick is to set a whatsThis property for all child widgets when the window enters the what's this mode and install a specialized event filter on each of them, then uninstall the event filter as soon as the what's this mode is left.
NoWhatsThisText = '__NoWhatsThis'
NoWhatsThisValue = 'There is no help for this object'
HelpData = {
NoWhatsThisText: NoWhatsThisValue,
'someButton': 'Do something with the button',
'helpButton': 'Click the button to show this help',
'textEdit': 'Type <b>some text</b> to <i>read</i> it',
'mainWindow': 'A main window is cool!',
}
class WhatsThisWatcher(QtCore.QObject):
whatsThis = QtCore.pyqtSignal(str)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.WhatsThis:
whatsThis = source.whatsThis()
while whatsThis == NoWhatsThisText:
if not source.parent():
break
source = source.parent()
whatsThis = source.whatsThis()
self.whatsThis.emit(whatsThis)
event.accept()
return True
return super().eventFilter(source, event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
# ...
whatsThisAction = self.menuBar().addAction('What\'s this?')
whatsThisAction.triggered.connect(
QtWidgets.QWhatsThis.enterWhatsThisMode)
self.watchedWhatsThis = []
self.whatsThisWatcher = WhatsThisWatcher()
self.whatsThisWatcher.whatsThis.connect(self.showHelp)
self.installEventFilter(self.whatsThisWatcher)
someButton.setWhatsThis('someButton')
helpButton.setWhatsThis('helpButton')
textEdit.setWhatsThis('textEdit')
self.setWhatsThis('mainWindow')
def showHelp(self, context=''):
# ...
if context:
self.helpTab.setHtml(HelpData.get(context, NoWhatsThisValue))
if QtWidgets.QWhatsThis.inWhatsThisMode():
QtWidgets.QWhatsThis.leaveWhatsThisMode()

Clickable Qlabel: Binding multiple labels to a single function

I have all this labels declared in a initUi
They are constructed like that:
ligne_1_left_cord = 300
self.label_ligne_1_1 = QtWidgets.QLabel(self)
pic_signe_tab =QPixmap('img/tab.png')
self.label_ligne_1_1.setPixmap(pic_signe_tab)
self.label_ligne_1_1.move(ligne_1_left_cord,300)
self.label_ligne_1_1.mousePressEvent = self.label_click
self.label_ligne_1_2 = QtWidgets.QLabel(self)
self.label_ligne_1_2.setPixmap(pic_signe_tab)
self.label_ligne_1_2.move( ligne_1_left_cord +85,300)
When I click label_ligne_1_1 the function label_click does this:
def label_click(self,event):
signe_pixmap = QPixmap('img/tabx.png')
self.label_ligne_1_1.setPixmap(signe_pixmap)
Is there anyway I could pass a variable when I'm calling label_click in order to bind it to all labels and use the same function?
Something like this?:
def label_click(name_of_the_lable):
name_of_the_lable.setPixmap(x)
Meaning of course that no matter on what square you click, the pixmap will change and an X will appear
Overriding functions in that way is not suggested, especially for what are considered protected functions in Qt, which is the case of any *Event() function of QObjects and QWidgets.
Also, in your case you're just overwriting the method with the same signature, which would never allow you to get the source of the function call.
A possible solution would be to use a lambda with the source as a keyword argument:
self.label_ligne_1_1.mousePressEvent = lambda ev, label=self.label_ligne_1_1: self.label_click(label)
But I wouldn't suggest you to do that.
A better approach, instead, would be to install an event filter on each label, and then set the pixmap each time a mouse press event is captured:
class Squares(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
layout.setSpacing(0)
for row in range(4):
for col in range(4):
square = QtWidgets.QLabel()
square.setPixmap(QtGui.QPixmap('tab.png'))
layout.addWidget(square, row, col)
setattr(self, 'label_ligne_{}_{}'.format(row + 1, col + 1), square)
square.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
source.setPixmap(QtGui.QPixmap('tabx.png'))
return super().eventFilter(source, event)

QTreeView, class, arguments, instance and attributes

I get to POO, python and PyQt slowly and I have a problem to understand something about passing argument and attributes.
I found online a code dealing with QTreeView (see below) and I don't understand how Index is passed into showPath(). Also why self.filename and self.filepath are not passed to the instance?
I hope I am clear enough ... Thank a lot.
from PyQt4 import QtGui
class TreeViewWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(TreeViewWidget, self).__init__(parent)
self.model = QtGui.QFileSystemModel(self)
self.model.setRootPath(rootpath)
self.indexRoot = self.model.index(self.model.rootPath())
self.treeView = QtGui.QTreeView(self)
self.treeView.setExpandsOnDoubleClick(False)
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.indexRoot)
self.treeView.setColumnWidth(0,220)
self.treeView.clicked.connect(self.showPath)
self.treeView.doubleClicked.connect(self.openQuickLook)
self.labelFileName = QtGui.QLabel(self)
self.labelFileName.setText("File Name:")
self.lineEditFileName = QtGui.QLineEdit(self)
self.labelFilePath = QtGui.QLabel(self)
self.labelFilePath.setText("File Path:")
self.lineEditFilePath = QtGui.QLineEdit(self)
self.gridLayout = QtGui.QGridLayout()
self.gridLayout.addWidget(self.labelFileName, 0, 0)
self.gridLayout.addWidget(self.lineEditFileName, 0, 1)
self.gridLayout.addWidget(self.labelFilePath, 1, 0)
self.gridLayout.addWidget(self.lineEditFilePath, 1, 1)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addLayout(self.gridLayout)
self.layout.addWidget(self.treeView)
def givePathName(self, index):
indexItem = self.model.index(index.row(), 0, index.parent())
self.filename = self.model.fileName(indexItem)
self.filepath = self.model.filePath(indexItem)
def showPath(self, index):
self.givePathName(index)
self.lineEditFileName.setText(self.filename)
self.lineEditFilePath.setText(self.filepath)
I don't understand how index is passed into showPath()
You connect the widget's click signal to showPath explicitly:
self.treeView.clicked.connect(self.showPath)
part of this signal is the index of the specific item clicked on; this is passed as an argument to showPath automatically.
Also why self.filename and self.filepath are not passed to the instance?
They are instance attributes, they belong to the instance and are accessible to all methods of that instance. They are created in givePathName(), and are then part of the TreeViewWidget instance object. They start with self. because that is, by convention, the name given to the instance in instance methods (and the implicit first argument to those methods).
Putting that together:
def showPath(self, index):
# ^ the instance object, so you can access its attributes
# ^ the index of the specific item clicked
The clicked signal of your QTreeView pass the QModelIndex of the item clicked as an argument to any connected slots.
See Qt Documentation and PyQt signal and slots documentation.

Return value from wxPython Frame

Could someone show me how I could return a value from a wxPython Frame? When the use clicks close, I popup a message dialog asking him a question. I would like to return the return code of this message dialog to my calling function.
Thanks
Because the wxFrame has events that process via the app.MainLoop() functionality, the only way to get at the return value of a wx.Frame() is via catching an event.
The standard practice of handling events is typically from within the class which derives from wx.Window itself (e.g., Frame, Panel, etc.). Since you want code exterior to the wx.Frame to receive information that was gathered upon processing the OnClose() event, then the best way to do that is to register an event handler for your frame.
The documentation for wx.Window::PushEventHandler is probably the best resource and even the wxpython wiki has a great article on how to do this. Within the article, they register a custom handler which is an instance of "MouseDownTracker." Rather than instantiating within the PushEventHandler call, you'd want to instantiate it prior to the call so that you can retain a handle to the EventHandler derived class. That way, you can check on your derived EventHandler class-variables after the Frame has been destroyed, or even allow that derived class to do special things for you.
Here is an adaptation of that code from the wx python wiki (admittedly a little convoluted due to the requirement of handling the results of a custom event with a "calling" function):
import sys
import wx
import wx.lib.newevent
(MyCustomEvent, EVT_CUSTOM) = wx.lib.newevent.NewEvent()
class CustomEventTracker(wx.EvtHandler):
def __init__(self, log, processingCodeFunctionHandle):
wx.EvtHandler.__init__(self)
self.processingCodeFunctionHandle = processingCodeFunctionHandle
self.log = log
EVT_CUSTOM(self, self.MyCustomEventHandler)
def MyCustomEventHandler(self, evt):
self.log.write(evt.resultOfDialog + '\n')
self.processingCodeFunctionHandle(evt.resultOfDialog)
evt.Skip()
class MyPanel2(wx.Panel):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent)
self.log = log
def OnResults(self, resultData):
self.log.write("Result data gathered: %s" % resultData)
class MyFrame(wx.Frame):
def __init__(self, parent, ID=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, ID, title, pos, size, style)
self.panel = panel = wx.Panel(self, -1, style=wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.FULL_REPAINT_ON_RESIZE)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add((25, 25))
row = wx.BoxSizer(wx.HORIZONTAL)
row.Add((25,1))
m_close = wx.Button(self.panel, wx.ID_CLOSE, "Close")
m_close.Bind(wx.EVT_BUTTON, self.OnClose)
row.Add(m_close, 0, wx.ALL, 10)
sizer.Add(row)
self.panel.SetSizer(sizer)
def OnClose(self, evt):
dlg = wx.MessageDialog(self, "Do you really want to close this frame?", "Confirm Exit", wx.OK | wx.CANCEL | wx.ICON_QUESTION)
result = dlg.ShowModal()
dlg.Destroy()
if result == wx.ID_CANCEL:
event = MyCustomEvent(resultOfDialog="User Clicked CANCEL")
self.GetEventHandler().ProcessEvent(event)
else: # result == wx.ID_OK
event = MyCustomEvent(resultOfDialog="User Clicked OK")
self.GetEventHandler().ProcessEvent(event)
self.Destroy()
app = wx.App(False)
f2 = wx.Frame(None, title="Frame 1 (for feedback)", size=(400, 350))
p2 = MyPanel2(f2, sys.stdout)
f2.Show()
eventTrackerHandle = CustomEventTracker(sys.stdout, p2.OnResults)
f1 = MyFrame(None, title="PushEventHandler Tester (deals with on close event)", size=(400, 350))
f1.PushEventHandler(eventTrackerHandle)
f1.Show()
app.MainLoop()
You can get the result of clicking the OK, CANCEL buttons from the Dialog ShowModal method.
Given dialog is an instance of one of the wxPython Dialog classes:
result = dialog.ShowModal()
if result == wx.ID_OK:
print "OK"
else:
print "Cancel"
dialog.Destroy()
A few years late for the initial question, but when looking for the answer to this question myself, I stumbled upon a built-in method of getting a return value from a modal without messing with any custom event funniness. Figured I'd post here in case anyone else needs it.
It's simply this guy right here:
wxDialog::EndModal void EndModal(int retCode)
Ends a modal dialog, passing a value to be returned from the
*wxDialog::ShowModal invocation.*
Using the above, you can return whatever you want from the Dialog.
An example usage would be subclassing a wx.Dialog, and then placing the EndModal function in the button handlers.
class ProjectSettingsDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Project Settings", size=(600,400))
sizer = wx.BoxSizer(wx.VERTICAL) #main sized
sizer.AddStretchSpacer(1)
msg = wx.StaticText(self, -1, label="This is a sample message")
sizer.Add(msg, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 15)
horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(self, -1, 'OK')
self.Bind(wx.EVT_BUTTON, self.OnOK, okButton)
cancelBtn = wx.Button(self, -1, "Cancel")
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelBtn)
horizontal_sizer.Add(okButton, 0, wx.ALIGN_LEFT)
horizontal_sizer.AddStretchSpacer(1)
horizontal_sizer.Add(cancelBtn, 0, wx.ALIGN_RIGHT)
sizer.Add(horizontal_sizer, 0)
sizer.AddStretchSpacer(1)
self.SetSizer(sizer)
def OnOK(self, event):
self.EndModal(wx.ID_OK) #returns numeric code to caller
self.Destroy()
def OnCancel(self, event):
self.EndModal(wx.ID_CANCEL) #returns numeric code to caller
self.Destroy()
(Note: I just banged this code out quickly; didn't test the sizers)
As you can see, all you need to do is call the EndModal from a button event to return a value to whatever spawned the dialog.
I wanted to do the same thing, to have a graphical "picker" that I could run from within a console app. Here's how I did it.
# Fruit.py
import wx
class Picker (wx.App):
def __init__ (self, title, parent=None, size=(400,300)):
wx.App.__init__(self, False)
self.frame = wx.Frame(parent, title=title, size=size)
self.apple_button = wx.Button(self.frame, -1, "Apple", (0,0))
self.apple_button.Bind(wx.EVT_BUTTON, self.apple_button_click)
self.orange_button = wx.Button(self.frame, -1, "Orange", (0,100))
self.orange_button.Bind(wx.EVT_BUTTON, self.orange_button_click)
self.fruit = None
self.frame.Show(True)
def apple_button_click (self, event):
self.fruit = 'apple'
self.frame.Destroy()
def orange_button_click (self, event):
self.fruit = 'orange'
self.frame.Destroy()
def pick (self):
self.MainLoop()
return self.fruit
Then from a console app, I would run this code.
# Usage.py
import Fruit
picker = Fruit.Picker('Pick a Fruit')
fruit = picker.pick()
print 'User picked %s' % fruit
user1594322's answer works but it requires you to put all of your controls in your wx.App, instead of wx.Frame. This will make recycling the code harder.
My solution involves define a "PassBack" variable when defining your init function. (similar to "parent" variable, but it is normally used already when initiating a wx.Frame)
From my code:
class MyApp(wx.App):
def __init__ (self, parent=None, size=(500,700)):
wx.App.__init__(self, False)
self.frame = MyFrame(parent, -1, passBack=self) #Pass this app in
self.outputFromFrame = "" #The output from my frame
def getOutput(self):
self.frame.Show()
self.MainLoop()
return self.outputFromFrame
and for the frame class:
class MyFrame(wx.Frame):
def __init__(self, parent, ID, passBack, title="My Frame"):
wx.Frame.__init__(self, parent, ID, title, size=(500, 700))
self.passBack = passBack #this will be used to pass back variables/objects
and somewhere during the execution of MyFrame
self.passBack.outputFromFrame = "Hello"
so all in all, to get a string from an application
app = MyApp()
val = app.getOutput()
#Proceed to do something with val
Check this answer on comp.lang.python: Linkie
I don't think a wxFrame can return a value since it is not modal. If you don't need to use a wxFrame, then a modal dialog could work for you. If you really need a frame, I'd consider using a custom event.
It would go something like this:
(1) User clicks to close the wxFrame
(2) You override OnClose (or something like that) to pop up a dialog to ask the user a question
(3) Create and post the custom event
(4) Close the wxFrame
(5) Some other code processes your custom event
I think I just had the same problem as you. Instead of making that popup a frame, I made it a dialog instead. I made a custom dialog by inheriting a wx.dialog instead of a wx.frame. Then you can utilize the code that joaquin posted above. You check the return value of the dialog to see what was entered. This can be done by storing the value of the textctrl when the user clicks ok into a local variable. Then before it's destroyed, you get that value somehow.
The custom dialog section of this site helped me out greatly.
http://zetcode.com/wxpython/dialogs/

Categories