Giter Club home page Giter Club logo

qml-coding-guide's Introduction

Introduction

The content that you see here is mostly based on my experience working with QML in a large project with a diverse team of developers and designers. I update the document as my opinions about certain things become validated by real life experience.

You may not agree with some of the ideas laid out here, in those cases please create an issue to discuss and update accordingly. I'll keep updating this guide as I learn new things. Contributions are vital to this document because it needs to reflect tried and validated ideas of more people to make sense in a general sense. It's likely that I may not have done a good job at explaining a concept. I would appreciate any contributions to improve it.

Please don't hesitate to raise issues and submit PRs. Even the tiniest contribution matters.

Table of Contents

Code Style

This section provides details about how to format the order of properties, signals, and functions to make things easy on the eyes and quickly switch to related code block.

QML object attributes are always structured in the following order:

  • id
  • Property declarations
  • Signal declarations
  • Property initializations
  • Attached properties and signal handlers
  • States
  • Transitions
  • Signal handlers
  • Child objects
    • Visual Items
    • Qt provided non-visual items
    • Custom non-visual items
  • QtObject for encapsulating private members1
  • JavaScript functions

The main purpose for this order is to make sure that the most intrinsic properties of a type is always the most visible one in order to make the interface easier to digest at a first glance. Although it could be argued that the JavaScript functions are also part of the interface, the ideal is to have no functions at all.

CS-1: Signal Handler Ordering

When handling the signals attached to an Item, make sure to always leave Component.onCompleted to the last line.

// Wrong
Item {
    Component.onCompleted: {
    }
    onSomethingHappened: {
    }
}

// Correct
Item {
    onSomethingHappened: {
    }
    Component.onCompleted: {
    }
}

This is because it mentally makes for a better picture because Component.onCompleted is expected to be fired when the components construction is complete.


If there are multiple signal handlers in an Item, then the ones with least amount of lines may be placed at the top. As the implementation lines increases, the handler also moves down. The only exception to this is Component.onCompleted signal, it is always placed at the bottom.

// Wrong
Item {
    onOtherEvent: {
        // Line 1
        // Line 2
        // Line 3
        // Line 4
    }
    onSomethingHappened: {
        // Line 1
        // Line 2
    }
}

// Correct
Item {
    onSomethingHappened: {
        // Line 1
        // Line 2
    }
    onOtherEvent: {
        // Line 1
        // Line 2
        // Line 3
        // Line 4
    }
}

CS-2: Property Initialization Order

The first property assignment must always be the id of the component. If you want to declare custom properties for a component, the declarations are always above the first property assignment.

// Wrong
Item {
    someProperty: false
    property int otherProperty: -1
    id: myItem
}

// Correct
Item {
    id: myItem

    property int otherProperty: -1

    someProperty: false
}

There's also a bit of predefined order for property assignments. The order goes as follows:

  • id
  • x
  • y
  • width
  • height
  • anchors

The goal here is to put the most obvious and defining properties at the top for easy access and visibility. For example, for an Image you may decide to also put sourceSize above anchors.


If there are also property assignments along with signal handlers, make sure to always put property assignments above the signal handlers.

// Wrong
Item {
    onOtherEvent: {
    }
    someProperty: true
    onSomethingHappened: {
    }
    x: 23
    y: 32
}

// Correct
Item {
    x: 23
    y: 32
    someProperty: true
    onOtherEvent: {
    }
    onSomethingHappened: {
    }
}

It is usually harder to see the property assignments If they are mixed with signal handlers. That's why we are putting the assignments above the signal handlers.

CS-3: Function Ordering

Although there are no private and public functions in QML, you can provide a similar mechanism by wrapping the properties and functions that are only supposed to be used internally in QtObject .

Public function implementations are always put at the very bottom of the file. Even though we prioritize putting the public declarations at the top of the file for other types, I encourage you to put the public functions at the bottom because if the number of lines get larger for a function, it significantly reduces the readability of the QML document. Ideally, you shouldn't have any functions at all and strive to rely on declarative properties of your component as much as possible.

// Wrong
Item {

    function someFunction() {
    }

    someProperty: true
}

// Correct
Item {
    someProperty: true
    onOtherEvent: {
    }
    onSomethingHappened: {
    }

    function someFunction() {
    }
}

CS-4: Animations

When using any subclass of Animation, especially nested ones like SequentialAnimation, try to reduce the number of properties in one line. More than 2-3 assignments on the same line becomes harder to reason with after a while. Or maybe you can keep the one line assignments to whatever line length convention you have set up for your project.

Since animations are harder to imagine in your mind, you will benefit from keeping the animations as simple as possible.

// Bad
NumberAnimation { target: root; property: "opacity"; duration: root.animationDuration; from: 0; to: 1 }

// Depends on your convention. The line does not exceed 80 characters.
PropertyAction { target: root; property: "visible"; value: true }

// Good.
SequentialAnimation {
    PropertyAction {
        target: root
        property: "visible"
        value: true
    }

    NumberAnimation {
        target: root
        property: "opacity"
        duration: root.animationDuration
        from: 0
        to: 1
    }
}

CS-5: Specifying IDs for Objects

If an object does not need to be accessed for a functionality, avoid setting the id property. This way you'll be less likely to run into duplicate id problem. Also, having an id for an object puts additional cognitive stress because it now means that there's additional relationships that we need to care for.

If you want to mark the type with a descriptor but you don't intend to reference the type, you can use objectName instead or just plain old comments.

Make sure that the top most component in the file always has root as its id. Qt will make unqualified name look up deprecated in QML 3, so it's better to start giving IDs to your components now and use qualified look up.

See QTBUG-71578 and QTBUG-76016 for more details on this.

CS-6: Property Assignments

When assigning grouped properties, always prefer the dot notation If you are only altering just one property. Otherwise, always use the group notation.

Image {
    anchors.left: parent.left // Dot notation
    sourceSize { // Group notation
        width: 32
        height: 32
    }
}

When you are assigning the component to a Loader's sourceComponent in different places in the same file, consider using the same implementation. For example, in the following example there are two instances of the same component. If both of those SomeSpecialComponent are meant to be identical it is a better idea to wrap SomeSpecialComponent in a Component.

// BEGIN bad.
Loader {
    id: loaderOne
    sourceComponent: SomeSpecialComponent {
        text: "Some Component"
    }
}

Loader {
    id: loaderTwo
    sourceComponent: SomeSpecialComponent {
        text: "Some Component"
    }
}
// END bad.

// BEGIN good.
Loader {
    id: loaderOne
    sourceComponent: specialComponent
}

Loader {
    id: loaderTwo
    sourceComponent: specialComponent
}

Component {
    id: specialComponent

    SomeSpecialComponent {
        text: "Some Component"
    }
}
// END good.

This ensures that whenever you make a change to specialComponent it will take effect in all of the Loaders. In the bad example, you would have to duplicate the same change.

When in a similar situation without the use of Loader, you can use inline components.

component SomeSpecialComponent: Rectangle {

}

CS-7: Import Statements

As a general rule, you should always prefer C++ over JavaScript to do heavy lifting. If there are cases where you justify having a separate JavaScript file, keep these in mind.

If you are importing a JavaScript file, make sure to not include the same module in both the QML file and the JavaScript file. JavaScript files share the imports from the QML file so you can take advantage of that. If the JavaScript file is meant as a library, this does not apply.

If you are not making use of the imported module in the QML file, consider moving the import statement to the JavaScript file. But note that once you import something in the JavaScript file, the imports will no longer be shared. For the complete rules see here.

Qt.include() is deprecated and should not be used.

As a general rule, you should avoid having unused import statements.

Import Order

When importing other modules, use the following order;

  • Qt modules
  • Third party modules
  • Local C++ module imports
  • QML folder imports

Full Example

// First Qt imports
import QtQuick 2.15
import QtQuick.Controls 2.15
// Then custom imports
import my.library 1.0

Item {
    id: root

    // ----- Property Declarations

    // Required properties should be at the top.
    required property int radius: 0

    property int radius: 0
    property color borderColor: "blue"

    // ----- Signal declarations

    signal clicked()
    signal doubleClicked()

    // ----- In this section, we group the size and position information together.

    x: 0
    y: 0
    z: 0
    width: 100
    height: 100
    anchors.top: parent.top // If a single assignment, dot notation can be used.
    // If the item is an image, sourceSize is also set here.
    // sourceSize: Qt.size(12, 12)

    // ----- Then comes the other properties. There's no predefined order to these.

    // Do not use empty lines to separate the assignments. Empty lines are reserved
    // for separating type declarations.
    enabled: true
    layer.enabled: true

    // ----- Then attached properties and attached signal handlers.

    Layout.fillWidth: true
    Drag.active: false
    Drag.onActiveChanged: {

    }

    // ----- States and transitions.

    states: [
        State {

        }
    ]
    transitions: [
        Transitions {

        }
    ]

    // ----- Signal handlers

    onWidthChanged: { // Always use curly braces.

    }
    // onCompleted and onDestruction signal handlers are always the last in
    // the order.
    Component.onCompleted: {

    }
    Component.onDestruction: {

    }

    // ----- Visual children.

    Rectangle {
        height: 50
        anchors: { // For multiple assignments, use group notation.
            top: parent.top
            left: parent.left
            right: parent.right
        }
        color: "red"
        layer: {
            enabled: true
            samples: 4
        }
    }

    Rectangle {
        width: parent.width
        height: 1
        color: "green"
    }

    // ----- Qt provided non-visual children

    Timer {

    }

    // ----- Custom non-visual children

    MyCustomNonVisualType {

    }

    QtObject {
        id: privates

        property int diameter: 0
    }

    // ----- JavaScript functions

    function collapse() {

    }

    function setCollapsed(value: bool) {
        if (value === true) {
        }
        else {
        }
    }
}

Bindings

Bindings are a powerful tool when used responsibly. Bindings are evaluated whenever a property it depends on changes and this may result in poor performance or unexpected behaviors. Even when the binding is simple, its consequence can be expensive. For instance, a binding can cause the position of an item to change and every other item that depends on the position of that item or is anchored to it will also update its position.

So consider the following rules when you are using bindings.

B-1: Prefer Bindings over Imperative Assignments

See the related section on Qt Documentation.

The official documentation explains things well, but it is also important to understand the performance complications of bindings and understand where the bottlenecks can be.

If you suspect that the performance issue you are having is related to excessive evaluations of bindings, then use the QML profiler to confirm your suspicion and then opt-in to use imperative option.

Refer to the official documentation on how to use QML profiler.

B-2: Making Connections

A Connections object is used to handle signals from arbitrary QObject derived classes in QML. One thing to keep in mind when using connections is the default value of target property of the Connections is its parent if not explicitly set to something else. If you are setting the target after dynamically creating a QML object, you might want to set the target to null otherwise you might get signals that are not meant to be handled.

Also note that using a Connections object will incur a slight performance/memory penalty since it's another allocation that has to be done. If you are concerned about this you can use QtObject.connect method, but be careful of the pitfalls of this solution.

// Bad
Item {
    id: root
    onSomethingHappened: {
        // Set the target of the Connections.
    }

    Connections {
        // Notice that target is not set so it's implicitly set to root.
        onWidthChanged: {
            // Do something. But since Item also has a width property we may
            // handle the change for root until the target is set explicitly.
        }
    }
}

// Good
Item {
    id: root
    onSomethingHappened: {
        // Set the target of the Connections.
    }

    Connections {
        target: null // Good. Now we won't have the same problem.
        onWidthChanged: {
            // Do something. Only handles the changes for the intended target.
        }
    }
}

B-3: Use Binding Object

Binding's when property can be used to enable or disable a binding expression depending on a condition. If the binding that you are using is complex and does not need to be executed every time a property changes, this is a good idea to reduce the binding execution count.

Using the same example above, we can rewrite it as follows using a Binding object.

Rectangle {
    id: root

    Binding on color {
        when: mouseArea.pressed
        value: mouseArea.pressed ? "red" : "yellow"
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent
    }
}

Again, this is a really simple example to get the point out. In a real life situation, you would not get more benefit from using Binding object in this case unless the binding expression is expensive (e.g It changes the item's anchor which causes a whole chain reaction and causes other items to be repositioned.).

B-4: KISS It

Removed. No longer applies. Keeping the record to not mess with item numbers.

Justification for Removal

The QML engine changed a lot since I first wrote this guide. While it is still a good idea to keep the bindings simple (ie Don't call any expensive functions in a binding), it'd be incorrect to suggest there would be certain optimizations. The advice about avoiding var properties still apply, and you should strive to use the most precise type possible.

Previous Content

You are probably already familiar with the KISS principle. QML supports optimization of binding expressions. Optimized bindings do not require a JavaScript environment hence it runs faster. The basic requirement for optimization of bindings is that the type information of every symbol accessed must be known at compile time.

So, avoid accessing var properties. You can see the full list of prerequisites of optimized bindings here.

B-5: Be Lazy

Removed. No longer applies. Keeping the record to not mess with item numbers.

Justification for Removal

Declarative is better than imperative in QML. This promotes an imperative approach, and doesn't provide a great value. If you are in need of disabling or enabling bindings, prefer Binding objects instead. Or use a boolean flag to enable a binding, e.g visible: privates.bindingEnabled ? root.count > 0 : false.

Previous Content

There may be cases where you don't need the binding immediately but when a certain condition is met. By lazily creating a binding, you can avoid unnecessary executions. To create a binding during runtime, you can use Qt.binding().

Item {
    property int edgePosition: 0

    Component.onCompleted: {
        if (checkForSomeCondition() == true) {
            // bind to the result of the binding expression passed to Qt.binding()
            edgePosition = Qt.binding(function() { return x + width })
        }
    }
}

You can also use Qt.callLater to reduce the redundant calls to a function.

B-6: Avoid Unnecessary Re-Evaluations

If you have a loop or process where you update the value of the property, you may want to use a temporary local variable where you accumulate those changes and only report the last value to the property. This way you can avoid triggering re-evaluation of binding expressions during the intermediate stages of accumulation.

Here's a bad example straight from Qt documentation:

import QtQuick 2.3

Item {
    id: root

    property int accumulatedValue: 0

    width: 200
    height: 200
    Component.onCompleted: {
        const someData = [ 1, 2, 3, 4, 5, 20 ];
        for (let i = 0; i < someData.length; ++i) {
            accumulatedValue = accumulatedValue + someData[i];
        }
    }

    Text {
        anchors.fill: parent
        text: root.accumulatedValue.toString()
        onTextChanged: console.log("text binding re-evaluated")
    }
}

And here is the proper way of doing it:

import QtQuick 2.3

Item {
    id: root

    property int accumulatedValue: 0

    width: 200
    height: 200
    Component.onCompleted: {
        const someData = [ 1, 2, 3, 4, 5, 20 ];
        let temp = accumulatedValue;
        for (let i = 0; i < someData.length; ++i) {
            temp = temp + someData[i];
        }

        accumulatedValue = temp;
    }

    Text {
        anchors.fill: parent
        text: root.accumulatedValue.toString()
        onTextChanged: console.log("text binding re-evaluated")
    }
}

Also note that list type doesn't have a change signal associated with adding/moving/removing elements from it. If you are using a list type to store sequential data, make sure that the places where this property is used does not do expensive things (e.g populating a view).

C++ Integration

QML can be extended with C++ by exposing the QObject classes using the Q_OBJECT macro or custom data types using Q_GADGET macro. It always should be preferred to use C++ to add functionality to a QML application. But it is important to know which is the best way to expose your C++ classes, and it depends on your use case.

CI-1: Avoid Context Properties

Context properties are registered using

rootContext()->setContextProperty("someProperty", QVariant());

Context properties always takes in a QVariant, which means that whenever you access the property it is re-evaluated because in between each access the property may be changed as setContextProperty() can be used at any moment in time.

Context properties are expensive to access, and hard to reason with. When you are writing QML code, you should strive to reduce the use of contextual variables (A variable that doesn't exist in the immediate scope, but the one above it.) and global state. Each QML document should be able to run with QML scene provided that the required properties are set.

See QTBUG-73064.

CI-2: Use Singleton for Common API Access

There are bound to be cases where you have to provide a single instance for a functionality or common data access. In this situation, resort to using a singleton as it will have a better performance and be easier to read. Singletons are also a good option to expose enums to QML.

class MySingletonClass : public QObject
{
public:
    static QObject *singletonProvider(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
    {
        if (m_Instance == nullptr) {
            m_Instance = new MySingletonClass(qmlEngine);
        }

        Q_UNUSED(jsEngine);
        return m_Instance;
    }
};

// In main.cpp
qmlRegisterSingletonType<SingletonTest>("MyNameSpace", 1, 0, "MySingletonClass",
                                        MySingletonClass::singletonProvider);

You should strive to not use singletons for shared data access. Reusable components are especially a bad place to access singletons. Ideally, all QML documents should rely on the customization through properties to change its content.

Let's imagine a scenario where we are creating a paint app where we can change the currently selected color on the palette. We only have one instance of the palette, and the data from this is accessed throughout our C++ code. So we decided that it makes sense to expose it as a singleton to QML side.

// ColorViewer.qml
Row {
    id: root

    Rectangle {
        color: Palette.selectedColor
    }

    Text {
        text: Palette.selectedColorName
    }
}

With this code, we bind our component to Palette singleton. Who ever wants to use our ColorViewer they won't be able to change it so they can show some other selected color.

// ColorViewer_2.qml
Row {
    id: root

    property alias selectedColor: colorIndicator.color
    property alias selectedColorName: colorLabel.color

    Rectangle {
        id: colorIndicator
        color: Palette.selectedColor
    }

    Text {
        id: colorLabel
        text: Palette.selectedColorName
    }
}

This would allow the users of this component to set the color and the name from outside, but we still have a dependency on the singleton.

// ColorViewer_3.qml
Row {
    id: root

    property alias selectedColor: colorIndicator.color
    property alias selectedColorName: colorLabel.color

    Rectangle {
        id: colorIndicator
    }

    Text {
        id: colorLabel
    }
}

This version allows you to de-couple from the singleton, enable it to be resuable in any context that wants to show a selected color, and you could easily run this through qmlscene and inspect its behavior.

CI-3: Prefer Instantiated Types Over Singletons For Data

Instantiated types are exposed to QML using:

// In main.cpp
qmlRegisterType<ColorModel>("MyNameSpace", 1, 0, "ColorModel");

Instantiated types have the benefit of having everything available to you to understand and digest in the same document. They are easier to change at run-time without creating side effects, and easy to reason with because when looking at a document, you don't need to worry about any global state but the state of the type that you are dealing with at hand.

// ColorsWindow.qml
Window {
    id: root

    Column {
        Repeater {
            model: Palette.selectedColors
            delegate: ColorViewer {
                required property color color
                required property string colorName

                selectedColor: color
                selectedColorName: colorName
            }
        }
    }
}

The code above is a perfectly valid QML code. We'll get our model from the singleton, and display it with the reusable component we created in CI-2. However, there's still a problem here. ColorsWindow is now bound to the model from Palette singleton. And If I wanted to have the user select two different sets of colors, I would need to create another file with the same contents and use that. Now we have 2 components doing basically the same thing. And those two components need to be maintained.

This also makes it hard to prototype. If I wanted to see two different versions of this window with different colors at the same time, I can't do it because I'm using a singleton. Or, If I wanted to pop up a new window that shows the users the variants of a color set, I can't do it because the data is bound to the singleton.

A better approach here is to either use an instantiated type or expect the model as a property.

// ColorsWindow.qml
Window {
    id: root

    property PaletteColorsModel model

    Column {
        Repeater {
            model: root.model
            // Alternatively
            model: PaletteColorsModel { }
            delegate: ColorViewer {
                required property color color
                required property string colorName

                selectedColor: color
                selectedColorName: colorName
            }
        }
    }
}

Now, I can have the same window up at the same time with different color sets because they are not bound to a singleton. During prototyping, I can provide a dummy data easily by adding PaletteColorElement types to the model, or by requesting test dataset with something like:

PaletteColorsModel {
    testData: "prototype_1"
}

This test data could be auto-generated, or it could be provided by a JSON file. The beauty is that I'm no longer bound to a singleton, that I have the freedom to instantiate as many of these windows as I want.

There may be cases where you actually truly want the data to be the same every where. In these cases, you should still provide an instantiated type instead of a singleton. You can still access the same resource in the C++ implementation of your model and provide that to QML. And you would still retain the freedom of making your data easily pluggable in different context and it would increase the re-usability of your code.

class PaletteColorsModel
{
    explicit PaletteColorsModel(QObject* parent = nullptr)
    {
        initializeModel(MyColorPaletteSingleton::instance().selectedColors());
    }
};

CI-4: Watch Out for Object Ownership Rules

When you are exposing data to QML from C++, you are likely to pass around custom data types as well. It is important to realize the implications of ownership when you are passing data to QML. Otherwise you might end up scratching your head trying to figure out why your app crashes.

If you are exposing custom data type, prefer to set the parent of that data to the C++ class that transmits it to QML. This way, when the C++ class gets destroyed the custom data type also gets destroyed and you won't have to worry about releasing memory manually.

There might also be cases where you expose data from a singleton class without a parent and the data gets destroyed because QML object that receives it will take ownership and destroy it. And you will end up accessing data that doesn't exist. Ownership is not transferred as the result of a property access. For data ownership rules see here.

To learn more about the real life implications of this read this blog post.

Performance and Memory

Most applications are not likely to have memory limitations. But in case you are working on a memory limited hardware or you just really care about memory allocations, follow these steps to reduce your memory usage.

PM-1: Reduce the Number of Implicit Types

If a type defines custom properties, that type becomes an implicit type to the JS engine and additional type information has to be stored.

Rectangle { } // Explicit type because it doesn't contain any custom properties

Rectangle {
    // The deceleration of this property makes this Rectangle an implicit type.
    property int meaningOfLife: 42
}

You should follow the advice from the official documentation and split the type into its own component If it's used in more than one place. But sometimes, that might not make sense for your case. If you are using a lot of custom properties in your QML file, consider wrapping the custom properties of types in a QtObject. Obviously, JS engine will still need to allocate memory for those types, but you already gain the memory efficiency by avoiding the implicit types. Additionally, wrapping the properties in a QtObject uses less memory than scattering those properties to different types.

Consider the following example:

Window {
    Rectangle { id: r1 } // Explicit type. Memory 64b, 1 allocation.

    // Implicit type. Memory 128b, 3 allocations.
    Rectangle { id: r2; property string nameTwo: "" }

    QtObject { // Implicit type. Memory 128b, 3 allocations.
        id: privates
        property string name: ""
    }
}

In this example, the introduction of a custom property to added additional 64b of memory and 2 more allocations. Along with privates, memory usage adds up to 256b. The total memory usage is 320b.

You can use the QML profiler to see the allocations and memory usage for each type. If we change that example to the following, you'll see that both memory usage and number of allocations are reduced.

Window {
    Rectangle { id: r1 } // Explicit type. Memory 64b, 1 allocation.

    Rectangle { id: r2 } // Explicit type. Memory 64b, 1 allocation.

    QtObject { // Implicit type. Memory 160b, 4 allocations.
        id: privates

        property string name: ""
        property string nameTwo: ""
    }
}

In the second example, total memory usage is 288b. This is really a minute difference in this context, but as the number of components increase in a project with memory constrained hardware, it can start to make a difference.

Signal Handling

Signals are a very powerful mechanism in Qt/QML. And the fact that you can connect to signals from C++ makes it even better. But in some situations, If you don't handle them correctly you might end up scratching your head.

SH-1: Try to Avoid Using connect Function in Models

You can have signals in the QML side, and the C++ side. Here's an example for both cases.

QML Example.

// MyButton.qml
import QtQuick.Controls 2.3

Button {
    id: root

    signal rightClicked()
}

C++ Example:

class MyButton
{
    Q_OBJECT

signals:
    void rightClicked();
};

The way you connect to signals is using the syntax

item.somethingChanged.connect(function() {})

When this method is used, you create a function that is connected to the somethingChanged signal.

Consider the following example:

// MyItem.qml
Item {
    id: root

    property QtObject customObject

    objectName: "my_item_is_alive"
    onCustomObjectChanged: {
        customObject.somethingChanged.connect(() => {
            console.log(root.objectName)
        })
    }
}

This is a perfectly legal code. And it would most likely work in most scenarios. But, if the life time of the customObject is not managed in MyItem, meaning if the customObject can keep on living when the MyItem instance is destroyed, you run into problems.

The connection is created in the context of MyItem, and the function naturally has access to its enclosing context. So, as long as we have the instance of MyItem, whenever somethingChanged is emitted we'd get a log saying my_item_is_alive.

Here's a quote directly from Qt documentation:

Delegates are instantiated as needed and may be destroyed at any time. They are parented to ListView's contentItem, not to the view itself. State should never be stored in a delegate.

So you might be making use of an external object to store state. But what If MyItem is used in a ListView, and it went out of view and it was destroyed by ListView?

Let's examine what happens with a more concrete example.

ApplicationWindow {
    id: root

    property list<QtObject> myObjects: [
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        },
        QtObject {
            signal somethingHappened()
        }
    ]

    width: 640
    height: 480

    ListView {
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
            bottom: btn.top
        }
        // Low enough we can resize the window to destroy buttons.
        cacheBuffer: 1
        model: root.myObjects.length
        delegate: Button {
            id: self

            readonly property string name: "Button #" + index

            text: "Button " + index
            onClicked: {
                root.myObjects[index].somethingHappened()
            }
            Component.onCompleted: {
                root.myObjects[index].somethingHappened.connect(() => {
                    // When the button is destroyed, this will cause the following
                    // error: TypeError: Type error
                    console.log(self.name)
                })
            }
            Component.onDestruction: {
                console.log("Destroyed #", index)
            }
        }
    }

    Button {
        id: btn
        anchors {
            bottom: parent.bottom
            horizontalCenter: parent.horizontalCenter
        }
        text: "Emit Last Signal"
        onClicked: {
            root.myObjects[root.myObjects.length - 1].somethingHappened()
        }
    }
}

In this example, once one of the buttons is destroyed we still have the object instance. And then object instance still contains the connection we made in Component.onCompleted. So, when we click on btn, we get an error: TypeError: Type error. But once we expand the window so that the button is created again, we don't get that error. That is, we don't get that error for the newly created button. But the previous connection still exists and still causes error. But now that a new one is created, we end up with two connections on the same object.

This is obviously not ideal and should be avoided. But how do you do it?

The simplest and most elegant solution (That I have found) is to simply use a Connections object and handle the signal there. So, If we change the code to this:

delegate: Button {
    id: self

    readonly property string name: "Button #" + index

    text: "Button " + index
    onClicked: {
        root.myObjects[index].somethingHappened()
    }

    Connections {
        target: root.myObjects[index]
        onSomethingHappened: {
            console.log(self.name)
        }
    }
}

Now, whenever the delegate is destroyed so is the connection. This method can be used even for multiple objects. You can simply put the Connections in a Component and use createObject to instantiate it for a specific object.

Item {
    id: root
    onObjectAdded: {
        cmp.createObject(root, {"target": newObject})
    }

    Component {
        id: cmp

        Connections {
            target: root.myObjects[index]
            onSomethingHappened: {
                console.log(self.name)
            }
        }
    }
}

SH-2: When to use Functions and Signals

When coming from imperative programming, it might be very tempting to use signals very similar to functions. Resist this temptation. Especially when communicating between the C++ layer of your application, misusing signals can be very confusing down the line.

Let's first clearly define what a signal should be doing. Here's how Qt defines it.

Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner.

This means that whatever happens in the signal handler is a reaction to an internal state change of an object. The signal handler should not be changing something else in the same object.

See the following example. We have a ColorPicker component that we want to use to show the user a message when the color is picked. As far as component design goes, the fact that the customer sees a message is not ColorPicker's job. Its job is to present a dialog and change the color it represents.

// ColorPicker.qml
Rectangle {
    id: root

    signal colorPicked()

    ColorDialog {
        onColorChanged: {
            root.color = color
            root.colorPicked()
        }
    }
}

// main.qml
Window {
    ColorPicker {
        onColorPicked: {
            label.text = "Color Changed"
        }
    }

    Label {
        id: label
    }
}

The above example is pretty straightforward, the signal handler only reacts to a change and does something with that information after which the ColorPicker object is not affected.

// ColorPicker.qml
Rectangle {
    id: root

    signal colorPicked(color pickedColor)

    ColorDialog {
        onColorChanged: {
            root.colorPicked(color)
        }
    }
}

// main.qml
Window {
    ColorPicker {
        onColorPicked: {
            color = pickedColor
            label.text = "Color Changed"
        }
    }

    Label {
        id: label
    }
}

In this example, the signal handler not only reacts to an internal state but it also changes it. This is a very simple example, and it'll be easy to spot an error. However complex your application is, you will always benefit from making the distinction clear. Otherwise what you think to be a function at first glance might end up being a signal and it loses its semantics of an internal state change.

Here's a general principle to follow:

  1. When communicating up, use signals.
  2. When communicating down, use functions.

Communicating with C++ Using Signals

When you have a model that you use in the QML side, it's very possible that you are going to run into cases where something that happens in the QML side needs to trigger an action in the C++ side.

In these cases, prefer not to invoke any C++ signals from QML side. Instead, use a function call or better a property assignment. The C++ object then should make the decision whether to fire a signal or not.

If you are using a C++ type instantiated in QML, the same rules apply. You should not be emitting signals from QML side.

JavaScript

It is the prevalent advice that you should avoid using JavaScript as much as possible in your QML code and have the C++ side handle all the logic. This is a sound advice and should be followed, but there are cases where you can't avoid having JavaScript code for your UI. In those cases, follow these guidelines to ensure a good use of JavaScript in QML.

JS-1: Use Arrow Functions

Arrow functions were introduced in ES6. Its syntax is pretty close to C++ lambdas and they have a pretty neat feature that makes them most comfortable to use when you are using the connect() function to create a binding. If there's no block within the arrow function, it has an implicit return statement.

Let's compare the arrow function version with the old way.

Item {
    property int value: -1

    Component.onCompelted: {
        // Arrow function
        root.value = Qt.binding(() => root.someOtherValue)
        // The old way.
        root.value = Qt.binding(function() { return root.someOtherValue })
    }
}

The arrow function version is easier on the eyes and cleaner to write. For more information about arrow functions, head over to the MDN Blog

JS-2: Use the Modern Way of Declaring Variables

With ES6, there are 3 ways of delcaring a variable: var, let, and const.

You should leverage let and const in your codebase and avoid using var. let and const enables a scope based naming wheras var only knows about one scope.

Item {
    onClicked: {
        const value = 32;
        let valueTwo = 42;
        {
            // Valid assignment since we are in a different scope.
            const value = 32;
            let valueTwo = 42;
        }
    }
}

Much like in C++, prefer using const If you don't want the variable to be assigned. But keep in mind that const variables in JavaScript are not immutable. It just means they can't be reassigned, but their contents can be changed.

const value = 32;
value = 42; // ERROR!

const obj = {value: 32};
obj.value = 42; // Valid.

See the MDN posts on const and let

States and Transitions

States and transitions are a powerful way to create dynamic UIs. Here are some things to keep in mind when you are using them in your projects.

ST-1: Don't Define Top Level States

Defining states at the top-level of a reusable component can cause breakages if the user of your components also define their own states for their specific use case.

// MyButton.qml
Rectangle {
    id: root

    property alias text: lb.text
    property alias hovered: ma.containsMouse

    color: "red"
    states: [
        State {
            when: ma.containsMouse

            PropertyChanges {
                target: root
                color: "yellow"
            }
        }
    ]

    MouseArea {
        id: ma
        anchors.fill: parent
        hoverEnabled: true
    }

    Label {
        id: lb
        anchors.centerIn: parent
    }
}

// MyItem.qml
Item {
    MyButton {
        id: btn
        text: "Not Hovering"
        // The states of the original component are not actually overwritten.
        // The new state is added to the existing states.
        states: [
            State {
                when: btn.hovered

                PropertyChanges {
                    target: btn
                    text: "Hovering"
                }
            }
        ]
    }
}

When you assign a new value to states or any other QQmlListProperty, the new value does not overwrite the existing one but adds to it. In the example above, the new state is added to the existing list of states that we already have in MyButton.qml. Since we can only have one active state in an item, our hover state will be messed up.

In order to avoid this problem, create your top-level state in a separate item or use a StateGroup.

Rectangle {
    id: root

    property alias text: lb.text
    property alias hovered: ma.containsMouse

    color: "red"

    MouseArea {
        id: ma
        anchors.fill: parent
        hoverEnabled: true
    }

    Label {
        id: lb
        anchors.centerIn: parent
    }

    // A State group or
    StateGroup {
        states: [
            State {
                when: ma.containsMouse

                PropertyChanges {
                    target: root
                    color: "yellow"
                }
            }
        ]
    }

    // another item
    Item {
        states: [
            State {
                when: ma.containsMouse

                PropertyChanges {
                    target: root
                    color: "yellow"
                }
            }
        ]
    }
}

With this change, the button will both change its color and text when the mouse is hovered above it.

Visual Items

Visual items are at the core of QML, anything that you see in the window (or don't see because of transparency) are visual items. Having a good understanding of the visual items, their relationship to each other, sizing, and positioning will help you create a more robust UI for your application.

VI-1: Distinguish Between Different Types of Sizes

When thinking about geometry, we think in terms of x, y, width and height. This defines where our items shows up in the scene and how big it is. x and y are pretty straightforward but we can't really say the same about the size information in QML.

There's 2 different types of size information that you get from various visual items:

  1. Explicit size: width, height
  2. Implicit size: implicitWidth, implicitHeight

A good understanding of these different types is important to building a reusable library of components.

Explicit Size

It's in the name. This is the size that you explicitly assign to an Item. By default, Items do not have an explicit size and its size will always be Qt.size(0, 0).

// No explicit size is set. You won't see this in your window.
Rectangle {
    color: "red"
}

// Explicit size is set. You'll see a yellow rectangle.
Rectangle {
    width: 100
    height: 100
    color: "yellow"
}

Implicit Size

Implicit size refers to the size that an Item occupies by default to display itself properly. This size is not set automatically for any Item. You, as a component designer, need to make a decision about this size and set it to your component.

The other thing to note is that Qt internally knows if it has an explicit size or not. So, when an explicit size is not set, it will use the implicit size.

// Even though there's no explicit size, it will have a size of Qt.size(100, 100)
Rectangle {
    implicitWidth: 100
    implicitHeight: 100
    color: "red"
}

Whenever you are building a reusable component, never set an explicit size within the component but instead choose to provide a sensible implicit size. This way, the user of your components can freely manipulate its size and when they need to return to a default size, they can always default to the implicit size so they don't have to store a different default size for the component. This feature is also very useful if you want to implement a resize-to-fit feature.

When a user is using your component, they may not bother to set a size for it.

CheckBox {
    text: "Check Me Out"
}

In the example above, the check box would only be visible If there was a sensible implicit size for it. This implicit size needs to take into account its visual components (the box, the label etc.) so that we can see the component properly. If this is not provided, it's difficult for the user of your component to set a proper size for it.

VI-2: Be Careful with a Transparent Rectangle

Rectangle should never be used with a transparent color except when you need to draw a border. This is especially true if you are using a Rectangle as part of a delegate that's supposed to be created in a batch.

Drawing transparent/translucent content takes more time because translucency requires blending. Opaque content is optimized better by the renderer.

In order to avoid paying the penalty, look for ways that you can defer the use of a transparent Rectangle. Maybe you can show it on hover, or during certain events and set it to invisible when it's no longer needed. Alternatively, you can put the Rectangles in an asynchronous Loader.

Here's a sample QML code to demonstrate the difference between using an opaque rectangle and a transparent one when it comes to the creation time of these components.

Window {
    visible: true

    Row {
        Button {
            text: "Rect"
            onClicked: {
                console.time("Rect")
                rprect.model = rprect.model + 10000
                console.timeEnd("Rect")
            }
        }

        Button {
            text: "Transparent"
            onClicked: {
                console.time("Transparent")
                rptrans.model = rptrans.model + 10000
                console.timeEnd("Transparent")
            }
        }

        Button {
            text: "Transparent Loader"
            onClicked: {
                console.time("Transparent Loader")
                rploader.model = rploader.model + 10000
                console.timeEnd("Transparent Loader")
            }
        }

        Button {
            text: "Translucent"
            onClicked: {
                console.time("Translucent")
                rptransl.model = rptransl.model + 10000
                console.timeEnd("Translucent")
            }
        }

        Button {
            text: "Reset"
            onClicked: {
                rprect.model = 0
                rptrans.model = 0
                rptransl.model = 0
                rploader.model = 0
            }
        }
    }

    Repeater {
        id: rptrans
        model: 0
        delegate: Rectangle {
            width: 10
            height: 10
            color: "transparent"
        }
    }

    Repeater {
        id: rptransl
        model: 0
        delegate: Rectangle {
            width: 10
            height: 10
            opacity: 0.5
            color: "red"
        }
    }

    Repeater {
        id: rprect
        model: 0
        delegate: Rectangle {
            width: 10
            height: 10
            color: "red"
        }
    }

    Repeater {
        id: rploader
        model: 0
        // This will speed things up. You can defer the creation of the rectangle to when it makes
        // sense and since it's asynchronous it won't block the UI thread.
        delegate: Loader {
            asynchronous: true
            sourceComponent: Rectangle {
                width: 10
                height: 10
                opacity: 0.5
                color: "transparent"
            }
        }
    }
}

When you run this example for the first time and create solid rectangles, you'll notice that the creation is pretty fast. If you close it and run it again, but this time create transparent or translucent ones you'll see that the time reported does not actually differ that much from the solid rectangle.

The real problem starts presenting itself when you are creating new transparent items when there's already rectangles on the scene. Try creating first the solid ones and then the transparent ones. You'll see that the time difference is very noticeable.

Please note that this will not matter that much when you are drawing a few rectangles here and there. The problem will present itself when you are using translucency in the context of a delegate because there can potentially be creating thousands of these rectangles.

See also: Translucent vs Opaque

qml-coding-guide's People

Contributors

adsk-furkanzmc avatar cybrilla-rajaravivarma avatar furkanzmc avatar jbache avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qml-coding-guide's Issues

Transient Bindings break existing bindings

Thanks for your work.
But in reality the "transient binding" described here https://github.com/Furkanzmc/QML-Coding-Guide#transient-bindings
looks like broke existing binding.
In code bellow expect that after destroying temp the previous binding cfg_flag1 -> flag1 will work,
but actually it doesn't work right after "transient binding" creation it stops work.
I tried with Qt 5.15.1

import QtQuick 2.0
import QtQuick.Controls 2.15

Rectangle {
    id: rect
    width: 100
    height: 100
    color: "red"

    property bool flag1: {
        console.log("set flag1 to", cfg_flag1);
        return cfg_flag1;
    }
    property bool cfg_flag1: true

    Text {
        anchors.centerIn: parent
        text: "flag1: " + flag1.toString() + ", cfg_flag1 " + cfg_flag1.toString()
    }

    Timer {
        id: timer
        interval: 1000
        repeat: false
        onTriggered: {
            console.log("timer trigger");
            cfg_flag1 = false;
        }
    }

    Button {
        anchors.top: parent.top
        text: "button 1"
        onClicked: {
            console.log("buggon1 cliecked");
            let temp = cmpBinding.createObject(rect, {
                "target": rect,
                "property": "flag1",
                "value": true,
                "restoreMode": Binding.RestoreBinding,
            });
            temp.Component.onDestruction.connect(function() { console.log("destroyed"); });
            temp.destroy();
            console.log("end of clicked");
            timer.start();
        }
    }

    Component {
        id: cmpBinding

        Binding {
        }

    }
}

Question about having "root" as id for top-most component

I'm wondering what is the reasoning behind this guideline:

Make sure that the top most component in the file always has root as its id.

Why not have each file have an id that describes them?

// SomeComponent.qml
Item
{
  id: someComponent
}

As opposed to the generic root?

C++ Integration

Hello. One point that I see missing from both the official Qt documentation and this guide is distinguishing C++ model types and C++ visual types. I think most users use the visual types provided by QtQuick or Qt Design Studio instead of creating their own visual components. And they use C++ for model / data backend. So it is important to emphasize what is the most natural integration type for this use case.

Usually data needs to be persistent and not be created / destroyed when you navigate between different views. Furthermore you sometimes need to initialize the object in some specific way. For example pass a handle to a data base or to an IPC (inter-process communication) object to it. This means that the only integration method that works in this situation is using qmlRegisterSingletonInstance. (I don't think lifetime concerns is a problem since these days everyone is familiar with unique_ptr, whereas if you need to initialize your object in some way or pass some specific arguments to it, having the QML engine create it is either impossible or very hard with no benefits). In summary, if you really want your model to be independent of your view, you want to also make the lifetime independent.

It seems to me that the guideline should be presented in this way:

  • If your C++ type is a model then use qmlRegisterSingletonInstance and keep your instance as a unique_ptr or parent it to the engine. If you want to duplicate your data for each view that uses the model, or if your model does not need to be persistent between views then use qmlRegisterType
  • If your C++ type is a custom visual type then use qmlRegisterType

The reason I think this should be emphasized is that all Qt documentation emphasize the integration methods that create the object from QML. This is not practical for more than 90% of models in my experience due to the above limitations and leads to wasted time trying to fit a square peg in a round hole.

What is QML?

Adding a link to QML documentation from main readme would be helpful for those that don't know what it is but stumbled upon the project.

Linter / formatter rules

Nice guide!

Any chances to make it into a linter / formatter rules, maybe for qmllint?

Enforcing some of options, especially imports ordering, would be extremely good to have ON by default.

Qt 6 Update

The guide here is very thorough! However there are a few places where the recommendations might be improved for updating in line with Qt6. But thanks for the awesome work here and sharing with everyone!

Some issues

I understand that coding conventions can be quite subjective (and that a lot of these conventions came from Qt's official docs), but regardless, here are some observations I made while reading this, based on my experience as a developer of and with Qt who writes a lot of QML.

Most of it looks good to me, so I'm just pointing out the stuff that stuck out.

Property Ordering

https://github.com/Furkanzmc/QML-Coding-Guide#property-ordering says:

If you want to declare custom properties for a component, the declarations are always above the first property assignment.

I understand that this is from https://doc.qt.io/qt-5/qml-codingconventions.html#qml-object-declarations, but I think our own conventions are wrong (or outdated) here. The order that I use is:

  • id first
  • bindings to properties, starting with the oldest ancestor base type and ending with the closest base type
  • property declarations
  • signal handlers
  • attached properties
  • function declarations
  • large property assignments (like model and delegate)
  • child items

Personally I find it very jarring to declare new properties before existing ones are assigned to. :D

Function Ordering

https://github.com/Furkanzmc/QML-Coding-Guide#function-ordering says:

Public function implementations are always put at the very bottom of the file.

"very bottom of the file" is perhaps a bit too far. Personally I would say after attached properties, as above.

Animations

https://github.com/Furkanzmc/QML-Coding-Guide#animations says:

When using any subclass of Animation, especially nested ones like SequentialAnimation, try to keep the Animation objects to one line for readability. You will benefit from keeping the animations as simple as possible since they are executed every frame, and also find it easier to see the execution of the animation in your head.

This is not a good argument for bunching up code onto one line. I know there are people (Qt developers, too) who will disagree with me on this, but I think any more than 2 property bindings on one line is too much. I think it makes it less readable, actually.

A code comment says:

Good. This is easier to read as your eyes find it easy to detect things horizontally rather than vertically.

I wholeheartedly disagree. :D

Giving Components ids

https://github.com/Furkanzmc/QML-Coding-Guide#giving-components-ids says:

If a component does not need to be accessed for a functionality, avoid setting the id property. This way you don't clutter the namespace with unused ids and you'll be less likely to run into duplicate id problem.

In one particular case this is very good advice: if you assign an id to a delegate in a Control (Qt Quick Controls 2), it will prevent deferred execution of that item, which is bad, for performance reasons. However, it's probably not worth mentioning here, and I plan to document it in the Qt Quick Controls 2 docs soon.

I would also add that ids can be useful for identifying what an item is/does, though I would strongly encourage setting an objectName in that case, as it's very useful for debugging and testing.

It is a good idea to use max 3-4 character abbreviation for the ids so that when you are looking for a certain component, say a TextBox, it will be easier to list the IDs of all the text boxes by just typing tb.

Sorry, but is not something I would consider a best practice. Names should be descriptive, and if that means making them long, so be it. This becomes so important when you have to maintain an existing codebase; if a variable is not well-named, it makes everything just that little bit harder. A good IDE solves the problem of typing with auto-completion.

Make sure that the top most component in the file always has root as its id.

This is something I can get behind though. It is one less thing that you have to think about when writing QML code, and I imagine it will become even more relevant when the QML engine devs start making scope lookup more strict:

Property Assignments

https://github.com/Furkanzmc/QML-Coding-Guide#property-assignments says:

When assigning grouped properties, always prefer the dot notation If you are only altering just one property. Otherwise, always use the group notation.

Personally I've never bothered with this. If there's a performance reason for it, it would be worth mentioning as justification.

Import Statements

https://github.com/Furkanzmc/QML-Coding-Guide#import-statements says:

Imports take time in QML. And If you are developing for a device with low system specifications, then you will want to optimize as much as possible. In that case, try to minimize the number of imports you use in your QML file.

I don't like this advice, because it conflicts with the idea of keeping QML files as small and self-contained as possible. If users follow this advice, they will end up with gigantic QML files. As for the performance side of it, I have no idea if it's true or not, but that's something Qt should aim to fix if it's an issue, rather than advocate for less imports. :)

https://github.com/Furkanzmc/QML-Coding-Guide#import-order says:

When importing other modules, use the following order;

  • Qt modules
  • Third party modules
  • Local C++ module imports
  • QML folder imports

Agreed! Using a similar order to C++ is what I do.

Item 2: Bindings

https://github.com/Furkanzmc/QML-Coding-Guide#item-2-bindings says:

Bindings are evaluated whenever a property it depends on changes and this may result in poor performance or unexpected behaviors.

Technically true, but a bit fear-mongerish in my opinion. :)

Even when the binding is simple, its consequence can be expensive. For instance, a binding can cause the position of an item to change and every other item that depends on the position of that item or is anchored to it will also update its position.

True, but also not a huge deal in most cases.

So consider the following rules when you are using bindings.

Reduce the Number of Bindings

The premise here is wrong, I think. Trying to improve performance by replacing declarative bindings with imperative assignments is backwards, and will likely result in slower code for all but the most extreme cases (i.e. expensive bindings). We have tools like the QML profiler to address bottlenecks in applications, so we should instead recommend that that be used if the user suspects their bindings are slow. When they've confirmed the binding is getting re-evaluated too often, and they've verified that there's nothing that can be improved in any relevant C++ code, then perhaps they could use profile-guided optimisation to replace bindings with assignments.

See also: https://doc-snapshots.qt.io/qt5-5.13/qtquick-bestpractices.html#prefer-declarative-bindings-over-imperative-assignments

WIP

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.