Qt: ProxyModel, QTreeView and Delegate Setup with filtering does not correctly update - expand

I have a setup with QTreeView displaying a QSortFilteredProxyModel with custom filtering in filterAcceptsRow() accepting only rows depending on the value of a parent row value (selected in a QComboBox via QStyledItemDelegate-derived class. For example, if I choose in a row "Type" the value "BMW", I wanna display children for this special case.
This works great for the first item, I can choose these values inside of Item1 multiple levels down (up to 5 levels). Everything works great.
However, i've notice that once i am playing with item1 and this custom filtering and then adding an item2, the problems start. First thing I notice is, that QTreeView does un-expand the common parent of item1/item2 and displays only the children of the root item. The normal behaviour (and behaviour of adding item1 does like that) is expand the parent of item1. Why this gets confused on the second item and ONLY IF ive been playing with the item1 children-settings, idk.
Also, Ive noticed that in item2 the filtering does not work correctly. I have a QComboBox in item2->Type that have children depending on item2->Type value.. the first child for default value of the QComboBox still gets displayed like it should, but when changing QComboBox the view does not get updated any longer and stays with this first loaded child inside item2/item3/... However, the Delegate::setModelData gets called with the correct value/index and calls TreeItem->setData() accordingly. Its just that the filtering seems to be stuck with the old value of TreeItem->data(). Please note again, that this does only occur after ive been playing with item1.. If i leave that item untouched, I can correctly play with all other items (item2, item3, ...) and its doing fine.
I would be very happy if anybody can help.
Does anybody have tipps for me, what might cause the "reset" of expandedStates, even though the expand() gets called the same for item1 and item2 insertion and works great for item1 ?
What might cause my further problems?
ExampleDelegate.h
class ExampleDelegate :
public QStyledItemDelegate
{
Q_OBJECT
public:
ExampleDelegate(QObject* parent = 0);
~ExampleDelegate(void);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void setEditorData(QWidget* editor, const QModelIndex &index) const;
}
ProxyModel.h
class ProxyModel :
public QSortFilterProxyModel
{
Q_OBJECT
public:
ProxyModel(QObject *parent);
~ProxyModel(void);
void refresh();
void doReset();
pthread_mutex_t* proxyMutex;
int rowCount(QModelIndex& parent) const;
bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const;
QModelIndex parent(const QModelIndex &index) const;
QModelIndex index(int row, int col, QModelIndex& parent) const;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
};
ProxyModel.cpp
bool ProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
printf("ProxyModel::filterAcceptRow \n");
pthread_mutex_lock(this->proxyMutex);
QAbstractItemModel* source = this->sourceModel();
QModelIndex index = source->index(sourceRow, 0, sourceParent);
TreeItem* item = ((TreeItem*) index.internalPointer());
if(item != NULL) {
int showOnly = item->showOnlyParentDetailDialog;
if(showOnly != -1) {
TreeItem* parent = ((TreeItem*) index.internalPointer())->parent();
if(parent != NULL) {
int parentOption = parent->selectedIndex;
//printf("parentOption: %u \n", parentOption);
bool result = (showOnly == parent->selectOptions->at(parentOption).detailDialog);
pthread_mutex_unlock(this->proxyMutex);
return result;
} else {
pthread_mutex_unlock(this->proxyMutex);
return true;
}
}
}
pthread_mutex_unlock(this->proxyMutex);
return true;
}

Related

Minecraft Forge - Place block onItemUse

I'm trying to make my custom item place water when used:
public class Beaker extends Item implements IHasModel {
public Beaker(String name, CreativeTabs tab, int maxStackSize) {
setUnlocalizedName(name);
setRegistryName(name);
setCreativeTab(tab);
setMaxStackSize(maxStackSize);
ItemInit.ITEMS.add(this);
}
#Override
public void registerModels() {
Main.proxy.registerItemRenderer(this, 0, "inventory");
}
#Override
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
BlockPos clickedBlock = new BlockPos(hitX, hitY, hitZ);
worldIn.setBlockState(clickedBlock, Blocks.WATER.getDefaultState());
return EnumActionResult.SUCCESS;
}
}
However, when I right click, the item gets used (animation plays) but the water is not placed, I'm using Minecraft 1.12.2 and Forge 14.23.2.2613.
hitX hitY and hitZ have no relation to the world location where the action was performed. They are partial values within the clicked block for performing actions based on where the block was clicked (e.g. which button on a number pad).
If we look at ItemBlockSpecial (which handles items like Cake, Repeaters, Brewing Stands, and Cauldrons) we find this code:
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
IBlockState iblockstate = worldIn.getBlockState(pos);
Block block = iblockstate.getBlock();
// snow layer stuff we don't care about
if (!block.isReplaceable(worldIn, pos))
{
pos = pos.offset(facing);
}
// placement code
}
The important thing to note here is that the pos parameter is used directly, while being modified by the facing parameter in the event that the block clicked is not replaceable (e.g. Tall Grass is replaceable, Stone is not).
If ItemBlockSpecial isn't sufficient for you (i.e. you can't make your item an instance of ItemBlockSpecial), then the code it uses to do things probably still will be.

TEdit and TCheckBox Validations

My purpose is that the users will never be able to check a TCheckBox when the number is entered into a TEdit less than 7 digits. Also, this TCheckBox can never be checked when the TEdit is being empty.
A problem of my codes is sometimes TCheckBox can still be checked although TEdit is being empty.
Moreover, my another target is that the start button can never be executed or will always display an error message if the start button is clicked when the TCheckBox is checked while the TEdit is being empty.
The problem is what codes should I put in the start button ?.
I am using the following code:
//--------------------------------------------------------------------------------
void __fastcall TForm::MyTEditBoxKeyPress(TObject *Sender, System::WideChar &Key)
{
if( Key == VK_BACK ) return;
if((Key < '1') || (Key > '9'))
{
MessageDlg("Please enter number only.",mtInformation, TMsgDlgButtons()<< mbOK, 0);
Key = 0;
}
}
//--------------------------------------------------------------------------------
void __fastcall TForm::MyTEditBoxExit(TObject *Sender)
{
if (MyTEditBox->Text.Length() < 7) {
MessageDlg("Please enter at least 7 digit.",mtInformation, TMsgDlgButtons()<< mbOK, 0);
}
}
//--------------------------------------------------------------------------------
void __fastcall TForm::MyCheckBoxClick(TObject *Sender)
{
if (MyCheckBox->Tag == 0 ) {
MyCheckBox->Tag = 1;
if (MyTEditBox->Text.Length() >= 7)
MyCheckBox->Checked = true;
IdThrottler->BitsPerSec = StrToInt64(MyTEditBox->Text);
}
else {
MyCheckBox->Tag = 0;
if (MessageDlg("Please enter at least 7 digit.",mtInformation, TMsgDlgButtons()<< mbOK, 0) == mrYes)
MyCheckBox->Checked = false;
}
}
First off, the throttler's BitsPerSec property is an int, not an __int64, so you should be using StrtoInt() instead of StrToInt64().
You are setting the TCheckBox::Enabled property in the TCheckBox::OnClick event, so the user has to actually click on the TCheckBox to make it update itself. If the user only typed in the TEdit and never clicks on the TCheckBox, it will never be updated.
If you don't want the user to click on the TCheckBox at all unless the TEdit text is adequate, you should use the TEdit::OnChange event to set the TCheckBox::Enabled property, and get rid of your TCheckBox::Tag handling altogether:
void __fastcall TMyForm::MyTEditBoxChange(TObject *Sender)
{
MyCheckBox->Enabled = (MyTEditBox->GetTextLen() >= 7);
}
void __fastcall TMyForm::MyCheckBoxClick(TObject *Sender)
{
if (MyCheckBox->Checked)
IdThrottler->BitsPerSec = StrToInt(MyTEditBox->Text);
else
IdThrottler->BitsPerSec = 0;
}
Do note that just because the user can type in more than 6 digits does not mean its Text represents a value int value. In that situation, StrToInt() will raise an exception.
A different way to handle this is to add a TActionList to your Form, create a custom action in it, assign that action to the TCheckBox::Action property, and then use the TAction::OnUpdate event to set the TAction::Enabled property (which will enable/disable the TCheckBox):
void __fastcall TMyForm::MyActionUpdate(TObject *Sender)
{
MyAction1->Enabled = (MyTEditBox->GetTextLen() >= 7);
}
The benefit of this approach is that the TCheckBox::Enabled property will be updated automatically and in real-time without having to manually react to changes in the TEdit at all.
With that said, if you are using a modern version of C++Builder, TEdit has a NumbersOnly property. When set to true, you don't have to filter keystrokes in the TEdit::OnKeyPress event anymore, the OS will prevent the user from typing non-digit characters for you (besides, when you are filtering manually, you are not allowing the user to type in 0 digits, which is wrong).
If you really must allow the user to enter a number via a TEdit, and if the TEdit::NumbersOnly property is not available in your version of C++Builder, you still have a couple of other options (which you should consider anyway, even in modern C++Builder versions):
make the TEdit read-only, attach a TUpDown to it via the TUpDown::Associate property, and assign appropriate TUpDown::Min and TUpDown::Max values as needed. Use the TUpDown::Position property to update the throttler's BitsPerSec property:
void __fastcall TMyForm::MyActionUpdate(TObject *Sender)
{
MyAction1->Enabled = (MyUpDown->Position > 999999);
}
void __fastcall TMyForm::MyUpDownClick(TObject *Sender, TUDBtnType Button)
{
if ((MyCheckBox->Enabled) && (MyCheckBox->Checked))
IdThrottler->BitsPerSec = MyUpDown->Position;
else
IdThrottler->BitsPerSec = 0;
}
Maybe also use a TTrackBar that sets the TUpDown::Value property in larger increments so the user does not have to press the up/down arrows for more than small adjustments:
void __fastcall TMyForm::MyTrackBarChange(TObject *Sender)
{
MyUpDown->Position = MyTrackBar->Position;
MyUpDownClick(NULL, btNext);
}
Don't bother using a TEdit at all. Use a TCSpinEdit or TSpinEdit instead (depending on your version of C++Builder). The user can type in numbers, and it will reject non-numeric input. It provides up/down arrows, like TUpDown, for making small adjustments. And it has a Value property that returns/accepts an int instead of a String, just like the TUpDown::Position property.
void __fastcall TMyForm::MyActionUpdate(TObject *Sender)
{
MyAction1->Enabled = (MySpinEdit->Value > 999999);
}
void __fastcall TMyForm::MySpinEditChange(TObject *Sender)
{
if ((MyCheckBox->Enabled) && (MyCheckBox->Checked))
IdThrottler->BitsPerSec = MySpinEdit->Value;
else
IdThrottler->BitsPerSec = 0;
}
Either way, the user cannot enter non-numeric values at all, and the TCheckBox still auto-disables itself for values that are smaller than your desired threshold.

qvariant_cast causing segfault

Within qt's item/view framework, I'm trying to save a QColorDialog as user data and then retrieve that dialog as the editor, as well as during paint, in a tableview.
In my class constructor I do
QStandardItem *item = new QStandardItem();
QColorDialog *colorDlg = new QColorDialog(QColor(0,0,255), this);
item->setData(QVariant::fromValue(colorDlg), ColorDialogRole);
mTableModel->setItem(0,2,item);
then, inside my delegate's paint function I have
void ReportFigureTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QVariant vColorDlg= index.data(ReportFigure::ColorDialogRole);
if(vColorDlg.isValid())
{
////////////////////////////////////////////////
// Program segfaults on the next line ... why?
////////////////////////////////////////////////
QColorDialog *colorDlg = qvariant_cast<QColorDialog*>(vColorDlg);
if(colorDlg != NULL)
{
painter->save();
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
painter->fillRect(opt.rect, colorDlg->selectedColor());
painter->restore();
}
else
QStyledItemDelegate::paint(painter, option, index);
}
else
QStyledItemDelegate::paint(painter, option, index);
}
During runtime, the table shows up the first time (although with the wrong color ... different issue I assume). I double click to edit the cell and it brings up the dialog as expected. When I close, though, it segfaults on the indicated line. I don't understand why since I think I'm doing all the necessary checks.
You set the data on a QStandardItem object. Meanwhile you are retrieving the data on a QModelIndex object. Now why is the variant valid is a mystery. Maybe because ReportFigure::ColorDialogRole is equal to a build-in Qt role while it should be at least Qt::UserRole.
Anyway In the paint() method you can access the previously set item using
QStandardItem *item = mTableModel->itemFromIndex(index);

How to edit controls in a system::thread.

I need to be able to add items to a listbox inside of a thread. Code is below:
1. ref class Work
2. {
3. public:
4. static void RecieveThread()
5. {
6. while (true)
7. {
8. ZeroMemory(cID, 64);
9. ZeroMemory(message, 256);
10. if(recv(sConnect, message, 256, NULL) != SOCKET_ERROR && recv(sConnect, cID, 64, NULL) != SOCKET_ERROR)
11. {
12. ID = atoi(cID);
13. String^ meep = gcnew String(message);
14. lbxMessages->Items->Add(meep);
15. check = 1;
16. }
17. }
18. }
19. };
I get the error
Error: a nonstatic member reference must be relative to a specific object on line 14. Is there any way to get it to let me do that? Because if I try to use String^ meep; outside of that Thread it doesn't contain anything. It works PERFECT when I use it within the thread but not outside of it. I need to be able to add that message to the list-box. If anyone can help I would appreciate it.
You don't show how lbxMessages is defined, but I'm going to assume that it's a non-static data member in the same class. If that's the case, then you need to specify which object you want to access lbxMessages on. The simplest way would be to switch the RecieveThread method to be non-static, then you can access this->lbxMessages.
You didn't say which windowing toolkit you're using, but you probably will need to Invoke back onto the UI thread in order to edit the control.
One way is using System::Thread with a ParameterizedThreadStart delegate, which let you pass objects, in this case lbxMessages.
ParameterizedThreadStart^ threadCallback;
threadCallback = gcnew ParameterizedThreadStart(&Work::ReceiveThread);
Thread^ recvThread = gcnew Thread( threadCallback );
recvThread->Start( lbxMessages );
Your static method for running the thread:
static void RecieveThread(Object^ state)
{
ListBox^ lbxMessages = (ListBox^)state;
//...code
}
But.. . there is another problem. Assuming ListBox is a Win32 control, you can make changes in control only from the thread which it was created. So every time you insert a ListBox item, it must be done from the UI's thread. One way is using a SynchronizationContext object.
// Start the thread
array<Object^>^ args = gcnew array<Object^>(2){
lbxMessages,
SynchronizationContext::Current
}
recvThread->Start( args);
Thread method should be something like this:
static void RecieveThread(Object^ state)
{
array<Object^>^ args = (array<Object^>^)state;
ListBox^ lbxMessages = (ListBox^)args[0];
SynchronizationContext^ ctx = (SynchronizationContext^)args[1];
//...code
String^ meep = gcnew String(message);
ctx->Send(gcnew SendOrPostCallback(&Work::SafeListBoxInsert),
gcnew array<Object^>(2){lbxMessages, meep}
);
}
You will need another method to be called from UI's thread and make the changes.
ref class Work{
//...other methods
static void SafeListBoxInsert(Object^ state)
{
array<Object^>^ args = (array<Object^>^)state;
ListBox^ lst = (ListBox^) args[0];
String^ item = (String^) args[1];
lst->Items->Add(item);
}
}

Edited properties who can't be set to true/false equally

It's hard to explain my problem so give me a break if it's not very clear.
I have some ten properties that can be edited in the view. Most of them are booleans. These properties configure a test environment (like one property can be configured to show the solution).
Now, the problem is that some properties can't be set to true if others are set to false. Nothing would go wrong, but it's just useless. It's like one property would be configured not to show the correct button and another to show the solution. In our system, if you can't click on the correct button, then solution will never be shown.
Does this kind of problem have a name?
Do such properties have a name (just like there are immutable properties)?
Are there best-practices to implement such a story? We can hard code it in the view, but I would rather have a generic system.
The word you're looking for is orthogonality. The settings are not orthogonal, as they can't vary independently.
As to how to handle showing these properties, the completely generic way to do it (and your problem may not warrant the coding cost of this genericicity) would be to give each control an expression that references the other controls, where if the complete expression evaluates to true (or false), the control is disabled in the view.
Easier to code would be a control that exposed an isDisabled() method, which you could override as necessary. Here's a short Java example, which leverages Java anonymous classes to do the hard work. It assumes there's already a Control class, with a booleanValue() getter that converts it to a boolean, and that since AutoDisabledControl is-a Control, it can be used as a drop-in replacement for a Control:
public class AutoDisabledControl extends Control {
public isDisabled() { return false ; }
}
..... usage ....
// control1 is never disabled
final Control1 = new AutoDisabledControl() ;
// Control2 is disabled if control1 is false
final Control2 = new AutoDisabledControl() {
public isDisabled() { return control1.booleanValue() == false; }
};
// conntrol 3 is enabled only if control1 and control2 are true
final Control1 = new AutoDisabledControl() {
public isDisabled() { return ! (
control1.booleanValue()
&& control2.booleanValue()) ;
};
Naturally, in the View's display, it checks each control's isDisabled() , and disables the ones that return true; when a Control's value is changed, the view redisplays. I'm assuming some sort of MVC Pattern.
You propably mismodeled your solution.
Try to think in a different way - perhaps U can eliminate some parameters that can be inferred from the others or u can use enumarations to combine few parameters into one.
Investigate your parameters' value space to find it out.
You could use an int or long to store the related properties and use a bit mask when setting a property to correctly clear invalid settings. This int or long could be in the form of a flagged enumeration.
[Flags]private enum BitValues
{
Bit1 = 1 << 0, //Inclusive
Bit2 = 1 << 1, //Exclusive to bit 3 and 4
Bit3 = 1 << 2, //Exclusive to bit 2 and 4
Bit4 = 1 << 3, //Exclusive to bit 2 and 3
ExclusiveBits = Bit2 | Bit3 | Bit4 //All exclusive bits
}
private BitValues enValues;
public bool Bit1
{
get { return (enValues & BitValues.Bit1) == BitValues.Bit1; }
set
{
//Clear the bit
enValues = (enValues ^ BitValues.Bit1) & enValues;
//Set the bit
enValues = enValues | BitValues.Bit1;
}
}
public bool Bit2
{
get { return (enValues & BitValues.Bit2) == BitValues.Bit2; }
set
{
//Clear exclusive bits
enValues = (enValues ^ BitValues.ExclusiveBits) & enValues;
//Set bit
enValues = enValues | BitValues.Bit2;
}
}