Programmatically Generate Click-Python Options - python-click

Using click is there a way to programmatically prompt a user for an input.
For some use cases, I have been able to create a custom class to make certain options required and prompt the user for input (or more accurately make an existing option no longer required and remove a prompt) inspired from this post.
For use cases where I need an unknown number of the same type of input, I could code a large number of options and then remove those that are not needed. But, a more elegant solution would be to programmatically generate options from a custom class from a single argument or option. Any suggestions would be appreciated.

This is what I came up with to replicate the desired behavior.
In this use case, the user has already downloaded a file with some number of unsigned messages (n_messages). The user then signs the messages via a separate process. And, then uploads the signed messages along with a file that contains the unsigned messages using the #click.command signed.
Here is the #click.command:
#main.command()
# click.argument('file', type=click.File('r'), required=True)
#click.option('-sig', 'signatures', type=click.File('r'), multiple=True, required=True, cls=VerifySigNumbers)
def signed(*args, **kwargs):
create_signed_tx(*args, **kwargs)
Here is the custom class:
class VerifySigNumbers(click.Option):
def __init__(self, *args, **kwargs):
super(VerifySigNumbers, self).__init__(*args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
pig_file = opts.get('file')
data = read_pig_file(file)
n_messages = data.get('n_messages')
signatures = opts.get('signatures')
n_sigs = 0 if signatures is None else len(signatures)
if n_sigs != n_messages:
raise click.UsageError(
f'{pig_file} requires {n_tx_inputs} signatures, {n_sigs} provided', ctx)
return super(VerifySigNumbers, self).handle_parse_result(ctx, opts, args)
I would like the users to be prompt for the unsigned messages, but unsure how to implement it. Additionally, I ran into an issue uploading files via a prompt.

Related

How to do 2-way data binding using Python+PyGObject's GObject.bind_property function?

The background to this question (and my overall goal) is to structure a Python GTK application in a nice way. I am trying to bind widget properties to model properties using GTK's bidirectional data bindings.
My expectation is that the bidirectional binding should keep two properties in sync. I find instead that changes propagate in one direction only, even though I am using the GObject.BindingFlags.BIDIRECTIONAL flag. I created the following minimal example and the failing test case test_widget_syncs_to_model to illustrate the problem. Note that in a more realistic example, the model object could be an instance of Gtk.Application and the widget object could be an instance of Gtk.Entry.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
import unittest
class Obj(GObject.Object):
"""A very simple GObject with a `txt` property."""
name = "default"
txt = GObject.Property(type=str, default="default")
def __init__(self, name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = name
self.connect("notify", self.log)
def log(self, source, parameter_name):
print(
f"The '{self.name}' object received a notify event, "
f"its txt now is '{self.txt}'."
)
class TestBindings(unittest.TestCase):
def setUp(self):
"""Sets up a bidirectional binding between a model and a widget"""
print(f"\n\n{self.id()}")
self.model = Obj("model")
self.widget = Obj("widget")
self.model.bind_property(
"txt", self.widget, "txt", flags=GObject.BindingFlags.BIDIRECTIONAL
)
#unittest.skip("suceeds")
def test_properties_are_found(self):
"""Verifies that the `txt` properties are correctly set up."""
for obj in [self.model, self.widget]:
self.assertIsNotNone(obj.find_property("txt"))
#unittest.skip("suceeds")
def test_model_syncs_to_widget(self, data="hello"):
"""Verifies that model changes propagate to the widget"""
self.model.txt = data
self.assertEqual(self.widget.txt, data)
def test_widget_syncs_to_model(self, data="world"):
"""Verifies that widget changes propagate back into the model"""
self.widget.txt = data
self.assertEqual(self.widget.txt, data) # SUCCEEDS
self.assertEqual(self.model.txt, data) # FAILS
if __name__ == "__main__":
unittest.main()
The above program outputs:
ssF
======================================================================
FAIL: test_widget_syncs_to_model (__main__.TestBindings)
Verifies that widget changes propagate back into the model
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jh/.config/JetBrains/PyCharmCE2021.1/scratches/scratch_14.py", line 52, in test_widget_syncs_to_model
self.assertEqual(self.model.txt, data) # FAILS
AssertionError: 'default' != 'world'
- default
+ world
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1, skipped=2)
__main__.TestBindings.test_widget_syncs_to_model
The 'widget' object received a notify event, its txt now is 'world'.
Process finished with exit code 1
My specific question is, how can I get the bidirectional data bindings to work?... I would be glad if someone could fix my example or provide another working example.
In a broader sense, are bidirectional bindings the way to go for syncing UI state and model state in a well-structured Python GTK application? What is the intended and well-supported way to do this? thanks!
I got an answer over at the gnome discourse thread about bidirectional property bindings in python.
To make it even more clear, the following code does not work because flags are not passed correctly:
# broken, flags are passed incorrectly as keywords argument:
self.model.bind_property("txt", self.widget, "txt", flags=GObject.BindingFlags.BIDIRECTIONAL)
Instead, flags must be passed as follows:
# functioning, flags are passed correctly as a positional argument:
self.model.bind_property("txt", self.widget, "txt", GObject.BindingFlags.BIDIRECTIONAL)
More example code: Proper use of bidirectional bindings is for example demonstrated in pygobject’s source code in the test_bidirectional_binding test case.

Python w/QT Creator form - Possible to grab multiple values?

I'm surprised to not find a previous question about this, but I did give an honest try before posting.
I've created a ui with Qt Creator which contains quite a few QtWidgets of type QLineEdit, QTextEdit, and QCheckbox. I've used pyuic5 to convert to a .py file for use in a small python app. I've successfully got the form connected and working, but this is my first time using python with forms.
I'm searching to see if there is a built-in function or object that would allow me to pull the ObjectNames and Values of all widgets contained within the GUI form and store them in a dictionary with associated keys:values, because I need to send off the information for post-processing.
I guess something like this would work manually:
...
dict = []
dict['checkboxName1'] = self.checkboxName1.isChecked()
dict['checkboxName2'] = self.checkboxName2.isChecked()
dict['checkboxName3'] = self.checkboxName3.isChecked()
dict['checkboxName4'] = self.checkboxName4.isChecked()
dict['lineEditName1'] = self.lineEditName1.text()
... and on and on
But is there a way to grab all the objects and loop through them, even if each different type (i.e. checkboxes, lineedits, etc) needs to be done separately?
I hope I've explained that clearly.
Thank you.
Finally got it working. Couldn't find a python specific example anywhere, so through trial and error this worked perfectly. I'm including the entire working code of a .py file that can generate a list of all QCheckBox objectNames on a properly referenced form.
I named my form main_form.ui from within Qt Creator. I then converted it into a .py file with pyuic5
pyuic5 main_form.ui -o main_form.py
This is the contents of a sandbox.py file:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import main_form
# the name of my Qt Creator .ui form converted to main_form.py with pyuic5
# pyuic5 original_form_name_in_creator.ui -o main_form.py
class MainApp(QtWidgets.QMainWindow, main_form.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
# Push button object on main_form named btn_test
self.btn_test.clicked.connect(self.runTest)
def runTest(self):
# I believe this creates a List of all QCheckBox objects on entire UI page
c = self.findChildren(QtWidgets.QCheckBox)
# This is just to show how to access objectName property as an example
for box in c:
print(box.objectName())
def main():
app = QtWidgets.QApplication(sys.argv) # A new instance of QApplication
form = MainApp() # We set the form to be our ExampleApp (design)
form.show() # Show the form
app.exec_() # and execute the app
if __name__ == '__main__': # if we're running file directly and not importing it
main() # run the main function
See QObject::findChildren()
In C++ the template argument would allow one to specify which type of widget to retrieve, e.g. to just retrieve the QLineEdit objects, but I don't know if or how that is mapped into Python.
Might need to retrieve all types and then switch handling while iterating over the resulting list.

displaying a different line of code in an eclipse editor (without changing everything else)

Setting: I am doing a programming experiment where I need to display a different method signature (one line of code) to my test persons than the line of code actually exists. Example:
// this code should be visible
public def aFunction(def a, def b) {
// this line of code should be used
public void aFunction(String a, List<String> b) {
The intention is that everything works like when the source code included the second line (like code completion, errors etc.), but only the first line is visible.
I've already tried patching the groovy editor I use and replacing some text on load and on save, but that just does not seem to do the job, using some code like
IDocument doc =this.getDocumentProvider().getDocument(this.getEditorInput());
doc.set(doc.get().replaceAll(...));
Sadly this leads to strange behavior like always marked as dirty files.
I've also tried using the getCompilationUnit method that the groovy editor supplies, but somehow this does not help in any ways (maybe because the "wrong" code was still visible in the editor?).
Finally I tried to wrap the InputStream for the IFile underlying the IEditorInput in the doSetInput-method like
IFile resource = (IFile) input.getAdapter(IFile.class);
InputStream in = resource.getContents();
//...wrap stream
resource.setContents(in, false, true, null);
but this only leads to the editor being completely empty.
Anyone got an idea on how to solve that problem?

Managing Resources with FRP in Scala RX

I'm using scala rx for an application. I have a reactive variable holding a File (which is a PDF file). I'm using a library to render pages from this pdf file to the screen. Now the PDF library I'm using gives me an object (let's call it Doc), which I can use to render single pages. But in order to render a page from a Doc object, the Doc object must be opened (thus the resource must be acquired).
Right now I'm loading the pdf file for each page I'm rendering anew (creating a new Doc object and closing it after rendering the single page). This makes the rendering of the page functional (given a file and a page number, return an image).
Is there a way to cling to an opened resource and close it on change in FRP in general, and for scala rx in particular? How would one handle this very common situation?
You can simply enclose the Doc object. So instead of render being
def render(file: File, pageNumber: Int): Image = // blah blah blah
change it to:
def open(file: File): (Int => Image) = {
val doc = // call your library to read file
(x: Int) => doc.getPage(x)
}
and then pass the function open returns to whatever page change signal you're reacting to.
Edit: Oh, I see, so you're saying you want it to close the file whenever the file:File signal changes to be a different file. In that case you should be able do something like this:
def pageGetterRx(file: Rx[File]): Rx[Int => Image] = {
val doc: Var[Doc] = Var(null)
val o = file.foreach { f =>
Option(doc()).foreach(_.close)
doc() = PdfLib.read(f) // or however you do the reading
}
Rx {
(x: Int) => doc().getPage(x)
}
}
Edit 2: To clarify, if you impose a "assemble network of functions phase / run the network on some signal(s) phase" distinction on FRP, the above function would be called only once; in the assemble phase. To say it another way, pageGetterRx (a lousy name, I'm fully aware) doesn't participate in a FR way, instead it returns a signal of lambdas that each close over one particular file and return pages from it.

What is timeout_id in GTK for?

I'm reading through the official PyGObject tutorial, and I found this (unexplained) line in one of the examples:
self.timeout_id = None
(it was within an __init__ function of a Gtk.Window-descendant class; the whole listing is here). I couldn't google it; what is it for?
You didn't see it being set and used further down in on_pulse_toggled ?
It is assigned the return value of GObject.timeout_add, which adds a function to be called at a later interval, possibly repeatedly (like in this case):
self.timeout_id = GObject.timeout_add(100, self.do_pulse, None)
When you want this timeout not be called anymore, you have to remove it, and to do so, you need the id of the timeout you created:
GObject.source_remove(self.timeout_id)