Вот функция, правила печати дерева решений scikit-learn под python 3 и смещения для условных блоков, чтобы сделать структуру более читаемой:
def print_decision_tree(tree, feature_names=None, offset_unit=' '):
'''Plots textual representation of rules of a decision tree
tree: scikit-learn representation of tree
feature_names: list of feature names. They are set to f1,f2,f3,... if not specified
offset_unit: a string of offset of the conditional block'''
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
value = tree.tree_.value
if feature_names is None:
features = ['f%d'%i for i in tree.tree_.feature]
else:
features = [feature_names[i] for i in tree.tree_.feature]
def recurse(left, right, threshold, features, node, depth=0):
offset = offset_unit*depth
if (threshold[node] != -2):
print(offset+"if ( " + features[node] + " <= " + str(threshold[node]) + " ) {")
if left[node] != -1:
recurse (left, right, threshold, features,left[node],depth+1)
print(offset+"} else {")
if right[node] != -1:
recurse (left, right, threshold, features,right[node],depth+1)
print(offset+"}")
else:
print(offset+"return " + str(value[node]))
recurse(left, right, threshold, features, 0,0)
Это обычная проблема с началом работы с людьми. Всякий раз, когда вы обновляете элементы пользовательского интерфейса из потока, отличного от основного потока, вам нужно использовать:
this.Dispatcher.Invoke(() =>
{
...// your code here.
});
Вы также можете использовать control.Dispatcher.CheckAccess()
, чтобы проверить, является ли текущий поток владеет контролем. Если он действительно владеет, ваш код выглядит нормально. В противном случае используйте шаблон выше.
это произошло со мной, потому что я попытался выполнить access UI
компонент в another thread insted of UI thread
, как этот
private void button_Click(object sender, RoutedEventArgs e)
{
new Thread(SyncProcces).Start();
}
private void SyncProcces()
{
string val1 = null, val2 = null;
//here is the problem
val1 = textBox1.Text;//access UI in another thread
val2 = textBox2.Text;//access UI in another thread
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2);
}
, чтобы решить эту проблему, обернуть любой вызов ui внутри , о чем упоминал Кандид в своем ответе
private void SyncProcces()
{
string val1 = null, val2 = null;
this.Dispatcher.Invoke((Action)(() =>
{//this refer to form in WPF application
val1 = textBox.Text;
val2 = textBox_Copy.Text;
}));
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2 );
}
Кроме того, другое решение гарантирует, что ваши элементы управления создаются в потоке пользовательского интерфейса, а не потоком рабочего потока, например.
Чтобы добавить мои 2 цента, исключение может возникнуть, даже если вы вызываете свой код через System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
. Дело в том, что вам нужно вызвать Invoke()
элемента Dispatcher
элемента управления, к которому вы пытаетесь получить доступ, что в некоторых случаях может не совпадать с System.Windows.Threading.Dispatcher.CurrentDispatcher
. Поэтому вместо этого вы должны использовать YourControl.Dispatcher.Invoke()
для обеспечения безопасности. Я уже несколько часов стучал головой, прежде чем понял это.
Проблема в том, что вы вызываете GetGridData
из фонового потока. Этот метод обращается к нескольким элементам управления WPF, которые привязаны к основному потоку. Любая попытка доступа к ним из фонового потока приведет к этой ошибке.
Чтобы вернуться к правильной теме, вы должны использовать SynchronizationContext.Current.Post
. Однако в этом конкретном случае, похоже, большая часть работы, которую вы выполняете, основана на пользовательском интерфейсе. Следовательно, вы создадите фоновый поток, чтобы сразу вернуться к потоку пользовательского интерфейса и выполнить некоторую работу. Вам нужно немного реорганизовать свой код, чтобы он мог выполнять дорогостоящую работу в фоновом потоке, а затем публиковать новые данные в потоке пользовательского интерфейса после
Как уже упоминалось здесь , Dispatcher.Invoke
может заморозить UI. Вместо этого используйте Dispatcher.BeginInvoke
.
Вот удобный класс расширений, который упрощает проверку и вызов диспетчера вызовов.
Пример использования: (вызов из окна WPF)
this Dispatcher.InvokeIfRequired(new Action(() =>
{
logTextbox.AppendText(message);
logTextbox.ScrollToEnd();
}));
Класс расширения:
using System;
using System.Windows.Threading;
namespace WpfUtility
{
public static class DispatcherExtension
{
public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
{
if (dispatcher == null)
{
return;
}
if (!dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
return;
}
action();
}
}
}
Еще одно полезное использование для Dispatcher.Invoke
- это немедленное обновление пользовательского интерфейса в функции, выполняющей другие задачи:
// Force WPF to render UI changes immediately with this magic line of code...
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
Я использую это, чтобы обновить текст кнопки до « Обработка ... "и отключить его при выполнении запросов WebClient
.
Я также обнаружил, что System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
не всегда является диспетчером целевого контроля, точно так же, как dotNet пишет в своем ответе. У меня не было доступа к диспетчеру управления, поэтому я использовал Application.Current.Dispatcher
, и он решил проблему.
По какой-то причине ответ Кандиде не строился. Это было полезно, однако, поскольку это заставило меня найти это, которое отлично работало:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
//your code here...
}));
Это работает для меня.
new Thread(() =>
{
Thread.CurrentThread.IsBackground = false;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {
//Your Code here.
}, null);
}).Start();
Вам нужно обновить пользовательский интерфейс, поэтому используйте
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
Я продолжал получать ошибку, когда добавлял каскадные comboboxes в свое приложение WPF и разрешал ошибку с помощью этого API:
using System.Windows.Data;
private readonly object _lock = new object();
private CustomObservableCollection<string> _myUiBoundProperty;
public CustomObservableCollection<string> MyUiBoundProperty
{
get { return _myUiBoundProperty; }
set
{
if (value == _myUiBoundProperty) return;
_myUiBoundProperty = value;
NotifyPropertyChanged(nameof(MyUiBoundProperty));
}
}
public MyViewModelCtor(INavigationService navigationService)
{
// Other code...
BindingOperations.EnableCollectionSynchronization(AvailableDefectSubCategories, _lock );
}