I have a program which will check for several conditions and check whether data is available or not. If there is any missing data then i will popup another window which will collect data in that window. It will have two buttons (Apply and Close). I want to return a value after button is triggered.
Both program.py and dataEntry.py have there own UIs designed in PyQt Disigner.
I want my program to wait for return value from other window. and depending on the input i will continue my other process.
Lets say program file is program.py and another window is dataEntry.py is imported in program.py
My dataEntry.py looks like
#imports necessary modules
class dataEntry(QtGui.QMainWindow,Ui_DataEntry):
def __init__(self):
super(dataEntry,self).__init__()
self.setupUi(self)
self.Btn_Apply.clicked.connect(self.ApplyChanges)
self.Btn_Close.clicked.connect(self.CancelChanges)
def ApplyChanges(self):
#This will trigger when ApplyButonn is clicked
#I want to return True value from here
return True
def CancelChanges(self):
#This will trigger when CancelButonn is clicked
#I want to return False value from here
return False
My program.py looks like
from dataEntry import dataEntry
class MainApp(QtGui.QMainWindow,Ui_MainApp):
def __init__(self):
super(MainApp,self).__init__()
self.setupUi(self)
self.CheckDetails()
def CheckDetails(self):
#Here i will check if required data is present else i will take data from dataEntry class
if checkFails:
input = dataEntry()
input.show()
#Here i want this class to wait until i get the result from dataEntry class
EntryResult = return value from dataEntry
if EntryResult:
#Do some thing when its True
else:
#Do some thing when its False
first of all import dataentry.py in program.py,
after this in your program.py
EntryResult = dataentry.py.Ui_DataEntry() #considering that the UI class in dataentry.py is Ui_DataEntry
This will run the dataentry.py and make your dataentry.py return the required value. This way, the program.py will wait till it gets a value from dataentry.py
Related
Hello I have two function. One Class is a watchdog and the other class is a pyqt gui. I have three QTextBrowser.
If I connect a button to the function in the QT Class and call the function my TextBrowser get updated normally.
If I trigger an event in the watchdog and the watchdog class calls my QT class, my TextBrowser didnt update.
This is how I call the function from another class
class Handler(FileSystemEventHandler):
#staticmethod
def on_any_event(event):
if event.is_directory:
return None
elif event.event_type == 'created':
# Event is created, you can process it now
print("Watchdog received created event - % s." % event.src_path)
window = MyWindow()
window.start_xl()
This is the function, which works when i call it with a button.
def update(self):
terminate('cmd.exe')
terminate('EXCEL.exe')
self.text_Browser = self.ui.textBrowser
self.text_Browser_2 = self.ui.textBrowser_2
self.text_Browser_3 = self.ui.textBrowser_3
self.text_Browser.setTextColor(QtGui.QColor('white'))
self.text_Browser_2.setTextColor(QtGui.QColor('white'))
self.text_Browser_3.setTextColor(QtGui.QColor('white'))
path = "C:/Program Files (x86)/STIHL/Faps/Temp"
# Check if the folder is empty
if os.listdir(path):
# Get a list of all files in the directory
files = os.listdir(path)
# The first file in the directory
first_file = files[0]
self.text_Browser.append("FINISHED")
self.text_Browser_2.append("goal_dir")
self.text_Browser_3.append(first_file)
else:
self.ui.label.setText("Ordner ist Leer")
pass
This is the function which is called by the class Handler. Everything is working besides the updating of the TextBrowser
def start_xl(self):
terminate('cmd.exe')
terminate('EXCEL.exe')
self.text_Browser = self.ui.textBrowser
self.text_Browser_2 = self.ui.textBrowser_2
self.text_Browser.setTextColor(QtGui.QColor('white'))
self.text_Browser_2.setTextColor(QtGui.QColor('white'))
print(self.text_Browser)
print(self.text_Browser_2)
# QtCore.QMetaObject.invokeMethod(self.text_Browser, "append", QtCore.Qt.QueuedConnection,
# QtCore.Q_ARG(str, "FINISHED"))
# QtCore.QMetaObject.invokeMethod(self.text_Browser_2, "append", QtCore.Qt.QueuedConnection,
# QtCore.Q_ARG(str, "goal_dir"))
self.text_Browser.setPlainText("FINISHED")
# self.text_Browser_2.append("goal_dir")
I tried it with invoke and append. The kill process get started, so the function is called.
Is there something I should consider?
This working code brings up a QFileDialog prompting the user to select a .csv file:
def load(self,fileName=None):
if not fileName:
fileName=fileDialog.getOpenFileName(caption="Load Existing Radio Log",filter="csv (*.csv)")[0]
...
...
Now, I'd like to change that filter to be more selective. The program saves each project as a set of three .csv files (project.csv, project_fleetsync.csv, project_clueLog.csv) but I only want the file dialog to display the first one (project.csv) in order to avoid presenting the user with too many choices when only a third of them can be handled by the rest of the load() function.
According to this post, it looks like the solution is to use a proxy model. So, I changed the code to the following (all of the commented lines in load() are things I've tried in various combinations):
def load(self,fileName=None):
if not fileName:
fileDialog=QFileDialog()
fileDialog.setProxyModel(CSVFileSortFilterProxyModel(self))
# fileDialog.setNameFilter("CSV (*.csv)")
# fileDialog.setOption(QFileDialog.DontUseNativeDialog)
# fileName=fileDialog.getOpenFileName(caption="Load Existing Radio Log",filter="csv (*.csv)")[0]
# fileName=fileDialog.getOpenFileName(caption="Load Existing Radio Log")[0]
# fileDialog.exec_()
...
...
# code for CSVFileSortFilterProxyModel partially taken from
# https://github.com/ZhuangLab/storm-control/blob/master/steve/qtRegexFileDialog.py
class CSVFileSortFilterProxyModel(QSortFilterProxyModel):
def __init__(self,parent=None):
print("initializing CSVFileSortFilterProxyModel")
super(CSVFileSortFilterProxyModel,self).__init__(parent)
# filterAcceptsRow - return True if row should be included in the model, False otherwise
#
# do not list files named *_fleetsync.csv or *_clueLog.csv
# do a case-insensitive comparison just in case
def filterAcceptsRow(self,source_row,source_parent):
print("CSV filterAcceptsRow called")
source_model=self.sourceModel()
index0=source_model.index(source_row,0,source_parent)
# Always show directories
if source_model.isDir(index0):
return True
# filter files
filename=source_model.fileName(index0)
# filename=self.sourceModel().index(row,0,parent).data().lower()
print("testing lowercased filename:"+filename)
if filename.count("_fleetsync.csv")+filename.count("_clueLog.csv")==0:
return True
else:
return False
When I call the load() function, I do get the "initializing CSVFileSortFilterProxyModel" output, but apparently filterAcceptsRow is not getting called: there is no "CSV filterAcceptsRow called" output, and, the _fleetsync.csv and _clueLog.csv files are still listed in the dialog. Clearly I'm doing something wrong...?
Found the solution at another stackoverflow question here.
From that solution:
The main thing to watch out for is to call
dialog.setOption(QFileDialog::DontUseNativeDialog) before
dialog.setProxyModel.
Also it looks like you then have to use fileDialog.exec_() rather than fileDialog.getOpenFileName. The value you set to setNameFilter does show up in the filter cyclic field of the non-native dialog, but is effectively just for decoration since the proxymodel filter overrides it. In my opinion that is a good thing since you can put wording in the filter cyclic that would indicate something useful to the user as to what type of filtering is going on.
Thanks to users Frank and ariwez.
UPDATE: to clarify, here's the full final code I'm using:
def load(self,fileName=None):
if not fileName:
fileDialog=QFileDialog()
fileDialog.setOption(QFileDialog.DontUseNativeDialog)
fileDialog.setProxyModel(CSVFileSortFilterProxyModel(self))
fileDialog.setNameFilter("CSV Radio Log Data Files (*.csv)")
fileDialog.setDirectory(self.firstWorkingDir)
if fileDialog.exec_():
fileName=fileDialog.selectedFiles()[0]
else: # user pressed cancel on the file browser dialog
return
... (the rest of the load function processes the selected file)
...
# code for CSVFileSortFilterProxyModel partially taken from
# https://github.com/ZhuangLab/storm-control/blob/master/steve/qtRegexFileDialog.py
class CSVFileSortFilterProxyModel(QSortFilterProxyModel):
def __init__(self,parent=None):
# print("initializing CSVFileSortFilterProxyModel")
super(CSVFileSortFilterProxyModel,self).__init__(parent)
# filterAcceptsRow - return True if row should be included in the model, False otherwise
#
# do not list files named *_fleetsync.csv or *_clueLog.csv
# do a case-insensitive comparison just in case
def filterAcceptsRow(self,source_row,source_parent):
# print("CSV filterAcceptsRow called")
source_model=self.sourceModel()
index0=source_model.index(source_row,0,source_parent)
# Always show directories
if source_model.isDir(index0):
return True
# filter files
filename=source_model.fileName(index0).lower()
# print("testing lowercased filename:"+filename)
# never show non- .csv files
if filename.count(".csv")<1:
return False
if filename.count("_fleetsync.csv")+filename.count("_cluelog.csv")==0:
return True
else:
return False
I'm a beginner with Kivy and I'm trying to create gui for a python app.
I have a backend programmed in python, working alone (keyboard as input) and now I want frontend with kivy. I have two questions:
- How can I run both components (back and front end) at once?
- How can I share objects of existing classes in my backend to display information in the frontend (kivy)?
classtest.py
class Test(object):
def __init__(self, attr):
self.attr = attr
gui.py
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text='User Name'))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password = TextInput(password=True, multiline=False)
self.add_widget(self.password)
print self.username.text
class Login(App):
def build(self):
Window.borderless = True
return LoginScreen()
main.py
import classtest, gui
users = ['user_name1', 'user_name2', 'user_name3']
gui.Login().run()
for u in users:
test = classtest.Test(u) # this should update the user text field on the windows login automatically, but how?
In example, How can I update an element of the login window when an instance attribute value change?
Thanks many many times!
It won't update because... of a loop :P Kivy for each App().run() or a similar "run" command launches a loop and your:
for u in users:
test = classtest.Test(u)
is written after that loop. So basically it won't even execute while your app is running. Just put print('something') into that for loop and you'll see.
Example:
while True:
<do something>
<change a variable in that loop> # == nope
Which for you means that you need either:
write that into gui.py file or
set those things right in the classtest.py
The second option also depends when you use the class. If outside of the main loop, then you are in the same situation as right now, therefore - use that Test() inside of gui.py.
You won't be able to use any code after run() while the app is running. Maybe with some dirty trick, but that will bring you only troubles. The code you wrote could be used in some "cleaning", which you can also do in App.on_stop method(inside main loop).
I have a GUI designed in glade, using python/gtk in the background.I want to handle the delete event and display a "Are you sure?"-message dialog.I have been trying to handle the delete and destroy events, but failing to do so.any light?
#!/usr/bin/python
import .... stuff
class App:
def __init__(self):
self.gladefile = 'test.glade'
windowname = 'window'# This must match the window name in glade
self.wTree = gtk.glade.XML(self.gladefile, windowname)# object for acessing widgets
dic={
# Also need to set project2's signal tab
'on_window_delete_event':self.on_erro,
'on_window_destroy_event':self.on_erro,
}
self.wTree.signal_autoconnect (dic)
self.op=self.wTree.get_widget('window')
self.op.show()
def on_erro(self,widget,*args):
print 'hello'
app = App()
gtk.main()
This code opens a simple window .On clicking on close button, it prints hello and exits.(I want the window to remain open)
You have to return True in order to stop propagation of the delete event in the callback on_erro as mentioned in the documentation for "delete-event". In your current code, the callback is not returning any boolean value as required by the function, which I am guessing is returning False (Please check the signature for on_window_delete_event callback functions, the return type is boolean)
Hope this helps!
I am searching for a simple dialog with a text entry widget asking the user for some input. The dialog should be easy to run (like the gtk.MessageDialog variants) and as flexible.
There are of course some examples but they are either not flexible enough or too complicated to construct for my taste.
I hate re-inventing the wheel... or a dialog.
Based on an example I found (thanks Ardoris!), I came up with a dialog subclass... hope it helps someone out there!
#!/usr/bin/env python
import gtk
class EntryDialog(gtk.MessageDialog):
def __init__(self, *args, **kwargs):
'''
Creates a new EntryDialog. Takes all the arguments of the usual
MessageDialog constructor plus one optional named argument
"default_value" to specify the initial contents of the entry.
'''
if 'default_value' in kwargs:
default_value = kwargs['default_value']
del kwargs['default_value']
else:
default_value = ''
super(EntryDialog, self).__init__(*args, **kwargs)
entry = gtk.Entry()
entry.set_text(str(default_value))
entry.connect("activate",
lambda ent, dlg, resp: dlg.response(resp),
self, gtk.RESPONSE_OK)
self.vbox.pack_end(entry, True, True, 0)
self.vbox.show_all()
self.entry = entry
def set_value(self, text):
self.entry.set_text(text)
def run(self):
result = super(EntryDialog, self).run()
if result == gtk.RESPONSE_OK:
text = self.entry.get_text()
else:
text = None
return text
The run() method returns either the text entered in the entry box if the user presses <Enter> or clicks Ok. If Cancel is clicked or <Esc> pressed, the run() method returns None.
Except for that, the dialog should behave as any other gtk.MessageDialog instance.
Maybe that is not very general as it assumes you will always have Ok as an option, but that is what I need in 99% of my use cases anyway.
There isn't one available in GTK+. You've got two options:
Create a dialog, pack the Entry and any other content you need (probably the best way in my opinion)
Retrieve the content_area of the MessageDialog and append an Entry to it.
Something along the lines of:
#!/usr/bin/env python
import gtk
messagedialog = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK, message_format="Hello")
action_area = messagedialog.get_content_area()
entry = gtk.Entry()
action_area.pack_start(entry)
messagedialog.show_all()
messagedialog.run()
messagedialog.destroy()
Though it does probably need more refinement to get the Entry to display nicely.
Here's the function I wrote, based on the previous answers here. It's a function instead of a class, which means you can use it in one line.
def get_text(parent, message, default=''):
"""
Display a dialog with a text entry.
Returns the text, or None if canceled.
"""
d = gtk.MessageDialog(parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL,
message)
entry = gtk.Entry()
entry.set_text(default)
entry.show()
d.vbox.pack_end(entry)
entry.connect('activate', lambda _: d.response(gtk.RESPONSE_OK))
d.set_default_response(gtk.RESPONSE_OK)
r = d.run()
text = entry.get_text().decode('utf8')
d.destroy()
if r == gtk.RESPONSE_OK:
return text
else:
return None