Qt 5.12 TableView Header Delegate - qtquick2

I've created this table view in Qt 5.12 with new TableView but I don't know how to create header for it, I read documentation there is no headerdelegate for it neither. Here some screenshot:
so how can i add headerDelegate for 5.12 TableView?
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
ListModel{
id:tableModel
ListElement{
identifier:1
name:'value'
title: 'test'
}
ListElement{
identifier:2
name:'value2'
title: 'test2'
}
}
width:1000; height: 500
//
TableView {
id:trans
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
anchors.fill: parent
columnSpacing: 1
rowSpacing: 1
clip: true
model: tableModel
delegate: Rectangle {
Row{
Rectangle{
implicitWidth: 100
implicitHeight: 50
Text{
text: identifier
}
}
Rectangle{
implicitWidth: 100
implicitHeight: 50
Text{
text:name
}
}
Rectangle{
implicitWidth: 100
implicitHeight: 50
Text{
text:title
}
}
}
}
}
}
and TableViewColumn didn't work
Qc.TableViewColumn{
title: 'id'
role: 'identifier'
width:100
}
i'm trying to set its header from c++ but sth is not ok with my code
class TableHelper : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void setTableHeader(QObject & table){
// get QTableView and Set QHeaderView
qDebug()<< "here";
}
};
main :
TableHelper th;
engine.rootContext()->setContextProperty("tableHelper",&th);
qml :
Component.onCompleted: {
tableHelper.setTableHeader(trans)
}
I have a break point in C++ code, but it never runs.

Related

Angular 10 ag-grid cannot set property 'setRowData' because gridOptions.api undefined

I am using following versions.
Angular CLI: 10.0.1
Node: 12.18.2
OS: win32 x64
Angular: 10.0.2
#ag-grid-community/angular#23.2.1
#ag-grid-community/all-modules#23.2.1
I am using the following way:
import { Component, OnInit, ViewChild, Output } from '#angular/core';
import { SummaryDataService } from '../services/data/summary-data.service';
import {AgGridService} from '../services/common/ag-grid.service';
import { UtilService } from '../services/common/util.service';
import { GridOptions } from '#ag-grid-community/all-modules';
import { FilterComponent } from '../common/filter/filter.component';
#Component({
selector: 'app-summary',
templateUrl: './summary.component.html',
styleUrls: ['./summary.component.css']
})
export class SummaryComponent implements OnInit {
errorMessage: any = null;
noResults: boolean;
summaryData: Array<any>;
columnDefs: any;
defaultColWidth: number = 200;
numberColWidth: number = 120;
defaultColDef: any;
innerHeight: any;
gridOptions: GridOptions;
test: any;
currentDateTo: string;
currentDateFrom: string;
pendingRequest: any;
constructor(
private summaryService: SummaryDataService,
private agGridServ: AgGridService,
private util: UtilService,
) {
this.innerHeight = 450;
this.errorMessage = null;
}
ngOnInit(): void {
this.noResults = false;
this.gridOptions = <GridOptions> {
columnDefs: this.createColumnDefs(),
onGridReady: () => {
this.gridOptions.api.sizeColumnsToFit();
},
rowHeight: 48,
headerHeight: 48,
pivotMode: true,
enableSorting: true,
enableColResize: true,
enableFilter: true,
showToolPanel: true,
enableRangeSelection: true,
sortingOrder: ['asc', 'desc'],
suppressAggFuncInHeader: true,
suppressCopyRowsToClipboard: true,
filter: 'text'
}
}
private createColumnDefs() {
return [
{
headerName: "Name",
field: "name",
width: this.defaultColWidth,
sortable: true,
resizable: true,
filter: true
},
{
// Other columns definition
}
];
}
handleUserSelection(selection) {
this.getSummaryData();
}
getSummaryData(selections: any): void {
this.summaryData = []
this.errorMessage = null;
this.noResults = false;
this.summaryService.loadSummaryData()
.subscribe (
data => {
this.summaryData = data;
this.noResults = this.summaryData.length === 0;
if(!this.gridOptions.api){
return;
}
this.gridOptions.api.setRowData(this.summaryData);
},
error => {
this.errorMessage = <any>error;
}
);
}
}
When I see the Network tab on chrome deveoper tools, the result is returned fine from the service.
The issue is at:
if(!this.gridOptions.api){
return;
}
Basically, in ngOnInit(), I am initializing the gridOptions (GridOptions); but my data is to be fetched later (based on some checks and user input).
So when I am trying to use setRowData, it fails as this.gridOptions.api is undefined.
How can I solve this?
Try setting up the api after grid initialisation.
You have to use the method onGridReady(params). You can get gridApi from the params like params.api
You have to use it as following. When the grid finished creating, it raises an event that it is ready. The event executes a function assigned to that event as seeing in the following code. According to Grid Event documentation page, the AgGridEvent has two properties, api, and columnApi. So you can take this property through the raised event's parameter, like params.api. You can assign this to whatever you want. Api property is needed to create, update, retrieve rows from the grid.
─ AgGridEvent
│ api: GridAPI, // see Grid API
│ columnApi: ColumnAPI // see Column API
Codes
<!-- IN HTML PAGE -->
<ag-grid-angular
(gridReady)='onGridReady($event)'>
</ag-grid-angular>
// in TYPE SCRIPT File
onGridReady(params: any) {
this.gridApi = params.api;
}
Actually if you still has the issue, you should paste the code where you call handleUserSelection method.
BTW, you code has an explicit issue, which may cause this.gridOptions undefined:
this.summaryService.loadSummaryData()
.subscribe (...)
You forgot to unsubscribe the observer, which may cause the issue and memory leak.

Prevent multiple Ionic Alerts from stacking up

How can I detect if ionic 2 alert ui component instance is already open in order not to present another alert ?
I ended up writing a wrapping provider for Ionic's Alert controller like so :
import { Injectable } from '#angular/core';
import { AlertController } from 'ionic-angular';
#Injectable()
export class Alert {
public alertPresented: any;
constructor(public alertCtrl: AlertController) {
this.alertPresented = false
}
present(title, subTitle) {
let vm = this
if(!vm.alertPresented) {
vm.alertPresented = true
vm.alertCtrl.create({
title: title,
subTitle: subTitle,
buttons: [{
text: 'OK',
handler: () => {
vm.alertPresented = false
}
}],
}).present();
}
}
}
where alertPresented flag prevents more than one instance from being presented
I have another idea that you can assign message for a variable and check new message is equal to it or not. If equal, return.
This is my code and hope you enjoy with it.
import { Injectable } from '#angular/core';
import { AlertController } from 'ionic-angular';
#Injectable()
export class AlertProvider {
public showingMessage = ""
constructor(
private alertController: AlertController
) {}
showAlert(message) {
// Check this message is showing or not
if (message === this.showingMessage) {
return
}
this.showingMessage = message
this.alertController.create({
title: "APP_NAME",
message: message,
buttons: [{
text: "OK",
handler: () => {
this.showingMessage = ""
}
}]
}).present()
}
}
You can create an AlertService to handle that with more options without inject an event for the buttons
import { Injectable } from '#angular/core';
import { AlertController, Alert } from 'ionic-angular';
/**
* A simple alert class to show only one alert at the same time
*/
#Injectable()
export class AlertService {
currentAlert: Alert
constructor(private alertCtrl: AlertController) {
}
show(title, message, buttons: any = [], inputs: any = [], cssClass = '') {
if (!buttons.length) {
buttons.push('Ok')
}
let alertOptions: any = {
title: title,
subTitle: message,
buttons: buttons,
cssClass: buttons.length === 2 ? 'confirmAlert' : cssClass
}
if (inputs.length) {
alertOptions.inputs = inputs
}
if (!this.currentAlert) {
this.currentAlert = this.alertCtrl.create(alertOptions)
this.currentAlert.present()
this.currentAlert.onDidDismiss(() => {
this.currentAlert = null
})
}
return this.currentAlert
}
}
Regards, Nicholls
My solution worked, i had to put a boolean and set it true after a cancel event and set it false when an alert is presented
if (this.network_alert) {
let alert = await this.alertController.create({
header: "No Network",
message:
"Please check your internet connection",
buttons: [{
text: "Dismiss",
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
this.network_alert = true
}
}],
});
await alert.present();
this.network_alert = false
}
}

Access Imported QML Component ID from Imported JS

I'm trying to access an included QML id through an included JavaScript function. And I get the error:
Reference error: textItem is not defined.
main.qml
import QtQuick 2.1
import "functions.js" as Logic
Rectangle {
anchors.fill: parent;
Button { }
MouseArea {
onClicked: Logic.changeText()
}
}
Button.qml
import QtQuick 2.1
Rectangle {
width: 100; height: 30
color: "black"
Text {
id: textItem
anchors.centerIn: parent
font.pointSize: 20
color: "white"
text: "Hello!"
}
}
functions.js
function changeText() {
textItem.text = "Goodbye!";
}
Is there any way to access an Imported QML's id scope from an imported JS file?
As folibis said, textItem is not accessible to functions.js. There are more problems with your code, though. The button whose text you want to change has no ID, so you can't change its text even if you wanted to.
Give the button an ID, and then pass the button to changeText():
import QtQuick 2.1
import "functions.js" as Logic
Rectangle {
anchors.fill: parent;
Button {
id: button
}
MouseArea {
onClicked: Logic.changeText(button)
}
}
The next problem is that your Button type doesn't expose a text property. You should make an alias to the text property of textItem:
import QtQuick 2.1
Rectangle {
width: 100; height: 30
color: "black"
property alias text: textItem.text
Text {
id: textItem
anchors.centerIn: parent
font.pointSize: 20
color: "white"
text: "Hello!"
}
}
Then the rest will work:
function changeText(button) {
button.text = "Goodbye!";
}
In order to reference something from other files (or inside the QML file), you need to think in terms of the QML tree (navigating through the tree with id property). With that being said, I would suggest the following changes:
import QtQuick 2.1
import "functions.js" as Logic
Rectangle {
id: rootRec
anchors.fill: parent;
Button {id: myButton}
MouseArea {
onClicked: Logic.changeText(myButton)
}
}
In Button.qml:
import QtQuick 2.1
Rectangle {
id: rootRec
width: 100; height: 30
color: "black"
property string text: "Hello"
Text {
id: textItem
anchors.centerIn: parent
font.pointSize: 20
color: "white"
text: rootRec.text
}
}
In "function.js" you reference it like this:
function changeText(buttonId) {
buttonId.text = "Goodbye!"; }

BlackBerry10 cascades :cannot access control objects from QML in c++

In my project, I have two qml files viz. main.qml and DetailsPage.qml.
I am trying to change the text of a label of DetailsPage.qml from c++ using findChild() method.
This is the code in c++ file and DetailsPage.qml
Myfile.cpp code:
using namespace bb::cascades;
AbstractPane *root;
AbstractPane *root1;
Navigater::Navigater
(bb::cascades::Application *app):QObject(app)
{
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
QmlDocument *qml1 = QmlDocument::create("asset:///DetailsPage.qml").parent(this);
qml->setContextProperty("_app", this);
qml1->setContextProperty("_app", this);
root = qml->createRootObject<AbstractPane>();
root1 = qml1->createRootObject<AbstractPane>();
app->setScene(root);
}
void Navigater::tr1()
{
Label *tryLabel1 = root1->findChild<Label*>("labelObj");
if(tryLabel1)
{
qDebug()<<"tttt "<<tryLabel1->text(); //initial text
tryLabel1->setText("Hello!!!!!!!") ;
qDebug()<<"yyyy "<<tryLabel1->text(); //changedText gets reflected on DeviceLog but not on UI
}
else
{
qDebug()<<"Not Found";}
}
DetailsPage.qml code:
// Navigation pane project template
import
bb.cascades 1.0
Page
{
// page with a picture detail
id: pgDetail
actions: [
ActionItem {
title: qsTr("Break")
onTriggered: {
_app.tr1();
imgView.imageSource = "asset: //images/picture1br.png"
}
}
]
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
onTriggered: {
navigationPane.pop()
}
}
}
onCreationCompleted: {
_app.tr1();
}
Container {
background: Color.Black
Label {
objectName: "labelObj" // control to be found
text: "Page 2"
horizontalAlignment: HorizontalAlignment.Center
textStyle {
base: SystemDefaults.TextStyles.TitleText
color: Color.Yellow
}
}
ImageView {
id: imgView
imageSource: "asset:///images/picture1.png"
horizontalAlignment: HorizontalAlignment.Center
}
Label {
text: qsTr("Picture description")
horizontalAlignment: HorizontalAlignment.Center
}
}
}
Change I have made is not getting reflected in simulator...but visible on device log.
Is there any way to access control objects from multiple pages i.e pages other than main.qml?
Could you please look into it.
You can use a navigation pane, add main.qml as page contents and details page as attachedObjects in the navigation pane like
attachedObjects: [
ComponentDefinition {
id: secondPageDefinition
source: "DetailsPage.qml"
}
in main page action add
onClicked: {
// show detail page when the button is clicked
var page = secondPageDefinition.createObject();
navigationPane.push(page);
}
i think this will solve your issue and in the main.cpp page change the content as follows
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
qml->setContextProperty("_app", this);
root = qml->createRootObject<AbstractPane>();
app->setScene(root);
and
void Navigater::tr1()
{
Label *tryLabel1 = root ->findChild<Label*>("labelObj");
if(tryLabel1)
{
qDebug()<<"tttt "<<tryLabel1->text(); /////////////intial text
tryLabel1->setText("Hello!!!!!!!") ;
qDebug()<<"yyyy "<<tryLabel1->text(); ////////////changedText gets reflected on DeviceLog but not on UI
}
else
{
qDebug()<<"Not Found";}
}
you can navigate in qml easily without using c++
1.page1.qml
Page {
id: rootPage
Container {
background: Color.LightGray
layout: DockLayout {
}
Label {
text: "First page"
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
}
}
actions: [
ActionItem {
title: "Next page"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
var page = pageDefinition.createObject();
navigationPane.push(page);
}
attachedObjects: ComponentDefinition {
id: pageDefinition
source: "PageTwo.qml"
}
}
]
}
onPopTransitionEnded: {
page.destroy();
}

How to get element class attribute with ExtJS 4.1.1 in a cross browser way?

I just noticed that Ext.get('myElement').getAttribute('class') doesn't work with IE8-. Does anyone has an elegant alternative (ExtJS 4.1.1 native or patch prefered)? Maybe this feature is hidden somewhere in the framework?
Edit
Here is the context. I have temporarily fixed the issue like this :
action.getAttribute('class') || action.dom.className
View (grid) :
{
xtype: 'actioncolumn',
items: [{
icon: '/images/edit.png',
iconCls: 'action-edit'
}, {
icon: '/images/delete.png',
iconCls: 'action-delete'
}]
}
Controller :
init: function () {
this.control({
'grid actioncolumn': {
click: function (a, b, c, d, ev, record) {
var action = Ext.get(ev.target);
if (action.is('img')) {
action = action.getAttribute('class') || action.dom.className;
action = action.match(/action-(.+)/)[1];
this[action + 'Action'](record);
}
}
}
});
},
editAction: function (record) {
// ...
},
deleteAction: function (record) {
// ...
}
Drop # from the node id and it'll probably work.
UPDATE: I'm still not sure what exactly you're trying to do. Ext.get returns Ext.dom.Element object, which wraps native element and provides a certain level of abstraction from DOM but still is pretty much low-level tool. Since older IE doesn't support 'class' attribute you'll have to call getAttribute('className') to get the result.
Or you can resort to using standard convenience methods like hasCls and getStyle for checking particular classes and styles on an element. To find elements with particular class there's query method. All in all I fail to see why exactly you would need to get the list of all classes defined on an element.
UPDATE 2: OK, now I get what you need. Your code is way too complex, it can be simplified like this:
CSS:
.action-edit, .action-delete {
width: 16px; /* Or whatever fits your bill */
height: 16px;
}
.action-edit {
background-image: url('/images/edit.png');
}
.action-delete {
background-image: url('/images/delete.png');
}
View:
{
xtype: 'actioncolumn',
items: [{
iconCls: 'action-edit',
action: 'edit'
}, {
iconCls: 'action-delete',
action: 'delete'
}]
}
Controller:
init: function() {
this.control({
'grid actioncolumn': {
click: function(a, b, c, d, ev, record) {
var target = Ext.get(ev.target);
if (target && target.action) {
this[target.action + 'Action'].call(this, record);
}
}
}
});
}
Or it can be done in more officially supported way:
View:
{
xtype: 'actioncolumn',
items: [{
iconCls: 'action-edit',
handler: function(grid, rowIndex) {
this.fireEvent('editAction', grid.getStore().getAt(rowIndex));
}
}, {
iconCls: 'action-delete',
handler: function(grid, rowIndex) {
this.fireEvent('deleteAction', grid.getStore().getAt(rowIndex));
}
}];
}
Controller:
init: function() {
this.control({
'grid actioncolumn': {
editAction: this.editAction,
deleteAction: this.deleteAction
}
});
}
Obviously the view event handling code can be further abstracted, but the main idea here is that you shouldn't be afraid of hiding view complexities Controller has no need to know about.