JavaFX get row and column from a GridPane - javafx

I'm trying to make a hangman in JavaFX. I have 26 text nodes in a gridpane, but I don't want to make a method for each node, so I want to make a single method that handles on which text node you've clicked. I can't really figure out how to do it though. Any help?

Create a method that takes a String parameter representing the text on which the click occurred:
private GridPane grid ;
private int numColumns ;
private void processClick(String text) {
// ...
}
and then just call it from each of the handlers, e.g.:
for (char c = 'A' ; c <= 'Z' ; c++) {
String s = Character.toString(c);
Text text = new Text(s);
text.setOnMouseClicked(e -> processClick(s));
grid.add(text, (c-'A') % numColumns, (c-'A') / numColumns);
}
(If you really need the row and column for whatever reason, you can equally easily make your method take those values and pass them from the handler.)

Related

QTableView not scrolling all the way to the bottom

I'm mocking up a table in Qt using QTableView and QStandardItemModel. The table represents events in a log. It should scroll to the bottom so that it can show the most recent event. To be clear, I am mocking up this display, I am not adding elements to the table dynamically.
When I try using either the scrollToBottom method or the scrollTo method, the view is scrolling about 3/4 of the way down, rather than to the actual bottom of the window.
Here's a simplified version of my code:
logModel = new QStandardItemModel(THREADWATCHER_LOG_TABLE_LENGTH, 2, this);
logTable = new QTableView;
for (int i = 0; i < THREADWATCHER_LOG_TABLE_LENGTH; i += 6) {
QString spawnTimeString = QString("[16:09:10:");
int spawnTimeMs = 186 + i * 8; // a pseudo-random choice for the interval between spawns
spawnTimeString.append(QString::number(spawnTimeMs) + "]");
logMessage[i] = new QStandardItem(spawnTimeString + " Thread \"Image Fetcher 0\" was spawned in the image processor ");
logModel->setItem(i, 0, logMessage[i]);
// do the same thing with [i+1], [i+2]...[i+5]
// so that I get six different messages in each iteration of the loop
}
logTable->setModel(logModel);
QModelIndex lastIndex = logMessage[THREADWATCHER_LOG_TABLE_LENGTH - 1]->index();
logTable->scrollTo(lastIndex);
I get exactly the same thing if I call logTable->scrollToBottom() instead of scrolling to the last index.
Things I've tried already:
using a single-shot timer (to ensure events have completed before scrolling)
creating a public method that calls logTable->scrollToBottom(), and having something else call that method after this class's constructor finishes (the logic above is in the constructor)

How to set data source for a list view to contain custom data ? (and associate with QTableView)

I am trying to get listview and tableview working together.
The listview must be used for display, the tableview must be used for editing data. The tableview is created on demand in a popup widget (and it may never be needed).
I populate the listview, on start, from a text file - each row a line, with 2 entries separated by a tab. Easy.
The tableview will have to edit 2 columns separately... also, on listview click, I must be able to retrieve the first part of the split...
I have created a model subclass of QStringListModel.
QListView *m_myView = new QListView();
StringList *m_myList = new StringList();
QTextStream in(&myFile);
while (!in.atEnd())
{
QString temp = in.readLine();
if(!temp.isEmpty())
m_myList->append(temp);
}
myFile.close();
m_myView->setModel(m_myList);
where
class StringList : public QStringListModel
{
public:
void append (const QString& string)
{
insertRows(rowCount(), 1);
QModelIndex m = index(rowCount() - 1);
setData(m, string, Qt::EditRole);
QStringList splist = string.split('\t');
setData(m, splist.at(0), Qt::ToolTipRole);
if(splist.size() > 1)
setData(m, splist.at(1), Qt::StatusTipRole);
else
setData(m, "", Qt::StatusTipRole);
qDebug() << data(m, Qt::EditRole).toString()
<< data(m, Qt::ToolTipRole).toString()
<< data(m, Qt::StatusTipRole).toString();
}
};
The EditRole displays correctly, the others display empty strings.
It seems that QStringListModel is incapable of storing anything except EditRole.
So I am left with 2 options - either do the split on each selection, and also when creating the table view, or - what I would like to try but don't know how - create a QStandardItemModel that can act as data sources for both the listview and the tableview, and can easily retrieve the partial data I require on click.
I thought I can simply use it to set the data on listview (like they do here).
QListView *m_myView = new QListView();
QStandardItemModel *m_myList = new QStandardItemModel();
QTextStream in(&myFile);
int r = 0;
while (!in.atEnd())
{
QString temp = in.readLine();
if(!temp.isEmpty())
{
QStringList splist = temp.split('\t'); // assume I know there will be 2 strings always
QStandardItem *item = new QStandardItem(splist.at(0));
m_myList->setItem(r, 0, item);
QStandardItem *item1 = new QStandardItem(splist.at(1));
m_myList->setItem(r, 1, item1);
++r;
}
}
myFile.close();
m_myView->setModel(m_myList);
connect(m_myListView, SIGNAL(clicked(QModelIndex)),
this, SLOT(listChangedSlot(QModelIndex)));
But this will only set the first string in the listview, I really need both, and I still don't know how to retrieve the data
void listChangedSlot(QModelIndex index)
{
qDebug() << m_myList->item(index.row(), 0)->data().toString()
<< m_myList->item(index.row(), 1)->data().toString();
} // shows empty strings
Or...
In the loading section, try:
if(!temp.isEmpty())
{
QStringList splist = temp.split('\t');
splist.append(QString()); // so I don't need to test
QStandardItem *item = new QStandardItem(temp);
m_myList->setItem(r, 0, item);
QModelIndex idx = m_myList->index(r, 0);
QMap<int, QVariant> roles;
roles.insert(Qt::UserRole + 1, QVariant(splist.at(0)));
roles.insert(Qt::UserRole + 2, QVariant(splist.at(1)));
roles.insert(Qt::DisplayRole, QVariant(temp));
m_myList->setItemData(idx, roles);
++r;
}
This works - displays fine and gets the correct content on click - but seems to be unusable for the tableview.
(And seems I am doing twice the work, with setItem and setItemData - so technically storing the content 3 times).
How can I get my listview have a datasource with 2 string items, show both, be able to set it on a tableview, and be able to retrieve the first item on click ?
I figured out a way to share the data source between the listview and tableview:
I create 3 columns - column 0 with the combined components, and columns 1 and 2 with the separated components.
The listview will display only column 0. For tableview, I will hide column 0.
The data I require for processing will be stored in columns 1 and 2. user edit of the data in tableview will require, upon accepting changes, the edit of corresponding column 0.

input for arraylist using readline() of BufferedReader

I am trying to take input and add them in an arraylist, using BufferedReader.
The code is taking endless inputs and it is not advancing to further lines (not exiting the for-loop).
Please find my below code:
public class ALinput {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n2 = br.read();// number of items in the list
ArrayList<String> list2 = new ArrayList<String>();
for(int i =1;i<=n2;++i)
{
list2.add(br.readLine());
}
System.out.println(list2);//printing list before sorting
Collections.sort(list2);//sorting the list
System.out.println("After sorting "+list2);
}
}
I have taken n2 as number of elements in the array list.
if input n2 = 5;
the readLine keeps on taking text input endlessly without exiting after 5 strings are added to the arraylist.
Its not coming out of the for loop. Please help me understand the mistake I am doing here.
It actually isn't going forever, the character code for a zero is 48, one is 49, two is 50, and so on. so when you type in a 5, the computer reads it's character value, and stores n2 as 53, and let's you type in 52 inputs. why 52 instead of 53?
well theres a second mistake, br.read() only looks at the first byte of input, so one character. the first time br.readLine() is called, it finishes reading whatever was on the line after the first number you typed. to fix these problems, just change
int n2 = br.read();
to
int n2 = Integer.parseInt(br.readLine());
and voila! done!
P.S. you might want to surround the Integer.parseInt() in a try catch block, in case the user types in something besides a number.

How to get the text on qpushbutton?

I am loading .ui files via QUiloader, and showing the GUI in my application.
QWidget *mywidget = loader.load(file, this);
QList<QWidget*> wlist = mywidget.findChildren<QWidget *>()
I would like to know what is the text on QPushbutton. I know there is a method text() to get a text from Pushbutton, but it is not accessible when I do:
QString btext = wlist.at(1).text();
Any idea how I can get the text from QPushbutton, and other Widgets, when they grouped as QWidget?
Thanks.
You should search for QPushButtons instead of QWidgets:
QList<QPushButton*> blist = widget.findChildren<QPushButton*>();
Still your code wouldn't compile. The last line should read:
QString btext = blist.at(1)->text();
Using -> since you are accessing a pointer, not the widget. Also you should check if the findChildren() function actually returnes enough buttons. You would get a crash or assertions when accessing a list item by an invalid index.
Also please note that at(1) does not return the first but the second item in the list (lists start from 0).
Update: If you search for QWidgets and cast each of them you need to take care of getting a nullptr:
QList<QWidget*> wlist = widget.findChildren<QWidget*>();
foreach (QWidget* w, wlist)
{
QPushButton* b = dynamic_cast<QPushButton*>(w);
// If "w" is not a button "b" is nullptr
if (b)
{
QString btext = b->text();
}
}

two dimensional vector

I wanted to have a linked list of nodes with below structure.
struct node
{
string word;
string color;
node *next;
}
for some reasons I decided to use vector instead of list.my question is that is it possible to implement a vector which it's j direction is bounded and in i direction is unlimited and to add more two strings at the end of my vertex.
in other words is it possible to implement below structure in vector ?
j
i color1 color2 …
word1 word2 …
I am not good with C/C++, so this answer will only be very general. Unless you are extremely concerned about speed or memory optimization (most of the time you shouldn't be), use encapsulation.
Make a class. Make an interface which says what you want to do. Make the simples possible implementation of how to do it. Most of the time, the simplest implementation is good enough, unless it contains some bugs.
Let's start with the interface. You could have made it part of the question. To me it seems that you want a two-dimensional something-like-an-array of strings, where one dimension allows only values 0 and 1, and the other dimension allows any non-genative integers.
Just to make sure there is no misunderstanding: The bounded dimension is always size 2 (not at most 2), right? So we are basicly speaking about 2×N "rectangles" of strings.
What methods will you need? My guesses: A constructor for a new 2×0 size rectangle. A method to append a new pair of values, which increases the size of the rectangle from 2×N to 2×(N+1) and sets the two new values. A method which returns the current length of the rectangle (only the unbounded dimension, because the other one is constant). And a pair of random-access methods for reading or writing a single value by its coordinates. Is that all?
Let's write the interface (sorry, I am not good at C/C++, so this will be some C/Java/pseudocode hybrid).
class StringPairs {
constructor StringPairs(); // creates an empty rectangle
int size(); // returns the length of the unbounded dimension
void append(string s0, string s1); // adds two strings to the new J index
string get(int i, int j); // return the string at given coordinates
void set(int i, int j, string s); // sets the string at given coordinates
}
We should specify what will the functions "set" and "get" do, if the index is out of bounds. For simplicity, let's say that "set" will do nothing, and "get" will return null.
Now we have the question ready. Let's get to the answer.
I think the fastest way to write this class would be to simply use the existing C++ class for one-dimensional vector (I don't know what it is and how it is used, so I just assume that it exists, and will use some pseudocode; I will call it "StringVector") and do something like this:
class StringPairs {
private StringVector _vector0;
private StringVector _vector1;
private int _size;
constructor StringPairs() {
_vector0 = new StringVector();
_vector1 = new StringVector();
_size = 0;
}
int size() {
return _size;
}
void append(string s0, string s1) {
_vector0.appens(s0);
_vector1.appens(s1);
_size++;
}
string get(int i, int j) {
if (0 == i) return _vector0.get(j);
if (1 == i) return _vector1.get(j);
return null;
}
void set(int i, int j, string s) {
if (0 == i) _vector0.set(j, s);
if (1 == i) _vector1.set(j, s);
}
}
Now, translate this pseudocode to C++, and add any new methods you need (it should be obvious how).
Using the existing classes to build your new classes can help you program faster. And if you later change your mind, you can change the implementation while keeping the interface.

Resources