Avoiding global variables in Genie - genie

Below is a working code of a ToolbarButton in Genie. The objective is to get the uri for the chosen file and return it back to the construct/init of the class. The problem is that in all examples I’ve come across global _variables are used (as shown in the code below). It looks unintuitive and I fear that whenever the code gets larger, it will become more difficult to remove bugs, since these variables will start to accumulate. Is any other way of making the function openfile return the uri to a regular variable within the construct/init of the class?
Here is the code:
uses
Granite.Widgets
Gtk
init
Gtk.init (ref args)
var app = new Application ()
app.show_all ()
Gtk.main ()
// This class holds all the elements from the GUI
class Application : Gtk.Window
_view:Gtk.TextView
_uri:string
construct ()
// Prepare Gtk.Window:
this.window_position = Gtk.WindowPosition.CENTER
this.destroy.connect (Gtk.main_quit)
this.set_default_size (400, 400)
// Headerbar definition
headerbar:Gtk.HeaderBar = new Gtk.HeaderBar()
headerbar.show_close_button = true
headerbar.set_title("My text editor")
// Headerbar buttons
open_button:Gtk.ToolButton = new ToolButton.from_stock(Stock.OPEN)
open_button.clicked.connect (openfile)
// Add everything to the toolbar
headerbar.pack_start (open_button)
show_all ()
this.set_titlebar(headerbar)
// Box:
box:Gtk.Box = new Gtk.Box (Gtk.Orientation.VERTICAL, 1)
this.add (box)
// A ScrolledWindow:
scrolled:Gtk.ScrolledWindow = new Gtk.ScrolledWindow (null, null)
box.pack_start (scrolled, true, true, 0)
// The TextView:
_view = new Gtk.TextView ()
_view.set_wrap_mode (Gtk.WrapMode.WORD)
_view.buffer.text = "Lorem Ipsum"
scrolled.add (_view)
def openfile (self:ToolButton)
var dialog = new FileChooserDialog ("Open file",
this,
FileChooserAction.OPEN,
Stock.OK, ResponseType.ACCEPT,
Stock.CANCEL, ResponseType.CANCEL)
//filter.add_pixbuf_formats ()
//dialog.add_filter (filter)
case dialog.run()
when ResponseType.ACCEPT
var filename = dialog.get_filename()
//image.set_from_file(filename)
if (dialog.run () == Gtk.ResponseType.ACCEPT)
_uri = dialog.get_uri ()
stdout.printf ("Selection:\n %s", _uri)
dialog.destroy ()
Or shouldn't I worry at all about _variables accumulating?

First a note on terminology and then a generalisation.
A "global variable" can be accessed any where in your program, so its scope is global. The _variables you are referring to in your question are private fields within the scope of your object. They can only be accessed by code that is defined in that object. You are, however, right to be concerned about the accumulation of private working variables within your objects.
Designing objects is hard to do and techniques and ideas have evolved over several decades of practise and research. The SOLID acronym, introduced by Michael Feathers, sums up five principles for object oriented design that provide useful criteria for evaluating your design. Also the book, Design Patterns: Elements of Reusable Object-Oriented Software, by Gamma et al. and first published in 1994, provides a good summary and categorisation of designs in object oriented programming. That book uses a document editor as a case study for demonstrating the use of such patterns. Both the SOLID principles and the design patterns in the book are abstractions, they won't tell you how to write a program but they do give a set of common ideas that allows programmers to discuss and evaluate. So I will use both of those tools in my answer, but be aware in recent years additional techniques have been developed to further enhance the software development process, specifically test driven development and behaviour driven development.
The S in SOLID stands for the Single Responsibility Principle and is a good starting point for looking at your example. By calling your object, Application, and thinking of the private working variables as global variables then it suggests you are writing the whole application within a single object. What you can do is start to separate Application in to a number of different objects that focus more on a single area of responsibility. First though I thought I would rename the Application object. I went for EditorWindow. In my example below EditorWindow also has a Header and a DocumentView.
Compile the code below with:
valac -X -DGETTEXT_PACKAGE --pkg gtk+-3.0 text_editor_example.gs
The use of -X -DGETTEXT_PACKAGE is explained at the end of this answer.
[indent=4]
uses
Gtk
init
Intl.setlocale()
Gtk.init( ref args )
var document = new Text( "Lorem Ipsum" )
var header = new Header( "My text editor" )
var body = new DocumentView( document )
var editor = new EditorWindow( header, body )
var document_selector = new DocumentFileSelector( editor )
var load_new_content_command = new Load( document, document_selector )
header.add_item( new OpenButton( load_new_content_command ) )
editor.show_all()
Gtk.main()
class EditorWindow:Window
construct( header:Header, body:DocumentView )
this.window_position = WindowPosition.CENTER
this.set_default_size( 400, 400 )
this.destroy.connect( Gtk.main_quit )
this.set_titlebar( header )
var box = new Box( Gtk.Orientation.VERTICAL, 1 )
box.pack_start( body, true, true, 0 )
this.add( box )
class Header:HeaderBar
construct( title:string = "" )
this.show_close_button = true
this.set_title( title )
def add_item( item:Widget )
this.pack_start( item )
class OpenButton:ToolButton
construct( command:Command )
this.icon_widget = new Image.from_icon_name(
"document-open",
IconSize.SMALL_TOOLBAR
)
this.clicked.connect( command.execute )
class DocumentView:ScrolledWindow
construct( document:TextBuffer )
var view = new TextView.with_buffer( document )
view.set_wrap_mode( Gtk.WrapMode.WORD )
this.add( view )
interface Command:Object
def abstract execute()
interface DocumentSelector:Object
def abstract select():bool
def abstract get_document():string
class Text:TextBuffer
construct ( initial:string = "" )
this.text = initial
class DocumentFileSelector:Object implements DocumentSelector
_parent:Window
_uri:string = ""
construct( parent:Window )
_parent = parent
def select():bool
var dialog = new FileChooserDialog( "Open file",
_parent,
FileChooserAction.OPEN,
dgettext( "gtk30", "_OK"),
ResponseType.ACCEPT,
dgettext( "gtk30", "_Cancel" ),
ResponseType.CANCEL
)
selected:bool = false
var response = dialog.run()
case response
when ResponseType.ACCEPT
_uri = dialog.get_uri()
selected = true
dialog.destroy()
return selected
def get_document():string
return "Reading the text from a URI is not implemented\n%s".printf(_uri)
class Load:Object implements Command
_receiver:TextBuffer
_document_selector:DocumentSelector
construct( receiver:TextBuffer, document_selector:DocumentSelector )
_receiver = receiver
_document_selector = document_selector
def execute()
if _document_selector.select()
_receiver.text = _document_selector.get_document()
A common high-level pattern for graphical user interfaces is model-view-controller (MVC). This is about de-coupling your objects so they can be easily re-used and changed. In the example document has become the object that represents the model. By making this a separate object it allows multiple views to be given of the same data. For example when writing a StackOverflow question you have an editor window, but also a pre-view. Both are different views of the same data.
In the example the header toolbar has been further separated into different objects using the command pattern. Each button in the toolbar has an associated command. By having the commands as separate objects the command can be re-used. For example the key binding Ctrl-O may also use the Load command. This way the code for the command attached to the open document button doesn't need to be re-written for attaching it to Ctrl-O.
The command pattern makes use of an interface. As long as an object implements the execute() method then it can be used as a command. The Load command also makes use of an interface for the object that asks the user which URI to open. Gtk+ also provides a FileChooserNative. So if you wanted to switch to using a FileChooserNative dialog instead of a FileChooserDialog you would just need to write a new object that implements the DocumentSelector interface and pass that to the Load command instead. By de-coupling objects in this way it makes your program much more flexible and the use of private fields are kept confined to each object.
As a side note, when compiling your example there were a few warnings: warning: Gtk.Stock has been deprecated since 3.10. The example in this answer uses the newer way:
for the open document icon the GNOME developer documentation for Stock Items states "Use named icon "document-open" or the label "_Open"." So I've used document-open. These names are from the freedesktop.org Icon Naming Specification
for the OK button in the file chooser dialog the GNOME Developer documentation states "Do not use an icon. Use label "_OK"." The underscore before means it is internationalised and translated by gettext. gettext uses 'domains' which are translation files. For GTK+3 the domain is called gtk30. To enable gettext when your program is compiled a macro for the default domain needs to be passed to the C compiler. This is why the -X -DGETTEXT_PACKAGE is needed. Also in the Genie program Intl.setlocale() is needed to set the locale to the runtime environment. When this is done using something like LC_ALL="zh_CN" ./text_editor_example to run your program will show the OK button in Chinese if you have that locale installed

Related

File reading in Kotlin without piggybacking off a button

I am coding a data class that is wanting to read a csv file to grab some information that is stored on the file. How ever, every way that I have tried to read the file will not work.
Here is what I have tried so far:
data class Bird(val birdNumIn: Int){
private var birdNum = birdNumIn
/**
* A function that searches the bird-data.csv file which is a list of birds to find the bird that was
* inputted into the class constructor and then add the values of the bird to the private variables.
*/
fun updateValues(){
var birdNumber = birdNum
var birdInfo: MutableList<String> = mutableListOf()
val minput = InputStreamReader(assets().open("bird-data.csv"), "UTF-8")
val reader = BufferedReader(minput)
}
How ever the assets().open() does not work. It returns an error of trying to open a file that does not exist, but the is in the assets folder, and the filename is spelt right.
I have tried many other methods on trying to read files, like using Java.io.File and using the path of the file.
If you would like to look at our whole project, please feel free to go to our github
What's the assets() function you're calling? This is just a bare data class, it has no connection to the Android environment it's running in, so unless you've injected an AssetManager instance (or a Context to pull it from) into your class, you can't access it.
You probably need to do this:
fun updateValues(context: Context){
val inputStream = context.assets.open("bird-data.csv")
val minput = InputStreamReader(inputStream, "UTF-8")
...
}
which requires your caller to have access to a Context.
Honestly from a quick look at your class, you might want to rework this. Instead of having a bunch of empty fields in your data class (which aren't part of the "data" by the way, only stuff in the constructor parameters is), and then having those updated later by the data class doing some IO, you might want to keep them as just basic stores of data, and create them when you read from your assets file.
So something like:
// all fixed values, initialised during construction
// Also you won't need to override toString now (unless you want to)
data class Bird(
val birdNum: Int
val nameOfBird: String
val birdFilePic: String
val birdFileSong: String
val alternativeName: String
val birdInfoFile: String
) { ... }
Then somewhere else
fun getBirbs(context: Context) {
// open CSV
// read the lines
val allBirds = lines.map {
// parse data for each bird, use it to construct a Bird object
}
}
or whatever you need to do, e.g. loading certain birds by ID.
That way your Bird class is just data and some functions/properties that work with it, it doesn't need a Context because it's not doing any I/O. Something else (that does have access to a Context) is responsible for loading your data and turning it into objects - deserialising it basically. And as soon as it's created, it's ready and initialised and immutable - you don't have to call update on it to get it actually initialised.
And if you ever wanted to do that a different way (e.g. loading a file from the internet) the data class wouldn't need to change, just the thing that does the loading. You could even have different loading classes! One that loads local data, one that fetches from the internet. The point is the separation of concerns, so it's possible to do this kind of thing because that functionality isn't baked into a class that's really about something else.
Up to you but just a thought! Especially if passing the context in like I suggested is a problem - that's a sign your design might need tweaking

I want to make a dropdown container in my script

I want to create a dropdown container to organize my export variable. Is it possible to create a custom dropdown container in the script?
Like this:
This is another approach to do this. It also requires the script to be tool.
What we need for this approach as a common prefix for the variables you want to group. The advantage is that we don't need _get and _set:
tool
extends Node
var custom_position:Vector2
var custom_rotation_degrees:float
var custom_scale:Vector2
func _get_property_list():
return [
{
name = "Custom",
type = TYPE_NIL,
hint_string = "custom_",
usage = PROPERTY_USAGE_GROUP
},
{
name = "custom_position",
type = TYPE_VECTOR2
},
{
name = "custom_rotation_degrees",
type = TYPE_REAL
},
{
name = "custom_scale",
type = TYPE_VECTOR2
}
]
As you can see we define a category with a name that will appear in the Inspector panel, and the hint_string is the prefix we will use. It is important to put the category before the properties in the array.
See: Adding script categories
Addendum: Using PROPERTY_USAGE_CATEGORY will produce a named header, similar to the one that says "Node2D" on the picture on the question. Use PROPERTY_USAGE_GROUP to make a collapsible group.
Yes, you can do this, but (in my opinion) it is a bit ugly and clutters up your script. You need to mark your script as a tool script and override the _get, _set, and _get_property_list functions.
An example based on your screenshot (not 100% sure this works exactly as-is; I'm also basing it on a recent project where I have since removed it and somewhat reorganized the project/code/node because the slightly nicer UI wasn't worth the additional clutter in the script):
tool
extends Node2D
# Note that these are NOT exported
var actual_position: Vector2
var actual_rotation: float
var actual_scale: Vector2
# Function to enumerate the properties to list in the editor
# - Not actually directly/automatically backed by variables
# - Note the naming pattern - it is {group heading}/{variable}
func _get_property_list():
var props = []
props.append({name="transform/position", type=TYPE_VECTOR2})
props.append({name="transform/rotation deg", type=TYPE_FLOAT}) # might not exist; look at docs to determine appropriate type hints for your properties
props.append({name="transform/scale", type=TYPE_VECTOR2})
return props
# Now the get/set functions to map the names shown in the editor to actual script variables
# Property names as input here will match what is displayed in the editor (what is enumerated in _get_property_list); just get/set the appropriate actual variable based on that
func _get(property: String):
if property == "transform/position":
return actual_position
if property == "transform/rotation deg":
return actual_rotation
if property == "transform/scale":
return actual_scale
func _set(property: String, value):
if property == "transform/position":
actual_position = value
return true
if property == "transform/rotation deg":
actual_rotation = value
return true
if property == "transform/scale":
actual_scale = value
return true
# Not a supported property
return false
Note that this answer is based on Godot 3.4. I'm not sure if a simpler approach is (or will be) available in Godot 4.

Is there a way to change the text of checked/unchecked MCheckBox states?

How would I go about changing the default MCheckBox state text (currently I/0) to, for example, YES/NO or ON/OFF?
Mr. Daniel Kurka is the author for all the widget classes in MGWT. If the look & feel is not
fulfilling our requirement, We can edit those classes and rewrite them according to our requirement.Because they are open source. I done this on many classes like CellList,FormListEntry and MCheckBox. code for ON/OFF instead of I/O
public MyOwnCheckBox(CheckBoxCss css) {
this.css = css;
css.ensureInjected();
setElement(DOM.createDiv());
addStyleName(css.checkBox());
onDiv = DOM.createDiv();
onDiv.setClassName(css.on());
onDiv.setInnerText("ON");
getElement().appendChild(onDiv);
middleDiv = DOM.createDiv();
middleDiv.setClassName(css.middle());
Element middleContent = DOM.createDiv();
middleContent.setClassName(css.content());
middleDiv.appendChild(middleContent);
getElement().appendChild(middleDiv);
offDiv = DOM.createDiv();
offDiv.setClassName(css.off());
offDiv.setInnerText("OFF");
getElement().appendChild(offDiv);
addTouchHandler(new TouchHandlerImplementation());
setValue(true, false);
}
Write a new class like MyOwnCheckBox.just copy the code in MCheckBox and paste in your class MyOwnCheckBox, find and replace the MCheckBox with MyOwnCheckBox in the code(change constructor's name). do the following changes.
onDiv.setInnerText("ON");
offDiv.setInnerText("OFF");
and finally create object to MyOwnCheckBox rather MCheckBox, it'll shows MCheckBox with ON/OFF.
Right now there is no way to do that, but there is no real reasons that checkbox does not implement HasText other than we might need to update the css so that big text will not break the layout.
If you think mgwt should implement this go and vote for this issue: http://code.google.com/p/mgwt/issues/detail?id=171
Well, an easy way to accomplish the same thing, without creating a new class that mimics MCheckBox, is to do something like the code below:
CheckBoxCss css = MGWTStyle.getTheme().getMGWTClientBundle().getCheckBoxCss();
String offClass = css.off();
String onClass = css.on();
NodeList<Node> checkBoxElems;
mIsSingleSkuBox = new MCheckBox(css);
checkBoxElems = mIsSingleSkuBox.getElement().getChildNodes();
for( int i = 0; i < checkBoxElems.getLength(); i++ )
{
Element openElem = (Element) checkBoxElems.getItem(i);
String className = openElem.getClassName();
if( className.equals( offClass))
{
openElem.setInnerText("No" );
}
else if( className.equals( onClass))
{
openElem.setInnerText("Yes" );
}
}
It will probably have space problems with anything longer than 3 characters, but it works consistently with "Yes" and "No" for me.

CATextlayer with AttributedString in MonoTouch

I am trying to create a "label" with different styles on different words, kind of like described here.
The problem is - as far as I can see - the MonoTouch implementation of UATextLayer does not accept assigning an NSAttributedString to the String property since the String property has the type string.
Is this an error in the implementation or is there another way of doing this?
(Yes, I am aware I can add separate labels - but I would rather not when there is a better solution).
EDIT (in response to the answer from Miguel):
After changing to GetHandler and correcting to "void_objc_msgSend_IntPtr" instead of "void_objc_msgSend_IntPrt" the code in the answer compiles and runs, but it doesn't quite work anyway (I was a bit fast in marking it as the answer).
No errors are thrown, but the text doesn't show.
Code:
string _text="Example string";
if(_textLayer==null) {
_textLayer = new CATextLayer();
_textLayer.Frame = new RectangleF(50,698,774,50);
_textLayer.Wrapped=true;
_textLayer.ForegroundColor=UIColor.White.CGColor;
_textLayer.BackgroundColor=UIColor.Clear.CGColor;
Layer.AddSublayer(_textLayer);
}
//_textLayer.String=_text;
CTFont _font=new CTFont("MarkerFelt-Thin",48);
CTStringAttributes _attrs=new CTStringAttributes();
_attrs.Font=_font;
_attrs.ForegroundColor = UIColor.White.CGColor;
var nsa = new NSAttributedString(_text);
Messaging.void_objc_msgSend_IntPtr(
_textLayer.Handle,
Selector.GetHandle("string"),
nsa.Handle);
If I uncomment the _textLayer.String=_text I see the text (but without attributes of course), so the problem is not with the layer.
For now, you can try:
using MonoTouch.ObjCRuntime;
var caTextLayer = new CATextLayer ();
var nsa = new NSAttributedString ();
[..]
Messaging.void_objc_msgSend_IntPrt (
caTextLayer.Handle,
Selector.sel_registerName ("string"),
nsa.Handle);
Alternatively, can you download this preview of the upcoming version:
http://tirania.org/tmp/monotouch.dll
It implements a property AttributedString in CATextLayer that you can set.

Masking QLineEdit text

I am using PyQt4 QLineEdit widget to accept password. There is a setMasking property, but not following how to set the masking character.
editor = QLineEdit()
editor.setEchoMode(QLineEdit.Password)
There is no setMasking property for QLineEdit in either PyQt4 or Qt4. Are you talking about setInputMask()? If you are, this does not do what you seem to think it does. It sets the mask against which to validate the input.
To get the control to hide what is typed, use the setEchoMode() method, which will (should) display the standard password hiding character for the platform. From what I can see from the documentation, if you want a custom character to be displayed, you will need to derive a new class. In general however, this is a bad idea, since it goes against what users expect to see.
It's quite easy using Qt: you would need to define a new style and return new character from the styleHint method whenever QStyle::SH_LineEdit_PasswordCharacter constant is queried. Below is an example:
class LineEditStyle : public QProxyStyle
{
public:
LineEditStyle(QStyle *style = 0) : QProxyStyle(style) { }
int styleHint(StyleHint hint, const QStyleOption * option = 0,
const QWidget * widget = 0, QStyleHintReturn * returnData = 0 ) const
{
if (hint==QStyle::SH_LineEdit_PasswordCharacter)
return '%';
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
lineEdit->setEchoMode(QLineEdit::Password);
lineEdit->setStyle(new LineEditStyle(ui->lineEdit->style()));
now the problem is that pyqt doesn't seem to know anything about QProxyStyle; it seem to be not wrapped there, so you're stuck, unless you would want to wrap it yourself.
regards
As docs say http://doc-snapshot.qt-project.org/4.8/stylesheet-examples.html#customizing-qlineedit:
The password character of line edits that have QLineEdit::Password echo mode can be set using:
QLineEdit[echoMode="2"] {
lineedit-password-character: 9679;
}
In Qt Designer
Select the line edit, and in the Property Editor window, there will be a property echoMode which you can set to Password.
Using python code
In this case, Anti Earth's answer will work which is:
myLineEdit.setEchoMode(QLineEdit.Password)

Resources