I am writing a Login page using MU with wxPython. (I am very new to Python)
The following is what I want to do:
Have a Login frame which allows user to enter username and password, after that the user click the login button
The Login frame will then be destroyed so that the main body of the program can get the Username and Password from the Login frame
The main program will pass the Username and Password to a function to check whether the Username and Password are valid or not.
After checking, the program will start a Loading frame to simulate a checking process (which is fake), this frame will be destroy after 0.5 second.
After the Loading frame destroyed, if the Username or Password is wrong, then the Login frame will be established again to ask the user to enter the information again, and then repeat the above step until the Username and Password are valid.
The below link is the video showing the right process of what I am talking about:
https://drive.google.com/file/d/12ZMkbZ6phcurO22Qk3RVYkGyYcidpKE2/view?usp=sharing
However, a problem suddenly appeared, by chance, the system will suddenly exit with exit code -1073741819, when this happened, the situation is like this:
https://drive.google.com/file/d/12_8nwG31kpGsWGndeqFYl5aJ2zHXwQje/view?usp=sharing
And when this happened, it didn't leave any Traceback for me to debug, all it left is just like this:
https://drive.google.com/file/d/1olyRki1UeiCw0GpBLdrP7FhXXXoE0Xub/view?usp=sharing
Moreover, as I mentioned, this situation happens by chance, I can't even repeat this error intentionally.
The below is the code I established the Login frame, I tried to simplify it for everyone to read easily:
Login_status = False
while Login_status == False:
Login_page_app = []
Login_page_app = wx.App()
Login_page = Login()
Login_page_app.MainLoop()
Login_Username = Login_page.getUsername()
Login_Password = Login_page.getPassword()
Login_status = Check_login_status(Login_Username, Login_Password)
After countless time of tracking (using the debug mode in Mu), I discovered that when the system exited suddenly, the program ended here:
Login_page_app.MainLoop()
I found that, when the program came to this, it will refer to the code in core.py:
def MainLoop(self):
"""
Execute the main GUI event loop
"""
rv = wx.PyApp.MainLoop(self)
self.RestoreStdio()
return rv
Then it will stop at:
rv = wx.PyApp.MainLoop(self)
After that, the system will exit suddenly with exit code -1073741819.
With my little knowledge of programing, I don't know what to do next... so can anyone help me please!
(If the above information is not enough to provide a full picture of the problem, please let me know, I will try my best to give more details)
After trying, I discovered that, because I use wx.Timer to destroy the Loading frame after 0.5 second, but after the Loading frame has been destroyed, I haven't stop the wx.Timer, so the Timer keep running and destroy the Loading frame for every 0.5 second. Therefore, when the Timer destroyed the Loading frame second time, there is no Loading frame there and causing the system crash.
Now, I solved the problem when I stop the Timer after destroying the loading frame.
Orginal code:
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.onClose, self.timer)
self.timer.Start(5000)
def onClose(self, event):
self.Destroy()
New code:
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.onClose, self.timer)
self.timer.Start(5000)
def onClose(self, event):
self.Destroy()
self.timer.Stop()
Related
I'm currently working on an application written in Kivy in python.
I have 2 multiprocessing.Processes running:
One is a process for the RFID-reader, I open a popup that lets me choose a screen to go to.
The second process is for Serial communication.
I use queues to communicate with my main thread, and that works great. The problem I'm facing at the moment is I need to introduce a learning process to my Kivy program.
I have 4 screens which I call
mainscreen
adminscreen
managementscreen
initialscreen <- This screen runs only once, once it's set, the screen won't be accessable anymore.
This is the function that gets called when I push the button inside the initialscreen:
def startLearningProcess(self, lockeramount):
self.lockeramount = int(lockeramount)
with Database(self.databasepath) as db:
db.truncate_table('LOCKERS')
# resetting primary key sequence to make the ids start from 1
db.reset_primary_key_sequence('LOCKERS')
for locker in range(int(lockeramount)):
# use a with statement to automatically close the db after the operations
with Database(self.databasepath) as db:
# inserting the given amount lockers to the database as rows.
db.insertLockerRow(locker+1,'Pieter')
if I add the following to the function, 5 popups get opened all at once:
while self.lockeramount != 0:
popup = Popup(title='Test popup', auto_dismiss=True, content=Label(text='Hello world'), size_hint=(None, None), size=(400, 400))
popup.open()
self.lockeramount -= 1
When I input the number 5 into my interface, I want to have 5 popups to open up for me one by one. How can I make it so when I push a button I open up 1 popup, instead of all 5 at once? I apologize for my grammar, english is not my first language.
EDIT:
while John's answer worked perfectly, I was looking for another solution that did not use threading. I solved it by doing the following:
In my class InitialScreen(Screen): I added 2 variables, a bool that starts out with False (booleanUp) and a int variable that starts at 0 (lockeramount).
When I enter my def startLearningProcess I set the lockeramount variable to the number I input into my screen. I added an interval to the startLearningProcess function: Clock.schedule_interval(lambda dt: self.schedule_popups(), 1). I then added the following functions:
def close_popup(self, instance):
self.booleanUp = False
def schedule_popups(self):
if self.lockeramount > 0 and not self.booleanUp:
print(f'opening up popup {self.lockeramount}')
popup = Popup(title='MyPopup', content=Label(text='Abba ' + str(self.lockeramount)), size_hint=(0.5, 0.5))
popup.bind(on_dismiss=self.close_popup)
self.lockeramount -= 1
self.booleanUp = True
popup.open()
else:
print('not opening another popup')
When I open a new popup, I set the boolean to true, so that with the next interval it won't open another interval. I made an on_dismiss event that resets the variable back to False and bound it to my popup.
You can use a Queue to make the Popups wait. Define a custom Popup that accepts a Queue in its __init__() method, and sends something (could even be None) to the Queue when it is dismissed. And your loop can use the Queue to wait for the Popups to be dismissed.
Here is a custom Popup that uses a Queue:
class MyPopup(Popup):
queue = ObjectProperty(None)
def dismiss(self, *_args, **kwargs):
super(MyPopup, self).dismiss(*_args, **kwargs)
if self.queue:
self.queue.put(None)
For this to work, you must run it in another thread. Otherwise, waiting for the Queue on the main thread will never end, because holding the main thread will prevent the Popup from dismissing. Here is some code that shows 5 Popups in succession, one at a time:
def doit(self):
threading.Thread(target=self.popup_thread).start()
def popup_thread(self):
self.queue = Queue()
for i in range(5):
Clock.schedule_once(self.show_popup)
self.queue.get()
def show_popup(self, dt):
popup = MyPopup(title='MyPopup', content=Label(text='Abba ' + str(dt)), size_hint=(0.5, 0.5), queue=self.queue)
popup.open()
To start the Popups, just call the doit() method, probably as an action associated with a Button.
I am working with some proprietary business software written in DB/C, a short-lived variant of COBOL over 20 years ago with no access to source code or useful documentation of any kind. I've been trying to automate some of our processes with python, and have written a handler object that can pass commands and send various key codes using pyautogui but I have reached a barrier where sometimes I need to rely on the output of the previous routine to decide when/whether to trigger the next. The software GUI runs in a console window, similar to ncurses.
How can I copy the entire contents of my console window to my clipboard? If there is a way to copy it directly to a local python variable that would be ideal; Once I have the screen text I can just match the part I need with a regex.
Here is my handler so far just to give you an idea:
import pyautogui
import time
import subprocess
class ECISession:
def __init__(self, username, password):
self.username = username
self.password = password
self.openECI()
self.login(username, password)
def openECI(self):
# Open ECI as a subprocess
subprocess.Popen("F:\\ECI\\hp4\\test\\s.bat")
def login(self, username, password):
# Login to ECI Session with username and password
time.sleep(15)
pyautogui.write(username, interval=0.1)
pyautogui.press('enter')
pyautogui.write(password, interval=0.1)
pyautogui.press('enter')
pyautogui.write('Y') # Make sure it ignores duplicate login
#staticmethod
def send(text='', supressEnter=False):
# Send passed text to console and press enter unless specified to supress
if text != '':
pyautogui.write(text, interval=0.1)
if supressEnter:
pass
else:
pyautogui.press('enter')
time.sleep(1)
#staticmethod
def sendF3(n=1):
# Send F3 save key 1 time by default, n times if needed
while n != 0:
pyautogui.press('F3')
n -= 1
#staticmethod
def sendF4(n=1):
# Send F4 search key 1 time by default, n times if needed
while n != 0:
pyautogui.press('F4')
n -= 1
#staticmethod
def readscreen():
# return entire console window text
pass
The readscreen() method is what I need to figure out. I just want to return the console content. I would just use the mouse to highlight the entire thing and press enter, but I have no way to make sure the window starts in the same place every time; It can't be maximized or it will crash since it expects an 80 column display with a specific font size.
FYI, the software in question is HealthPac by Eldorado Computing Incorporated. If any of you have used it you know the struggle. I would post some screenshots but it may be a HIPPA violation or something.
I am writing a script intended to be used by members of a project team. As part of the script, I am launching a 3rd party proprietary application run through Citrix. I am going to use the script mostly to send keys to this application, but the first step once it launches is for the user to log in.
Because I would like the user to log in while the script is running, rather than asking for user/pass from some kind of GUI input earlier, and because the time it takes Citrix to launch varies, I would like to include some kind of logic that detects when the user has logged in and then resume the script from there, rather than including an obnoxiously long implicit wait or risking the script timing out.
Is there a way to detect user keystrokes using win32com.client (or to detect a change in state of the application itself)? See below for the relevant code to launch the app:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.Run('C:\Citrix\[rest of path])
EDIT:
Per Vasily's suggestion in the comments below, I attempted to adapt the "hook and listen" code to my scenario, but was unsuccessful. When I launch my file, I don't even get an exception message in my terminal, I get a Windows pop-up that says Python encountered a problem and needs to quit.
This is how I adapted it:
#[omitting import lines for brevity]
def on_timer():
"""Callback by timer out"""
win32api.PostThreadMessage(main_thread_id, win32con.WM_QUIT, 0, 0);
def on_event(args):
"""Callback for keyboard and mouse events"""
if isinstance(args, KeyboardEvent):
for i in range(1,100):
time.sleep(1)
if args.pressed_key == 'Lcontrol':
break
def init():
hk = Hook()
hk.handler = on_event
main_thread_id = win32api.GetCurrentThreadId()
t = Timer(55.0, on_timer) # Quit after 55 seconds
t.start()
hk.hook(keyboard=True, mouse=True)
At the point when the 3rd party Citrix app begins to launch in my main script, I call hookandlisten.init().
As a reminder, my goal is to wait until the user sends a certain keystroke (here I chose Control) before proceeding with the rest of the main script.
Solved this by eliminating the timer and unhooking the keyboard upon the correct keystroke:
import win32api
import win32con
from pywinauto.win32_hooks import Hook
from pywinauto.win32_hooks import KeyboardEvent
from pywinauto.win32_hooks import MouseEvent
def on_event(args):
"""Callback for keyboard and mouse events"""
if isinstance(args, KeyboardEvent):
if args.current_key == 'Lcontrol' and args.event_type == 'key down':
print("Success")
hk.unhook_keyboard()
return
def init():
hk.handler = on_event
hk.hook(keyboard=True, mouse=False)
hk = Hook()
I am trying to automate some application using pywinauto but there is some step that I don't know how to solve.
When I am at the main window I click "proces" buton and after this there is a possibility to open 1 window from 3 different but only 1 at a time. Please help me to solve this.
example: main window --> buton click --> 1st window (process file) or 2nd window (Your password expired, set new password) or 3rd window (this user is already logged in please kill his session and continue or break) --> process 1 of 3 windows but which will show up I don't know --> ...
You can simply check whether 1st or 2nd or 3rd window does exist:
win1st = app.Window_(title="process file")
win2nd = app.Window_(title="Your password expired, set new password")
win3rd = app.Window_(title="this user is already logged in")
if win1st.Exists(timeout=5): # in seconds
# process win1st
elif win2nd.Exists(): # default timeout is 0.5 sec
# process win2nd
elif win3rd.Exists():
# process win3rd
For existing window you may check IsVisible() or IsActive() value as well.
Methods Wait('ready') and WaitNot('visible') are useful for making sure the window is here or out. timeout can be a param if not a default one used.
More efficient way (since previously we have to wait for 5+ seconds if second or third window raised):
from pywinauto.timings import WaitUntil
WaitUntil(5, 0.1, lambda: len(app.Windows_(visible_only=True)) >= 2)
title = app.active_().WindowText()
if title == "1st case":
# process file
elif title == "2nd case":
# change a password
elif title == "3rd case":
# kill session
I'm working on a project where I'm attempting to get a QMesssageBox to exit with the "accepted" condition in response to incoming MIDI data. The MIDI input library (pygame.midi) needs to poll the input to see if any data has arrived, so I start a QThread to handle this, and have it emit a "dataReceived" signal when data arrives in the buffer. I then attach this signal to the QMessageBox's accept() slot:
def midiLearn(self, mainWindowInstance, widget):
class midiLearnWait(QtCore.QThread):
dataReceived = QtCore.pyqtSignal()
def __init__(self, midiInputDevice, parent=None):
super(midiLearnWait, self).__init__(parent)
self.midiInputDevice = midiInputDevice
def run(self):
if self.midiInputDevice.poll():
self.dataReceived.emit()
if self.midiInputDevice:
midiLearnMessage = QtGui.QMessageBox(1, 'MIDI Learn', 'Please move a controller.',
QtGui.QMessageBox.Cancel)
midiInputThread = midiLearnWait(self.midiInputDevice)
#just trigger accept for testing
midiInputThread.dataReceived.connect(lambda: midiLearnMessage.accept())
midiInputThread.start()
ret = midiLearnMessage.exec_()
if ret == QtGui.QMessageBox.Cancel:
return
else:
QtGui.QMessageBox.warning(mainWindowInstance, 'MIDI Error', 'No MIDI input selected.')
Unfortunately, this doesn't seem to work - the message box never gets accepted when MIDI data gets sent to the program. I am not completely sure at this point if the problem is something to do with how I've configured the MIDI library, or in how I've done this GUI code. If anyone could point out any errors in how I've attempted to set up the GUI aspect of the code it would be much appreciated.
midiInputDevice.poll() shouldn't be a blocking call, so your thread runs once when started and immediately exits... and probably the poll call will return false, that's why the box stays there.
you'll either have to use midiInputDevice.read() (which should block), or poll the device in a loop until there is some data.