is there any way to have an input box inside of an message box opened with the ctypes library? so far I have:
import ctypes
messageBox = ctypes.windll.user32.MessageBoxA
title = 'Title'
text = 'Message box!'
returnValue = messageBox(None, text, title, 0x40 | 0x1)
print returnValue
and this gives a message box with an image icon and two buttons, both of which I know how to change, and it sets a variable "returnValue" to a number representing the button clicked. However, I also need a variable that will set to a string input in the message box. The reason I need this and I can't just do simple a = raw_input('prompt') is that I want the program itself to run in the background (it would launch itself on logon).
If you want a simple solution, use the PyMsgBox module. It uses Python's built-in tkinter library to create message boxes, including ones that let the user type a response. Install it with pip install pymsgbox.
The documentation is here: https://pymsgbox.readthedocs.org/
The code you want is:
>>> import pymsgbox
>>> returnValue = pymsgbox.prompt('Message box!', 'Title')
Message box is for messages only. What you need is QDialog. You can create it in QtDesigner(I have login dialog created this way, with 2 QLineEdit for username and pass, 2 buttons in QDialogButtonBox and QCombobox for language choose). You'll get .ui file, which you'll need to convert into .py this way in cmd:
pyuic4 -x YourLoginDialogWindow.ui -o YourLoginDialogWindow.py
import created YourLoginDialogWindow.py and you can use it and implement any method you need:
import YourLoginDialogWindow
class YourLoginDialog(QtGui.QDialog):
def __init__(self, parent = None):
super(YourLoginDialog, self).__init__(parent)
self.__ui = YourLoginDialogWindow.Ui_Dialog()
self.__ui.setupUi(self)
...
self.__ui.buttonBox.accepted.connect(self.CheckUserCredentials)
self.__ui.buttonBox.rejected.connect(self.reject)
def GetUsername(self):
return self.__ui.usernameLineEdit.text()
def GetUserPass(self):
return self.__ui.passwordLineEdit.text()
def CheckUserCredentials(self):
#check if user and pass are ok here
#you can use self.GetUsername() and self.GetUserPass() to get them
if THEY_ARE_OK :
self.accept()# this will close dialog and open YourMainProgram in main
else:# message box to inform user that username or password are incorect
QtGui.QMessageBox.about(self,'MESSAGE_APPLICATION_TITLE_STR', 'MESSAGE_WRONG_USERNAM_OR_PASSWORD_STR')
in your __main__ first create login dialog and then your main window...
if __name__ == "__main__":
qtApp = QtGui.QApplication(sys.argv)
loginDlg = YourLoginDialog.YourLoginDialog()
if (not loginDlg.exec_()):
sys.exit(-1)
theApp = YourMainProgram.YourMainProgram( loginDlg.GetUsername(), loginDlg.GetPassword())
qtApp.setActiveWindow(theApp)
theApp.show()
sys.exit(qtApp.exec_())
Related
This question already has answers here:
clicked.connect() Error
(3 answers)
Closed 1 year ago.
I'm new to Python 3.8 and PyQT5 and I'm trying to make an application with a GUI.
I've created a login form with two QLineEdits (User and Password). I want to check if what the user entered as User/Password is in a database using a function that it's in other file once the user clicks the 'Login' button. The class I've created for the form is similar to the next one:
# Where the check function for the password is
from CLBK_CheckPassword import CheckPassword
from PyQt5 import QtWidgets
class W_Password(QtWidgets.QWidget):
def __init__(self, App):
super(W_Password,self).__init__()
# Set the window title and size
WindowHeight = 400
WindowWeight = WindowHeight/3
self.setWindowTitle("Login Window")
self.resize(WindowHeight,WindowWeight)
# Text box
self.Input_User = QtWidgets.QLineEdit()
self.Input_Password = QtWidgets.QLineEdit()
self.Input_Password.setEchoMode(QtWidgets.QLineEdit.Password)
# Button
self.Button_Login = QtWidgets.QPushButton("Login")
self.Button_Login.clicked.connect(CheckPassword())
# Layout and items positioning
self.Layout = QtWidgets.QGridLayout(self)
self.Layout.addWidget(self.Button_Login,3,2,1,2)
self.Layout.addWidget(self.Label_User,0,0,1,1)
self.Layout.addWidget(self.Input_User,0,1,1,3)
self.Layout.addWidget(self.Label_Password,1,0,1,1)
self.Layout.addWidget(self.Input_Password,1,1,1,3)
I have two problems with this code:
When the form is created and shown, the function 'CheckPassword()' is automatically executed (it should be only executed if the user press the button).
The second problem is that I've the following error once the function has been executed : TypeError: argument 1 has unexpected type 'NoneType'
Problem 1
You should pass the CheckPassword function as a parameter to event listener clicked.connect.
So
self.Button_Login.clicked.connect(CheckPassword())
line changes to:
self.Button_Login.clicked.connect(CheckPassword)
Problem 2
Your function CheckPassword seems to takes at least one parameter. So you have to change logic a little bit.
You can use lambda expression:
self.Button_Login.clicked.connect(lambda: (CheckPassword(THE_PARAMETER)))
Edit
Answer to: what if the function 'CheckPassword' returns something?
As musicamante mentioned you can have another method which would run your function and capture the return value like:
# Where the check function for the password is
from CLBK_CheckPassword import CheckPassword
from PyQt5 import QtWidgets
class W_Password(QtWidgets.QWidget):
def __init__(self, App):
super(W_Password,self).__init__()
...
self.Button_Login.clicked.connect(lambda: (self.checher()))
...
def checher(self):
value = CheckPassword(THE_PARAMETER)
# Do something with value
I try to validate if a editable QCombobox input is a directory or not before it gets added to the QCombobox.
from PySide import QtGui, QtCore
class DirValidator(QtGui.QValidator):
def __init__(self, cb_input):
super(DirValidator, self).__init__()
self._input = cb_input
def validate(self, _text, _pos):
_dir = QtCore.QDir(_text)
if self._input.hasFocus(): # ignore validation while editing not complete
return QtGui.QValidator.Acceptable
if QtCore.QDir.exists(_dir):
return QtGui.QValidator.Acceptable
return QtGui.QValidator.Invalid
dir_validator = DirValidator(self.cb_path.lineEdit())
self.cb_path.setValidator(dir_validator)
sadly it does not work properly because every input still gets added to the combobox when i hit enter.
Any suggestions?
EDIT: i also tried to use the validator on the QLineEdit like so:
dir_validator = DirValidator(self.cb_path.lineEdit())
self.cb_path.lineEdit().setValidator(dir_validator)
Does not work either.
EDIT: It kinda works...but when i press return "hasFocus" is still True so it is just accepting the input and then of course adding it to the combobox. if i get rid of "if self._input.hasFocus():" it does not accept any input if i type it in...just if a paste a complete directory path.
So what i need is a way to check if the edit is finished to then check if it is a directory.
And as far as i understand i can only check this in a combobox via QValidator...because it adds the input to the box right away...before i can intercept it in any way.
EDIT: i did find solution for my case. I just abandoned the whole validator approach. The purpose of that was to prevent the combobox from creating a new item if it was no valid directory...what i now did instead was to validate the input after it was finished by taking advantage of the QLineEdit().editingFinished() signal. After it created the new Item i just removed it again if the input was not valid and it also gave me the opportunity to add a error Popup telling the user that the input was no directory.
I do not see the need for hasFocus(), because if you are writing to the QLineEdit it obviously has the focus. If the path is incorrect then you must return a QValidator::Intermediate:
from PySide import QtGui, QtCore
class DirValidator(QtGui.QValidator):
def validate(self, _text, _pos):
_dir = QtCore.QDir(_text)
if _dir.exists():
return QtGui.QValidator.Acceptable
return QtGui.QValidator.Intermediate
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
combo = QtGui.QComboBox(editable=True)
dir_validator = DirValidator(combo.lineEdit())
combo.setValidator(dir_validator)
combo.show()
sys.exit(app.exec_())
In Tkinter for constructing the menubar with the <menu_item>.add_command() we need a string for the accelerator argument which will create the hotkey binding for a command.
I created a method, which is checking if the user's platform is Mac or other, and if it is, then returns the Command key string combined with the other keys.
But it doesn't work -> the menu is building, if I click on the menu-item it is working, but not working with the hot-keys. ALthough I can see the ⌘ + N in the menu..
My first thought is, that the self.hot_key() method is not called while passed as an argument..
import sys
import Tkinter
class app(object):
def __init__(self):
self.gui = Tkinter.Tk()
self.gui.minsize(width=640, height=320)
menu = Tkinter.Menu(self.gui)
filemenu = Tkinter.Menu(menu, tearoff=0)
filemenu.add_command(
label = 'New',
command = self.New,
accelerator = self.hot_key('n')
)
menu.add_cascade(
label = 'File',
menu = filemenu
)
self.gui.config(menu=menu)
self.text = Tkinter.Text(self.gui)
self.text.pack(expand=Tkinter.YES, fill=Tkinter.BOTH)
def hot_key(self, *keys):
super_key = 'Command' if sys.platform == 'darwin' else 'Control'
return '{super}+{keys}'.format(super=super_key, keys='+'.join(keys))
def New(self):
print "I'm working!"
App = app()
App.gui.mainloop()
According to this page,
The "accelerator" option is used to indicate the menu accelerator that
should be associated with this menu. This does not actually create the
accelerator, but only displays what it is next to the menu item. You
still need to create a binding for the accelerator yourself.
So your accelerator keyword argument is working as designed -- the Command-N symbol appears in your menu.
As mgilson suggests in a comment, you can use bind_all to get the keyboard combination to actually do something.
self.gui.bind_all("<Command-n>", lambda event: self.New())
I've got a small PyGTK program, that has a statusicon. Upon left click on the statusicon a window with a TextView should appear and a predefined text should be shown in the TextView widget. My problem is that I don't know how to pass the text to shown as a parameter to the method that creates the window. I can create the window with a TextView without problems, but I cannot insert text into it.
Here's my code:
import gtk
import keybinder
class PyPPrinter(object):
def __init__(self):
self.staticon = gtk.StatusIcon()
self.staticon.set_from_stock(gtk.STOCK_INDEX)
self.staticon.set_visible(True)
self.staticon.connect('activate', self.browser(output_text = 'text'))
gtk.main()
def browser(self, window, output_text):
browser = gtk.Window()
browser.set_usize(600, 500)
textbox = gtk.TextView()
text = gtk.TextBuffer()
text.set_text(output_text)
textbox.set_buffer(text)
browser.add(textbox)
browser.show_all()
if __name__ == '__main__':
PyPPrinter()
This code gives me an exception: TypeError: browser() takes exactly 3 arguments (2 given). Perhaps I should also pass a value for the window parameter, but what should it be?
Two variants:
Change connect part:
self.staticon.connect('activate', self.browser(output_text = 'text'))
to:
self.staticon.connect('activate', self.browser, 'text')
or change Handler-Signature:
def browser(self, window, output_text):
to:
def browser(self, window):
I try to automatically download a file by clicking on a link on the webpage.
After clicking on the link, I get the 'File Download' Window dialog with 'Open', 'Save' and 'Cancel' buttons. I would like to click the Save button.
I use watsup library in the following way:
from watsup.winGuiAuto import *
optDialog = findTopWindow(wantedText="File Download")
SaveButton = findControl(optDialog,wantedClass="Button", wantedText="Save")
clickButton(SaveButton)
For some reason it does not work. The interesting thing is that exactly the same
code works perfectly to click on 'Cancel' button, however it refuses to work with
'Save' or 'Open'.
Anybody knows what I should do?
Thank you very much,
Sasha
Sasha,
It is highly likely that the file dialog you refer to (the Security Warning file download dialog) will NOT respond to windows messages in this manner, for security reasons. The dialog is specifically designed to respond only to a user physically clicking on the OK button with his mouse. I think you will find that the Run button will not work this way either.
Try this:
from watsup.winGuiAuto import *
optDialog = findTopWindow(wantedText="File Download")
SaveButton = findControl(optDialog, wantedClass="Button", wantedText="Submit")
clickButton(SaveButton)
Sasha,
The code at this link is supposed to work. It uses ctypes instead of watsup.winGuiAuto, and relies on win32 calls. Here is the code:
from ctypes import *
user32 = windll.user32
EnumWindowsProc = WINFUNCTYPE(c_int, c_int, c_int)
def GetHandles(title, parent=None):
'Returns handles to windows with matching titles'
hwnds = []
def EnumCB(hwnd, lparam, match=title.lower(), hwnds=hwnds):
title = c_buffer(' ' * 256)
user32.GetWindowTextA(hwnd, title, 255)
if title.value.lower() == match:
hwnds.append(hwnd)
if parent is not None:
user32.EnumChildWindows(parent, EnumWindowsProc(EnumCB), 0)
else:
user32.EnumWindows(EnumWindowsProc(EnumCB), 0)
return hwnds
Here's an example of calling it to click the Ok button on any window that has
the title "Downloads properties" (most likely there are 0 or 1 such windows):
for handle in GetHandles('Downloads properties'):
for childHandle in GetHandles('ok', handle):
user32.SendMessageA(childHandle, 0x00F5, 0, 0) # 0x00F5 = BM_CLICK
It's possible that the save button is not always enabled. While it may look to your eye that it is, a program might see an initial state that you're missing. Check it's state and wait until it's enabled.
[EDIT] But it's possible that Robert is right and the dialog will just ignore you for security reasons. In this case, I suggest to use BeautifulSoup to parse the HTML, extract the URL and download the file in Python using the urllib2 module.