I have both a UITextLabel and a UITextView occupying the same real estate, point-for-point in my view. I have an NSMutableArray allocated to populate the UITextLabel based on what the user taps in a table. I would also like to program the app to use the UITextView if and only if the user taps on one specific row. I think this can be accomplished with an if-else (if) statement, but I'm not sure. If that will work, I don't know what to put in the if part. So far, I have this:
if (qux == ??) {
fooTextView.text = textString;
} else {
fooTextLabel.text = textString;
}
I don't know what to set the if statement to. I tried to set it to
if (qux == the really long code containing the single array statement
I want displayed as a UITextView)
but that returned an error. Does anyone have a suggestion on how to get Xcode to recognize my single NSMutableArray table cell with the UITextView and not (also) with the UILabel? As of right now, the app builds but displays the text both in the UITextView, so it scrolls and does exactly what I want but also displays in the UILabel and is static and visible "underneath" the scrolling UITextView.
Also, I'm using an NSMutable Array with initWithObjectAndKeys. In this special case that is the only one of its kind in my UITableView, I use a special key that is only used by this particular part of the array. Is it possible to set it up so it's something regarding:
if (qux == *NSMutableArray with the special key*) {
fooTextView.text = textString;
} else {
fooTextLabel.text = textString;
}
And should I be using an if-else if statement or just an if-else statement?
Use the UITableView Delegate method didSelectRowAtIndexPath: you can get you row index from the NSIndexPath (indexPath.row). The you can use an if-then or a switch to determine which of your controls gets the data. Make sure you set your UITableView's delegate property to your file owner so the UITableView will call back to you controller.
HTH
Related
I'm looking for a way to detect whether a Qt widget's ToolTip is visible at the moment when a particular key combination is pressed. If it is, I want to copy the ToolTip's text to the clipboard.
Specifically, I have a QListView containing abbreviated strings, which is set up (via the Qt::ToolTipRole of the associated model) to show the full string of the appropriate list item when the mouse is hovered over it. The behaviour I'm looking for is that if the user presses CTRL-C (as detected by a QShortcut) while the tooltip is visible, then the tooltip text is copied to the clipboard.
My original idea was to use the children() method of the QListView widget to see if there was a tooltip preset among them:
// Inisde the slot connected to QShortcut::activated...
auto children = _ui -> myListView -> children();
QString selectionText;
for (const auto & child : children)
{
if (qobject_cast<QToolTip *>(child))
{
selectionText = qobject_cast<QToolTip *>(child) -> text();
break;
}
}
...but this failed because it turns out that QToolTip does not inherit from QObject.
I've also thought of screening for QEvent::QToolTip events in the ListView's main event handler, and while I could probably get this to work it seems excessively low-level; I'd need to use screen co-ordinates to determine which item in the list was being hovered over and look for the widget's timeout to check that the tooltip hadn't disappeared again by the time that the QShortcut was fired. I'd be disappointed if there weren't a simpler way.
Is there an obvious way forward that I've failed to see?
There are probably several possible solutions, but I am afraid none of them is simple. What I would do is to use the implementation detail that the tooltip actual widget is called QTipLabel. See https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qtooltip.cpp.html#QTipLabel and it inherits from QLabel so you can easily get the text from it.
I am afraid the following solution is just a savage hack. I have not tested it, but it should work.
I would override the data model for your view, specifically override method data() which would call the data() method of the original model class but cache the last value which was returned when this method is called with role == Qt::ToolTipRole.
Then you need to catch the shortcut you are interested in. After it is caught, you get all qApp->topLevelWidgets() https://doc.qt.io/qt-5/qapplication.html#topLevelWidgets` and go through them and check if any of them has class name equal to QTipLabel (use QMetaObject::className()) and is visible, i.e. isVisible() == true.
If you get this visible QTipLabel widget (you hold it via QWidget*), qobject_cast it to QLabel* (you cannot cast it to QTipLabel beause you do not have access to the definition of QTipLabel class because it is in private Qt source file) and get the text with QLabel::text(). If the text is the same as the text which you stored in step 1, then yes, this is the text you are looking for and you can copy it to clipboard or do whatever yo want with it.
Nasty, isn't it? But it is the simplest what I can think of.
PS: I believe that step 1 can be implemented also by catching QEvent::QToolTip for your view and then do some magic to get the text, but I think that overriding data() for model can be a bit easier.
PPS: One obvious drawback is that Qt can rename QTipLabel class in the future. But I would not be worry about it. That won't happen becaus ethey do not change QtWidgets module any more. And if it happens, then you just rename the class in your code. No problem.
PPPS: Another potential corner-case is that some other widget (whose tooltip you do NOT want to capture with that shortcut) actually has the same tooltip text as any of the items in your list view (which you DO want to capture). Then if you display tooltip for your list item, then you move your mouse over to that other widget and hover so that its tooltip gets shown (but you do NOT want to capture it) and then you press that shortcut... But I guess that in reality this will not be your case. I doubt there will be this unlikely clash of tooltips.
With thanks to #V.K., this is what worked:
auto candidates = qApp->topLevelWidgets();
QString selectionText;
for (const auto & candidate : candidates)
{
if (strcmp(candidate->metaObject()->className(), "QTipLabel") == 0)
{
QLabel * label = qobject_cast<QLabel *>(candidate);
if (label->isVisible())
{
selectionText = label -> text();
break;
}
}
}
if (!selectionText.isEmpty())
QGuiApplication::clipboard() -> setText(selectionText);
I have a table inside of appmaker that I've added checkboxes to like so:
I'd like to get a list of the emails from entires that the user checks. At this point I'm not even able to access the status of a single checkbox. This is the current code snippet I tried adding to a button:
console.log(widget.parent.parent.parent.children.Panel1.children.Table3Panel.children.Table3.children.Table3Body.children.Table3Row.children.UserSelectionCheckbox.value);
I get the error:
Cannot read property 'children' of undefined
at Home.Panel1.OuSelectPanel1.Button6.onClick:1:133
I was able to use the autofill to write this entire statement, why can't it find the child object? Is there any way to reference this list directly without going down the tree from the widget or the app root?
For this type of functionality the autofill (intellisense) will not work for you. You need to address the children differently when you try to get a collection of rows from a table. I would suggest code similar to this for your button onClick event:
var rows = widget.root.descendants.Table3Body.children._values;
var emails = [];
for (var i in rows) {
var value = rows[i].children.Checkbox1.value;
if (value) {
emails.push(rows[i].datasource.item.Email);
}
}
console.log(emails);
Again the auto complete code feature simply won't work after you choose the _values, which will return all immediate children of your table body, which is what you want.
based on the 1:133 it seems to be saying that Table3Row has no children. I wonder if using widget.root.descendants.Table3Row might be a different way of doing it address the particular table.
I am creating a small program that takes user input into a model and then shows that input in several views that take it through filters.
When the user clicks the button that accepts the input, the program updates the amount of cells in the views and then resizes those cells as necessary so that they fit neatly in their area.
My problem is that the cell resizing doesn't seem to work for one of the views for some reason (I tried looking for differences but couldn't find a reason for what I'm experiencing).
I'm calling the cell resizing function in two places:
dataChanged slot.
resizeEvent slot.
If the cell resize function gets called twice inside dataChanged, then the view does update, however this involves some calculations and ui access and obviously not supposed to happen.
If I resize my window then the cells are resized properly.
I suspect that I'm always one update behind - that the view doesn't paint until the new update starts getting calculated and then that new update is on hold until the next calculation (since resize happens a lot of times in succession it might just act the same as the button but is harder/impossible to notice).
I have some dirty workarounds:
As I mentioned, if I call my cell resize function again, the view updates properly.
If I remove the second "if" in this next piece of code then everything works.
I thought I'd save my computer some work by only processing when the entire input had been received. My thinking was that, although dataChanged is emitted for every single item I'm inserting, I only really need to update once it is all in:
void MainWindow::on_dataChanged()
{
static int left_to_insert = -1;
if ( 0 > left_to_insert )
{
left_to_insert = m_model.rowCount() - 1;
}
if ( 0 == left_to_insert )
{
...
m_matrix_proxy.resize_to_fit();
adjust_matrix_cells_sizes();
}
--left_to_insert
}
Is it bad to only process the last signal? Why?
I tried calling update() and/or repaint() on both the matrix and the main window.
I tried calling both of these on the viewport of the QTableView and tried calling them in succession from the matrix itself to the highest parent that didn't make my program crash. (ui->matrix->parentWidget()->parentWidget()...)
I tried qApp->processEvents().
I even resorted to emitting a resizeEvent, but this is overkill IMO as it makes some calculations be performed again.
Just in case it is somehow relevant: The data appears correctly. The only thing that's wrong is that the cells don't resize.
You need to emit layoutChanged signal from your model. But be care with large amounts of items, because handling of this signal may take a lot of time.
Similar questions: one, two
This logic in only code sample you have given is wrong. And this static keyword makes it even worse.
Actual answer:
There is ready solution delivered by Qt! See documentation of QHeaderView::ResizeMode and QHeaderView::setSectionResizeMode
Old answer:
IMO this should look like this:
void MainWindow::MainWindow()
…
{
…
mNeetToResizeCells = false;
connect(this, &MainWindow::NeedUpdateCellsSizes,
this, &MainWindow::ResizeTableCells,
Qt::QueuedConnection); // this is imporatant
}
void MainWindow::on_dataChanged()
{
if (!mNeetToResizeCells) {
mNeetToResizeCells = true;
emit NeedUpdateCellsSizes();
}
}
void MainWindow::ResizeTableCells()
{
mNeetToResizeCells = false;
// update cells sizes here
ui->tableView->resizeColumnsToContents();
ui->tableView->resizeRowsToContents();
}
This way all data updates performed in one iteration of event loop will cause only one invocation of MainWindow::ResizeTableCells in some future iteration of event loop.
I am asked to develop an application where a member can select 1 plan, a combination of plans, all plans, or none.
plan1, plan2, plan3, .... plan12
I ran the truth table to find out how many possibilities there are, and it turned out to be 4096. (Ridiculous!)
My plan was to write an if statement for each possibility like this:
if (plan1.Checked == true && plan2.Checked == false && ... && plan12.Checked == false){
// insert into into table test VALUES('Plan1')
}
and so on! Obviously, there must be a better and easier way than this. Any suggestions would help. Thank you all.
If you used a CheckBoxList or similar component this would be one approach.
CheckBoxList checkBoxList = ....; // Just an example here
// You would add the different project names into the CheckBoxList
String message = "";
for (int i = 0; i < checkBoxList.Items.Count; i++) // Look at every project name
{
if (checkBoxList.Items[i].Selected) // See if it's selected
{
message += checkBoxList.Items[i].Text; // Add the name to the message
}
}
Put message in DB; // <-- Store the message into your database here
Since you're only interested in selected items you wouldn't have to deal with non-selected items at all.
Note: There is probably a better/more efficient way of creating strings than this and you might be able to use lambda expressions. This is just showing a simple approach.
Andrew_CS answer is suitable and I prefer it, but here is another way:
Handle the checked event in the code behind of all checkboxes (use one event, but allow all checkboxes to be handled by it), and depending on the sender, then you can load the information you want.
It may be that you use a Select statement to load the relevant information for each checkbox during the checkbox checked event (might be a little easier to understand than a for loop!)
Fellow developers,
I have a custom list page, where a user can select few records, hit a button in Action pane that runs some logic in a class, and all that works fine. My problem is that the cursor does not stay at the same record but goes to the top of the grid. Sounds like a familiar issue?
I store the FormDataSource of the list page using args in the custom class that has all the logic.
I tried few things but none worked.
formDataSource.research(true)
True parameter is supposed to retain the position after research does its job. I am guessing this should have been the most straightforward solution. List page query has 2 datasources joined using Outer join and my guess is research(true) works only with Inner joins.
formDatasource.setPosition(position)
int position;
position = formDatasource.getPosition();
formDatasource.research();
formDatasource.setPosition(position);
I store the position using getPosition and set it again using setPosition. No use.
formDataSource.findRecord()
currentRecord = formDatasource.cursor();
recId = currentRecord.RecId;
formDatasource.reread();
formDatasource.research();
formDatasource.findRecord(currentRecord);
i use the ds.cursor() to get the current record and pass it to findRecord() after research(). No use.
formDataSource.findValue()
currentRecord = formDatasource.cursor();
recId = currentRecord.RecId;
formDatasource.reread();
formDatasource.research();
formDatasource.findValue(fieldNum(Table, RecId), int642str(recId));
i use the ds.cursor() to get the current record and recId and pass it to findValue() after research(). No use.
I debugged the above code and the cursor() method does get the current record and its recId.
I have started to believe that it might be a limitation of list page, and praying that somebody proves me wrong.
Any help is appreciated.
Use Method 3 but like this.
YourTable tmpTable;
currentRecord = formDatasource.cursor();
recId = currentRecord.RecId;
tmpTable = TmpTable::findByRecId(recId);
formDatasource.reread();
formDatasource.research();
formDatasource.findRecord(tmpTable);
Hope this helps.
Try to pass "true" as a parameter for the research method.
salesLine_ds.research(true) works in my case, i.e. if I research the line, cursor stays on the same line.
use the second method like this
int position;
position = formDatasource.getPosition();
//Make your update operations here
formDatasource.research(true);
formDatasource.setPosition(position);
It works for me.