I am new to osx programming. I am using pyobjc to create the alerts. My understanding of Modal windows or dialogs is that modal windows require the user’s action before they can proceed. However, if I use runModal of NSAlert, I am still able to goto other apps while the alert is still shown. Is my understanding of modal dialogs incorrect.
class Alert(object):
def __init__(self, messageText):
super(Alert, self).__init__()
self.messageText = messageText
self.informativeText = ""
self.buttons = []
def displayAlert(self):
alert = NSAlert.alloc().init()
alert.setMessageText_(self.messageText)
alert.setInformativeText_(self.informativeText)
# alert.setAlertStyle_(NSInformationalAlertStyle)
alert.setAlertStyle_(NSCriticalAlertStyle)
for button in self.buttons:
alert.addButtonWithTitle_(button)
NSApp.activateIgnoringOtherApps_(True)
self.buttonPressed = alert.runModal()
def alert(message="Default Message", info_text="", buttons=["OK"]):
ap = Alert(message)
ap.informativeText = info_text
ap.buttons = buttons
ap.displayAlert()
return ap.buttonPressed
You would not be able to swap to any other apps if the modal dialog was a system modal dialog. In the case of your app, it prevents you from proceeding any further in the user interface of your own application, not in other applications.
In the case of your code, you're creating an application-modal dialog, which is as described in the NSAlert Documentation.
Related
I have created a checkable QComboBox where all the options are checkboxes. Everything works fine, except I can no longer click on the QLineEdit to open/close the combobox pop-up, the way a regular QComboBox would work.
I have tried to apply an event filter to the QLineEdit, as shown below, that should ideally close the combobox pop-up if it is currently open, and open it if it is currently closed. But instead, clicking on QLineEdit only opens the pop-up everytime.
I believe this is because the mouse button press (QEvent.MouseButtonPress) closes the pop-up (hence setting the self.isPopup boolean to False), so the mouse button release (QEvent.MouseButtonRelease) will always open the pop-up. I've tried to get the QCombobox to ignore the MouseButtonPress event, but to no avail. I'm not sure where I've gone wrong here - if anyone has any suggestions, it would be much appreciated.
(Here's the relevant parts of the code)
class CustomComboBox(QtWidgets.QComboBox):
def __init__(self):
super(CustomComboBox, self).__init__()
self.setModel(QtGui.QStandardItemModel(self)) # setting up widget to make it checkable
self.setEditable(True)
self.lineEdit().setReadOnly(True)
self.lineEdit().setPlaceholderText("--Select Option--")
self.isPopup = False # bool to close or open pop up
self.lineEdit().installEventFilter(self) # event filter for lineedit presses
def hidePopup(self):
super().hidePopup()
self.isPopup = False
def showPopup(self):
super().shwoPopup()
self.isPopup = True
def eventFilter(self, widget, event):
if widget == self.lineEdit():
if event.type() == QtCore.QEvent.MouseButtonRelease:
if self.isPopup:
self.hidePopup()
else:
self.showPopup()
return True
elif event.type() == QtCore.QEvent.MouseButtonPress:
event.ignore()
return True
return super(CustomComboBox, self).eventFilter(widget, event)
The problem is caused by the fact that making the combo editable, you actually have two widgets that can handle mouse events, and since QComboBox handles mouse buttons in a specific way (to allow proper popup management) that makes things difficult, because the popup normally closes after the button press.
Since your requirement for the editable line edit is just to write custom text, then just override the paintEvent by slightly changing the default behavior:
class CustomComboBox(QtWidgets.QComboBox):
customText = ''
def setCustomText(self, text):
if self.customText != text:
self.customText = text
self.update()
def paintEvent(self, event):
if not self.customText:
super().paintEvent(event)
return
painter = QStylePainter(self)
painter.setPen(self.palette().color(QPalette.Text))
opt = QStyleOptionComboBox()
self.initStyleOption(opt)
painter.drawComplexControl(QStyle.CC_ComboBox, opt)
opt.text = self.customText
painter.drawControl(QStyle.CE_ComboBoxaLabel, opt)
With the code above, you don't need to make the combo editable, and therefore there is no event filtering.
I have a modal with two buttons, one Accept and one Cancel.
I set the cancel button to be the default with .setDefault() and .setAutoDefault()
Pressing return activates the cancel-button, but when I press spacebar the accept-button is activated.
Why is the application/accept-button ignoring the defaultness-configuration and activates on spacebar presses rather than the cancel button? It seems like the accept-button has focus or something despite there being a different default.
Why would the default not have focus?
If I call cancel_button.setFocus() just before showing the modal (but not earlier than that), even the spacebar will activate the Cancel-button instead of the Acccept-button, so that solves the underlying problem.
The question is why spacebar and enter do not both activate the default button.
Minimal example:
The modal shows up when the program is run, as well as when the user presses X.
Press ctrl+Q to close the application.
import sys
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QApplication, QMainWindow, QGroupBox, QHBoxLayout, QVBoxLayout, \
QWidget, QShortcut, QDialog, QPushButton
class Modal(QDialog):
def __init__(self, parent):
super().__init__(parent)
self.resize(QSize(600, 300))
self.setParent(parent)
self.setWindowModality(True)
layout = QVBoxLayout()
self.setLayout(layout)
buttons = self.create_buttons()
layout.addWidget(buttons)
# This sets focus (when pressing spacebar), and makes the modal work as expected.
# The question is why is this needed to make spacebar default to activating Cancel?
# Why is spacebar activating Accept by default without this line?:
#self.cancel_button.setFocus()
def create_buttons(self):
button_groupbox = QGroupBox()
button_box_layout = QHBoxLayout()
button_groupbox.setLayout(button_box_layout)
# Despite setting the defaultness, pressing spacebar still activates the accept-button.
# Pressing return activates the cancel-button, however, and is expected behaviour.
# Why is the Accept-button being activated when space is pressed?
accept_button = QPushButton("Accept")
accept_button.clicked.connect(self.accept)
accept_button.setDefault(False)
accept_button.setAutoDefault(False)
self.accept_button = accept_button
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(self.reject)
cancel_button.setDefault(True)
cancel_button.setAutoDefault(True)
self.cancel_button = cancel_button
# This does not set focus (when pressing spacebar), maybe because it has not been added yet?
#cancel_button.setFocus()
button_box_layout.addWidget(accept_button)
button_box_layout.addWidget(cancel_button)
return button_groupbox
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
shortcut.activated.connect(app.quit)
shortcut = QShortcut(QKeySequence("X"), self)
shortcut.activated.connect(self.run_modal)
self.resize(QSize(800, 600))
self.show()
def showEvent(self, event):
self.run_modal()
def run_modal(self):
self.modal = Modal(self)
self.modal.finished.connect(self.modal_finished)
self.modal.show()
def modal_finished(self, result):
if result == 0:
print("CANCEL")
elif result == 1:
print("ACCEPT")
else:
raise Exception("BAD RESULT")
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwindow = MainWindow()
sys.exit(app.exec_())
By default, widgets receive focus based on the order in which they are added to a parent. When the top level window is shown, the first widget that accepts focus, following the order above, will receive input focus, meaning that any keyboard event will be sent to that widget first.
Note that when widgets are added to a layout, but were not created with the parent used for that layout, then the order follows that of the layout insertion.
The default property of QPushButtons, instead will "press" the button whenever the top level widget receives the Return or Enter keys are pressed, no matter of the current focused widget, and as long as the focused widget does not handle those keys.
In your case, the currently focused widget is the "Accept" button (since it's the first that has been added to the window), which results in the counter-intuitive behavior you're seeing.
If you want the cancel button to react to both Return/Enter keys (no matter what is the focused widget) and the space bar upon showing, then you have to explicitly call setFocus(). But there's a catch: since setFocus() sets the focus on a widget in the active window, it can only work as long as that widget already belongs to that window.
In your case, the cancel_button.setFocus() call done within create_buttons won't work because, at that point, the button doesn't belong to the top level window yet.
It does work when you do that after layout.addWidget(buttons), because then the button is part of the window.
So, considering the above:
if you want to set the focus on a widget, that widget must already belong to the top level widget before calling setFocus();
the default button will always be triggered upon Return/Enter keypress even if another button has focus;
With your current code, you either do what you already found out (using setFocus() on the instance attribute after adding the widget), or use a basic QTimer in the create_buttons function:
QTimer.singleShot(0, cancel_button.setFocus)
Note that:
while creating separate functions can help you to better organize your code, having a separate function that is just called once is often unnecessary (other than misleading and forcing the creation of instance attributes where they're not actually necessary); just separate code blocks with empty lines, unless those functions can be overridden by further subclasses;
setting a "Cancel" button that can be activated by Return/Enter is not a very good idea, as those keys are generally used for "Accept/Apply/Commit/Write/etc." purposes;
if you want to show a dialog as soon as its parent is shown, you shall only use a QTimer: QTimer.singleShot(0, self.run_modal); the paint event is certainly not a viable option (paint events occur very, very often, and in some systems even when the widget loses focus, which can cause recursion), nor is the showEvent() since that could happen when switching virtual desktops or unminimizing the window;
I want to develop a plugin that prints in the console window, but I don't want to customize the Tool Window component。
This is how I currently do it:
ToolWindow toolWindow = ToolWindowManager.getInstance(e.getProject()).getToolWindow("Run");
toolWindow.show();
ContentManager contentManager = toolWindow.getContentManager();
ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(e.getProject()).getConsole();
Content content = contentManager.getFactory().createContent(consoleView.getComponent(), "Generator", false);
contentManager.addContent(content );
contentManager.setSelectedContent(content);
consoleView.print(text, ConsoleViewContentType.NORMAL_OUTPUT);
consoleView.scrollTo(consoleView.getContentSize()-1);
But I found that if the window is not activated, the getToolWindow method gets null。
Is it possible to activate the window directly by means of code, rather than by activate it from the View -> Tool Windows menu?
I have an OS X app that I am working on (technically implemented in RubyMotion, but that doesn't matter). The WebView wraps a web app that triggers a JavaScript alert before allowing you to perform an action. It works correctly in a normal web browser, but is not displayed in the WebView.
What configuration setting am I missing, or how can I handle this feature?
#web_view = WebView.alloc.initWithFrame(NSMakeRect(0, 0, 1000, 500))
#web_view.setAutoresizingMask(NSViewMinXMargin|NSViewMaxXMargin|NSViewMinYMargin|NSViewMaxYMargin|NSViewWidthSizable|NSViewHeightSizable)
#web_view.setMainFrameURL('http://localhost:3000')
#mainWindow.contentView.addSubview(#web_view)
In order to handle JavaScript alerts and confirmation dialog boxes, you need to specify the WebUIDelegate:
#web_view = WebView.alloc.initWithFrame(NSMakeRect(0, 0, 1000, 500))
#web_view.setUIDelegate(self)
...and implement these methods:
def webView(web_view, runJavaScriptAlertPanelWithMessage: message, initiatedByFrame: frame)
alert = NSAlert.new
alert.addButtonWithTitle("OK")
alert.setMessageText(message)
alert.runModal
end
def webView(web_view, runJavaScriptConfirmPanelWithMessage: message, initiatedByFrame: frame)
result = NSRunInformationalAlertPanel("JavaScript", # title
message, # message
"OK", # default button
"Cancel", # alt button
nil)
NSAlertDefaultReturn == result
end
Hi In CRM2011 I created custom button in form. On click of that button it opens javascript modal dialog. This modal dialog calls html where silverlight app is embedded. So my question i s how can I get following information. If silverlight app is in form we may easily get following values but my silver light app opens in modal dialog.
var xrmProperty = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
You want to talk to the opener. For example, in JavaScript you'd call:
window.opener.Xrm.Page.getAttribute('cei_name').getValue()
to get the value of the "cei_name" attribute on the form.
Try Following code
dynamic xrmnew = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
if (xrmnew == null)
{
HtmlWindow parentWindow = HtmlPage.Window.GetProperty("parent") as HtmlWindow;
xrmnew = (ScriptObject)parentWindow.GetProperty("Xrm");
}
Guid Id = new Guid(xrmnew.Page.data.entity.getId());