Страницы

Поиск по вопросам

вторник, 5 марта 2019 г.

Прозрачность ElementHost

Можно ли сделать экземпляр ElementHost прозрачным? Перепробовал множество вариантов, но ни один не помог.


Ответ

Нет. На стыке WinForms и WPF прозрачность не поддерживается.
Какое-то время назад разрабы Microsoft совершили героический подвиг и таки решили эту проблему, но эта фича не попала в релиз, потому что её сочли слишком костыльной. Такая боль.

C# как вычислить координаты картинки в картинке

Есть 5 видов разных шахматных досок, разного размера (на разные размеры экрана).
Есть массив минимальных квадратиков каждого цвета.
Всё, получается. Но..
private void btnGetWindows_Click(object sender, System.EventArgs e) {
Bitmap bScreen = DoingScreen(); bool res = Contains(bMark, bScreen);
}
Берем делаем скриншот, проверяем с заготовлеными квадратиками. Как можно получить координаты начала маленькой картинки в большой картинке? И следующей за ней?
Например, у 1 квадратика координаты приблизительно 5х5, у второго 155х5 и т.д.
PS Много вопросов появилось. Выкладываю функцию распознавания. Как ее модифицировать, чтобы координаты находились?
private bool Contains(Bitmap smallBmp, Bitmap bigBmp, double tolerance = 0) { BitmapData smallData = smallBmp.LockBits(new Rectangle(0, 0, smallBmp.Width, smallBmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb); BitmapData bigData = bigBmp.LockBits(new Rectangle(0, 0, bigBmp.Width, bigBmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
int smallStride = smallData.Stride; int bigStride = bigData.Stride;
int bigWidth = bigBmp.Width; int bigHeight = bigBmp.Height - smallBmp.Height + 1; int smallWidth = smallBmp.Width * 3; int smallHeight = smallBmp.Height;
Rectangle location = Rectangle.Empty; int margin = Convert.ToInt32(255.0 * tolerance);
unsafe { byte* pSmall = (byte*)(void*)smallData.Scan0; byte* pBig = (byte*)(void*)bigData.Scan0;
int smallOffset = smallStride - smallBmp.Width * 3; int bigOffset = bigStride - bigBmp.Width * 3;
bool matchFound = true;
for (int y = 0; y < bigHeight; y++) { for (int x = 0; x < bigWidth; x++) { byte* pBigBackup = pBig; byte* pSmallBackup = pSmall;
//Look for the small picture. for (int i = 0; i < smallHeight; i++) { int j = 0; matchFound = true; for (j = 0; j < smallWidth; j++) { //With tolerance: pSmall value should be between margins. int inf = pBig[0] - margin; int sup = pBig[0] + margin; if (sup < pSmall[0] || inf > pSmall[0]) { matchFound = false; break; }
pBig++; pSmall++; }
if (!matchFound) break;
//We restore the pointers. pSmall = pSmallBackup; pBig = pBigBackup;
//Next rows of the small and big pictures. pSmall += smallStride * (1 + i); pBig += bigStride * (1 + i); }
//If match found, we return. if (matchFound) { location.X = x; location.Y = y; location.Width = smallBmp.Width; location.Height = smallBmp.Height; break; } //If no match found, we restore the pointers and continue. else { pBig = pBigBackup; pSmall = pSmallBackup; pBig += 3; } }
if (matchFound) break;
pBig += bigOffset; } }
bigBmp.UnlockBits(bigData); smallBmp.UnlockBits(smallData);
if (location == Rectangle.Empty) return false; return true; }


Ответ

Дык вот же они.. Всё есть.. См. функцию распознования..
if (matchFound) { location.X = x; location.Y = y; location.Width = smallBmp.Width; location.Height = smallBmp.Height; break; }

Узнать букву диска зная адрес шары

Допустим, есть \\share\lala , как мне программно узнать какой букве диска на сервере соответствует данная шара?


Ответ

Это можно сделать через WMI для которого в .NET есть обертка.
Вам понадобится добавить в проект ссылку на System.Management.DLL и указать в коде using System.Management;
Выведем все имеющиеся "шары" с указанием полного пути к локальной папке
var oManager = new ManagementClass("Win32_Share"); foreach(ManagementObject oShare in oManager.GetInstances()) { var strShareName = oShare .Properties .Cast() .First(x => x.Name == "Name") .Value.ToString(); var strSharePath = oShare .Properties .Cast() .First(x => x.Name == "Path") .Value.ToString(); if(strSharePath == "") strSharePath = "***UNDEFINED***"; Console.WriteLine(strShareName + " - " + strSharePath); }
strPath будет содержать полный путь к шаре, из которого достать букву диска уже совсем просто - например так:
var di = new DirectoryInfo(strSharePath); Console.WriteLine(di.Root);
Добавив совсем немного кода, можно сделать поиск данных "шары" по ее сетевому имени.
Приведенный пример позволяет узнать пути к общим папкам на локальной машине, однако WMI позволяет также обращаться к удаленным машинам, при наличии соответствующих прав у пользователя и необходимых разрешениях на удаленной машине. Подробнее про удаленное подключение и безопасность WMI в документации на MSDN тут и тут
Идея взята с pinvoke.net, там вообще много интересного по использованию WinAPI под .NET

Большая задержка(пауза) в Java

Делаю Java приложения. Возникла необходимость при выполнении определенных условий делать паузу выполнения программы на длительное время(час или даже больше). Код выполняется отдельном потоке. На сколько правильно будет использовать для такой большой паузы
Thread.sleep(*очень большое число*)
Может быть есть другой, более правильный способ?


Ответ

Правильнее будет воспользоваться таймером или ScheduledExecutor(ом) например вот так:
Timer t = new Timer(); t.scheduleAtFixedRate(new TimerTask() { public void run() { System.out.println("do task"); } }, 0, 100);

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleWithFixedDelay( () -> { System.out.println("do task"); }, 0, 1, TimeUnit.HOURS);

Пользовательские элементы: Custom Elements

Как создать свой пользовательский элемент с рабочим функционалом?
Например хочу чтоб при добавке
0
в HTML работал секундомер.
Как можно реализовать это? Ведь в angular.js таким образом по моему работают директивы.Спасибо


Ответ

это делается с помощью document.registerElement и object.create(HTMLElement.prototype)

0
Пользовательские элементы: Custom Elements

Выборка по наличию слова jquery

Как можно выбрать все элементы у которых текст начинается на "Гл." ? а что после этого текста неизвестно


Ответ

Можно использовать код такого вида:
$('p').each(function(){ if(/^Гл\./.test($(this).text())){ //необходимые действия над элементом } })
Разумеется, вместо селектора "p" вам нужно использовать свой селектор, описывающий общность элементов, среди которых ищем.
Стоит заметить, что здесь мы получим не младший элемент иерархии, в котором непосредственно содержится текст, а "p" (в нашем случае), внутри которого (напрямую или обернутым в любое количество других элементов) текстовое содержимое будет начинаться с "Гл.".

Регулярные выражения. Флаг /u. Unicode

/hell\u{6F}/u.test('hello'); // -> true /hell\u{6F}/.test('hello'); // -> false
Объясните специфику работы флага /u в регулярных выражениях.


Ответ

Все просто как топор:
С этим флагом регулярка понимает выражения типа \u{6f} внутри регулярки, как юникод символ, без него - понимает как строку из последовательных символов u,{,6,f,}
Главная специфика тут одна: это фича появившаяся в ecmascript-6 стандарте, то есть без предварительной компиляции в ecmascript-5 код ее поймут не все браузеры.
Есть еще один небольшой нюанс: Пусть x - это не эскейп символ, тогда это синтаксически валидная регулярка:
var c = /\x/
А это - нет:
var c = /\x/u

сложный запрос в базу mysql [дубликат]

На данный вопрос уже ответили: Как соединить две таблицы БД 1 ответ здравствуйте, есть две таблицы:
`oc_vendor` ( `vendor_id` int(11) NOT NULL AUTO_INCREMENT, `vproduct_id` int(11) NOT NULL, `ori_country` varchar(128) COLLATE utf8_bin NOT NULL, `product_cost` decimal(15,4) NOT NULL DEFAULT '0.0000', `shipping_method` int(2) NOT NULL DEFAULT '0', `prefered_shipping` int(2) NOT NULL DEFAULT '0', `shipping_cost` decimal(15,4) NOT NULL DEFAULT '0.0000', `vtotal` decimal(15,4) NOT NULL DEFAULT '0.0000', `product_url` text COLLATE utf8_bin NOT NULL, `vendor` int(11) NOT NULL, `wholesale` varchar(128) COLLATE utf8_bin NOT NULL, `date_add` datetime NOT NULL,
и вторая:
`oc_vendors` ( `vendor_id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `vendor_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `commission_id` int(11) NOT NULL, `product_limit_id` int(11) NOT NULL, `company` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `company_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `vendor_description` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `telephone` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `fax` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `paypal_email` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `iban` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `bank_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `bank_address` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `swift_bic` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `tax_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `accept_paypal` tinyint(2) NOT NULL, `accept_cheques` tinyint(2) NOT NULL, `accept_bank_transfer` tinyint(2) NOT NULL, `store_url` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `vendor_image` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `firstname` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `lastname` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `address_1` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `address_2` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `city` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `postcode` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `country_id` int(11) NOT NULL, `zone_id` int(11) NOT NULL, `sort_order` int(11) NOT NULL, `date_add` datetime NOT NULL,
можно ли одним сложным запросом сделать выборку из этих двух, если можно то как??? а выбрать нужно vendor_name, vendor_description, vendor_image, firstname, lastname from oc_vendors where vproduct_id = '.$product_id где $product_id - id продукта в таблице oc_vendor ???


Ответ

На самом деле все оказалось проще чем я думал. Запрос выглядит так:
Select vendor_name, firstname, lastname, ... From vendors, vendor where vendors.vendor_id=vendor.vendor and vendor.vproduct_id=product_id
Всем спасибо )

Свойства объекта MouseEvent

В чем отличие этих свойств объекта MouseEvent?
clientX/Y layerX/Y pageX/Y screenX/Y И просто X/Y? Только что еще offsetX/Y заметил.
Большинство из них одинаково. И отличие screenX/Y от остальных понятно.


Ответ

Координаты курсора мыши относительно окна. Относительно ближайшего слоя-родителя. Если элемент, который вызвал событие является слоем (то есть имеет значение свойства position отличное от static), то координата будет высчитываться относительно этого элемента. Относительно документа. Координаты относительно монитора. Псевдоним clientX/Y Показывает отступ курсора мыши по оси X/Y от края целевого DOM узла.

Нестандартный вид вывода таблицы в php из MySql

Доброго времени суток, прошу помощи, ситуация такая: есть таблица в MySql:
задача: вывести ее на страницу в виде:
шапку таблицы создавал следующим образом:
connect_errno) { printf("Соединение не удалось: %s
", $mysqli->connect_error); exit(); } $sqltableclandate = "SELECT * FROM `nametable` GROUP BY Date"; $sqltableclanResultdate = $mysqli->query($sqltableclandate); while($tableclandate = $sqltableclanResultdate->fetch_assoc()) {?>
А вот дальше затык....
Подскажите куда копать и где искать)
Забыл вчера добавить: Добавление: если за какую-то дату нет информации, то в ячейке должен быть 0 или Н/Д
Добавление всего кода таблицы на данный момент:

Статистика:

query($query_dateclan)){ $rowclandate=$resultclandate->fetch_assoc(); $kol_rowsclandate = $resultclandate->num_rows; for($i=0; $i<$kol_rowsclandate; $i++){?>
Дата


вид базы: таблица: stat (как видно name5 появился позже)


Ответ

Можно воспользоваться функцией GROUP_CONCAT(), которая позволяет вывести списки каждой из групп, получаемых GROUP BY. Обычно в GROUP_CONCAT() передают имя столбца, но тут потребуется два значения - дата и gold. Их можно объединить при помощи CONCAT() каким-нибудь уникальным разделителем, например, решеткой.
SELECT GROUP_CONCAT(CONCAT(data, "#", gold)) AS data_gold FROM gold GROUP BY name +-------------------------------------------+ | data_gold | +-------------------------------------------+ | 2016-08-27#10,2016-08-28#15,2016-08-29#20 | | 2016-08-27#20,2016-08-28#25,2016-08-29#30 | | 2016-08-27#30,2016-08-28#35,2016-08-29#40 | | 2016-08-27#40,2016-08-28#45,2016-08-29#50 | +-------------------------------------------+
В PHP-коде можно сначала разбить строку по запятой (например, функцией explode()), а потом каждый элемент полученного массива еще раз разбить по #. GROUP_CONCAT допускает сортировку, а по полученной дате вы всегда сможете определить к какой ячейке должен относиться текущей элемент.
Для формирования результирующей таблицы можно воспользоваться следующим PHP-кодом:
PDO::ERRMODE_EXCEPTION));
function parse_gold($str) { $elements = explode(',', $str); $arr = array(); foreach($elements as $el) { list($date, $gold) = explode('#', $el); $arr[$date] = $gold; } return $arr; }
$query = "SELECT name, GROUP_CONCAT(CONCAT(data, '#', gold)) AS data_gold FROM gold GROUP BY name"; $usr = $pdo->query($query);
$users = array(); while($user = $usr->fetch()) { $users[$user['name']] = parse_gold($user['data_gold']); } $dates = array(); foreach($users as $user) { $dates += array_keys($user); } echo '

'; // Шапка echo ''; foreach($dates as $date) { echo ""; } echo ''; // Содержимое таблицы foreach($users as $name => $golds) { echo ''; echo ""; foreach($dates as $date) { if(array_key_exists($date, $golds)) { echo ""; } else { echo ""; } } echo ''; } echo '
Имя$date
$name{$golds[$date]}-
'; } catch (PDOException $e) { echo "Ошибка выполнения запроса: " . $e->getMessage(); }

Как определить тип устройства?

Надо чтобы приложение переходило в полноэкранный режим только на телефонах. Пробовал только так.
ApplicationView view = ApplicationView.GetForCurrentView(); view.TryEnterFullScreenMode();
Но, логично, так в полный экран будет переходить и при запуске на PC и на телефоне. А отслеживать размер экрана на котором запускается приложение, как по мне - криво... Может есть какой нибудь более простой способ указать, что на телефонах должен быть полный экран?


Ответ

Проверить тип устройства, на котором запущено приложение, можно так:
var platform = Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily; if (platform == "Windows.Mobile") { ... }
Другие возможные значения:
Windows.Desktop (если UIViewSettings.GetForCurrentView().UserInteractionMode == UserInteractionMode.Mouse, то десктоп, иначе планшет) Windows.Universal (это IoT устройство) Windows.Team (это surface hub)
Правда Microsoft не рекомендует использовать это свойство. Официальная политика такова, что вам не должно хотеться кастомизировать приложение в зависимости от платформы.

Вытащить конкретные данные из JSON (php) (мало кода)

Всем доброго времени суток. Учусь работать с API. В процессе мне пришли данные формата JSON, с помощью
$inv = json_decode($url);
echo "

";
    print_r($inv);
    echo "
";
Я получил данные вот в таком формате:

Все эти данные мне не нужны, а нужны только выборочные. Можно ли как-то вывести на экран только [id] или например только [classid] ? И если можно, то как, подскажите ?
Всё что пытался делать сам, выдавало только ошибки ...


Ответ

// 2й параметр == true => "возвращать как массив" / false => "как объект" $inv = json_decode($url, true);
foreach($inv['rgInventory'] as $key => $item) { var_dump($key, $item['id'], $item['classid']); }
Справка
foreach json_decode

Как лучше работать со строковым файлом в Java

Вопрос в том, что есть файл(обычный .txt), в этом файл имеет вид 10 строк и два столбца в каждом столбце номера. Необходимо как то работать с эти файлом, а то есть считать данные и по номеру из первого столбца выводить данные со второго столбца. Как это сделать, использовать базу данных или sharedPreference или массивы? Подскажите пожалуйста. Вид файла который надо считать имеет вид:
000000001 0002 000000002 09809 000000003 0002 000000004 09809 и т.д.


Ответ

Зачем все так усложнять?
final File data = new File("file.txt"); LineNumberReader lnr = new LineNumberReader(new BufferedReader(new FileReader(data))); String line; while((line = lnr.readLine()) != null) { final String args[] = line.split("\t", -1); //parse params args[0], args[1] int id_1 = Integer.parseInt(args[0]); int id_2 = Integer.parseInt(args[1]); }

Как в несколько потоков прочитать ОГРОМНЫЙ (скажем ~25 ГБ) xml файл?

структура xml довольно простая...
Но размер файла довольно большой....
Хочется читать файл в несколько параллельных потоков...
Как в несколько потоков прочитать ОГРОМНЫЙ xml файл?
какой парсер вы бы посоветовали ?
UPD:
Файл имеет не сложную структуру... сильной вложенности нет...
Кусочек файла:
... ... ... ... ... ...
К сожалению, этот xml формируется не в нашей компании (у клиентов)...
Доступа к базам данных(4 разных типа БД) на стороне клиента у нас нет, и быть не может, по соображениям политики безопасности в области информации и чего-то там(одним словом - гос.контора )...
У заказчика нет специалистов и желания переписывать свой софт, который собирает данные из таблиц разных БД и формирует этот xml.


Ответ

Файлы формата XML, как и любых других LL(n)-подобных грамматик, невозможно читать в несколько потоков. Максимум что вы можете сделать - это сразу после получения данных передавать их в другой поток на обработку, чтобы 1 поток всегда был занят парсингом.
К примеру, если на разбор файла уходит половина времени, и еще половина - на запись в БД, то вынесение работы с БД в другой поток ускорит процесс в два раза.
Здесь вам может пригодиться класс java.util.concurrent.BlockingQueue и паттерн "производитель-потребитель" (Producer-Consumer)

Открыть файл в Javascript

Я передаю табличные данные из html в excel с помощью tableExport. Выглядит это так:
doExport = function () { _this.$el.tableExport($.extend({}, _this.options.exportOptions, { type: type, // выбранный тип экспорта })); };
Получается файл с дефолтным именем tableExport.xls, который создаётся в дефолтной папке для загрузок, записывается, сохраняется и закрывается.
Понадобилось добавить после таблицы график. tableExport для этого не предназначен. Итого вопрос - как после выполнения tableExport мне обратиться к этому файлу? Его имя остаётся дефолтным - но может добавляться порядковый номер. То есть имя я точно не буду знать. Понимаю, что есть способ как-то запомнить путь и имя файла, но куда копать?


Ответ

Вы не можете с помощью JS обратиться к файлу из файловой системы пользователя. Вам нужно как то добавить ваши данные до сохранения файла. Вероятно для этого придется разбираться в коде библиотеки и возможно модифицировать ее. Думаю лучшим решением будет сохранить график в отдельном файле.

Как правильно хранить сессию при разработке JSON API?

Обычно мы храним id сессии в cockie браузера. Но допустим я хочу использовать свое API в мобильных приложениях ios/android, и я собираюсь хранить куку в памяти приложения и вставлять потом в каждый запрос с приложения. Но на сколько это правильно? Есть ли какие-то советы по этому поводу?


Ответ

Но на сколько это правильно?
Тут просто нет другого подхода. Вам так или иначе надо идентифицировать пользователя API. По каким-либо косвенным признакам вы его идентифицировать не можете - на одном IP могут сидеть несколько потребителей, User-Agent нет в принципе, да и вообще это просто подменяемый заголовок и не более.
Поэтому вам надо с каждым запросом передавать некий идентификатор, который сможет помочь вам найти пользователя. Тут есть несколько тонких моментов:
Это не должен быть сам идентификатор пользователя (иначе весь механизм ломается через угон идентификатора, и с угнанным пользователем нельзя сделать ничего, кроме его убийства) Этот идентификатор (или механизм, связанный с ним) должен однозначно подтверждать корректность сессии либо передаваться исключительно по защищенному каналу (HTTPS), иначе его можно будет ровно так же угнать и представляться некорректным пользователем. Идентификаторы должны считаться расходным материалом в приложении: их всегда можно грохнуть и насоздавать новых, чтобы любой угнанный идентификатор мог быть обнулен сразу после обнаружения угона
Общепринятым подходом считается передаче токена доступа в произвольном заголовке (github, google, amazon s3). Так или иначе информация для аутентификации всегда остается в заголовках (в query parameters и непосредственно теле запроса ей не место), поэтому здесь на самом деле тоже особого выбора нет. Токен хранится на устройстве, в случае паники пользователь удаляет токен (пользуясь тем же токеном доступа)
Последний абзац - про подтверждение корректности сессии, передаваемое по незащищенному каналу. В случае, если есть опасения за перехват токена при его передаче - необходимо так или иначе подписывать исходящие запросы. Для этого можно использовать полновесную или легковесную challenge-response аутентификацию, смысл которой можно свести к тому, что сервер и клиент загадывают друг другу загадки, которые могут разгадать только они сами. В самом простом варианте сервер может выписывать токен, состоящий из идентификатора и секретной части (ключа шифрования), которая однократно передается клиенту при создании токена (клиент тоже может при этом переать некий секрет, но мы считаем, что серверу всегда можно доверять). После этого все сообщения от клиента к серверу либо полностью шифруются полученным ключом, либо содержат в дополнительном заголовке сформированную на основе ключа и тела запроса подпись, которую злоумышленник не сможет подделать, не своровав секретный ключ. Это, однако, не защищает само по себе от:
Replay-атак (e.g. если пользователь шлет запрос "удалить последнюю запись", то злоумышленник может сгенерировать еще сотню таких запросов и полностью очистить историю пользователя). Самое простое решение - жонглирование временными метками (e.g. не принимать запросы старше 5 секунд), но при малейших проблемах с синхронизацией стреляет в ногу. Физического изъятия ключа у клиента. Обычно считается, что от этого нельзя защититься, и этот момент просто игнорируется. Перехвата ключа в момент выписывания токена (поэтому лучше все-таки обзавестись HTTPS). При открытом трафике можно воспользоваться алгоритмом Диффи-Хеллмана и аналогами, но это займет времени больше, чем написание самого приложения. Вычисления ключа при использовании некорректного алгоритма (e.g. если не происходит ротации ключа и если злоумышленник знает, что всегда используется JSON, то он, зная, что последний символ всегда является } или ], может вычислить ключ, сравнивая запросы разной длины). Опять же, проще взять HTTPS, есть альтернативные методы, но я не буду их рассказывать, чтобы ненароком не пропустить важный момент.
upd. есть некий стандарт json web token, сам я с ним пока не ознакомился.

передача List в другую активити

Как передать Listlist= new ArrayList<>() в другую активити? Ну или в ViewPager?


Ответ

Вам надо поместить ваш список сторок в Intent, коий вы запускаете вторую активити с помощью метода Intent#putStringArrayListExtra(String key, ArrayList list). Потом, во второй активити вам надо по ключу вытащить этот список из интенета.
В первой активити:
ArrayList ar = new ArrayList(); ar.add("Apple"); ar.add("Banana");
Intent i = new Intent(this, SecondActivityClassName.class); i.putStringArrayListExtra("list", ar); startActivity(i);
Во второй:
ArrayList ar1 = getIntent().getExtras().getStringArrayList("list");

Корректность и время работы линейного алгоритма разбиения массива “на месте” на три части: элементы меньше, равные и больше опорного

Было задание реализовать алгоритм, который делил бы "на месте" массив на три условных части. Элементы меньше v, равные v, больше v. При этом не важно, в какой последовательности между собой будут элементы в левой и правой частях. Время работы должно быть O(n).
Задание для меня оказалось не таким уж и простым. В итоге получился следующий код:
public static void buildArray(int v, int[] array) { int length=array.length; System.out.println("Сортируем по элементу: " + array[v]);
swap(array, 0, v); v=0; int position=0; for(int i=1; i < length; i++) { if(array[i] < array[v]) { swap(array, v+1, i); swap(array, v-position, v+1); v=v+1; } else if(array[i] == array[v]) { swap(array, v+1, i); position=position+1; v=v+1; } } }
Суть решения лежит в том чтобы выбранный нами элемент v поместить в начало массива и ко всем оставшимся элементам применить сравнение.
Если текущий элемент ниже array[v], значит поместить его сзади выбранного. Если оба (array[i] и array[v]) равны, значит поместить их рядом, при этом все элементы попадающие под первое сравнение будут перемещаться в область (v-position), где position инкрементируется от количества одинаковых v-элементов.
Тут два вопроса:
Корректен ли алгоритм? Предыдущие варианты решения могли сработать на 10 разных входных значениях, но были неисправными на 11, поэтому вопрос не лишен смысла. Равняется ли время работы O(n). Мы проходим по массиву один раз. Хватает ли этого чтобы утверждать оценку в O(n)?


Ответ

Алгоритм, судя по всему, правильный.
Доказательство. Инвариант: элементы с индексами 0 <= индекс < v - position из куска массива вплоть до i меньше разделяющего значения, элементы с индексами v - position <= индекс <= v равны ему. При этом v - position — индекс первого значения, равного разделяющему, а v индекс последнего (из рассмотренных).
В начале инвариант выполняется.
Если новый элемент меньше разделяющего значения, выполняется первая ветка, этот элемент перемещается на место сразу за группой [v - position .. v], а затем меняется с начальным элементом группы. В результате этот элемент оказывается перед группой, а группа «смещается» на один элемент вперёд. Продвигается v с сохранением инварианта.
Если новый элемент равен разделяющему значению, выполняется вторая ветка, этот элемент перемещается на место сразу за группой [v - position .. v], и эта группа увеличивается на 1 элемент (position=position+1; v=v+1;), включая новый элемент.
Если новый элемент больше разделяющего значения, инвариант также не нарушается.
В конце цикла нерассмотренных элементов не осталось, и согласно нашему инварианту, элементы массива обработаны правильно.

Каждая итерация цикла O(1), т. к. выполняется за ограниченной количество операций. Количество итераций O(n). Итого сложность алгоритма таки O(n)

Как выбирать действия в адаптере в зависимости от активити

у меня один адаптер и два активити. И хочу чтоб в каждом из этих активити отображались разные переходы по клику на item.
itemHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(context, EventActivity.class); intent.putExtra("event_id", item.event.getId()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { context.startActivity(intent); ((Activity)context).overridePendingTransition(R.anim.slide_in_from_right, R.anim.push_to_back); } else {
Pair p1 = Pair.create((View) itemHolder.mTitleView, "event_title"); Pair p2 = Pair.create((View) itemHolder.mInterestViewColor, "event_interest_bg"); Pair p3 = Pair.create((View) itemHolder.mImageView, "ivent_image"); Pair p4 = Pair.create((View) itemHolder.mInterestTitle, "event_interest_name"); ActivityOptionsCompat options = ActivityOptionsCompat. makeSceneTransitionAnimation((Activity) context, p1, p2, p3, p4); context.startActivity(intent, options.toBundle()); } } });
Как сделать выбор, если он находится в активити1, то по нажатию на item, переходит на одну страницу, если находится в активти2, то по нажатию на этот Же item, но в другую страницу.
Item это отдельный layout, для recyclerview.


Ответ

В данном случае проще и архитектурно правильно прокинуть интерфейс из адаптера и регистрировать его в вызывающей активити, чем вешать действия слушателей прямо в адаптере. Данный подход позволит реализовать любую уникальную логику по клику в каждой вызывающей активити, без всяких костылей и нагромождения вариантов переходов в кликере адаптера. Примерный код (исключены все методы не относящиеся к реализации):
class SomeAdapter extends RecyclerView.Adapter {
interface OnCardClickListener { void onCardClick(View view, int position, boolean isValid); }
private static OnCardClickListener mListener;
public void setOnCardClickListener(OnCardClickListener listener) { mListener = listener; }
public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
CardView mCard;
public ItemHolder(View v) { super(v);
mCard = (CardView) v.findViewById(R.id.bell_card); mCard.setOnClickListener(this); }
@Override public void onClick(View v) { int position = getAdapterPosition(); boolean isValid = (position != RecyclerView.NO_POSITION); mListener.onCardClick(v, position, isValid); } } }
Далее в активити используем так:
public class SomeActivity extends Activity implements SomeAdapter.OnCardClickListener {
RecyclerView mList; SomeAdapter mAdapter;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
mList = (RecyclerView) findViewById(R.id.list); mList.setLayoutManager(new LinearLayoutManager(this)); mAdapter = new SomeAdapter(mData); mAdapter.setOnCardClickListener(this); mList.setAdapter(mAdapter);
}
@Override public void onCardClick(View view,final int position, boolean isValid) {
if (isValid) { switch (view.getId()) { case R.id.button1: return true; default: return false; } } }
}
В каждой активити может быть своя реализация действий в переопределенном методе onCardClick(). Колбэк передает в активити View, по которому кликнули, позицию в адаптере и признак валидности этой позиции. Вы можете передавать из адаптера и другие произвольные данные, которые вам могут требоваться.

Вывести число которое повторяется наибольшее количество раз

Получил задание: вывести цифру, которая повторяется в числе подряд наибольшее количество раз.
Однако возникла одна проблема. К примеру возьмем такое число - 122233. По идее должно вывестись сообщение "||2||3||". То есть цифра два повторяется три раза. Однако выводится следующее "||3||3||". Значение переменной max сохраняется и все отлично, но значение перемнной s_1 равняется последнему числу. Я пробовал делать еще одну проверку в else, типа:
if(same != max) { delete s_1; }
Но не работало. Помогите пожалуйста, вот код:
function same_numbers() { var number = document.getElementById('number').value; var replaced; var same; var s_1; var arr = []; while (number.length) { s_1 = number[0]; replaced = number.split(s_1).join(''); same = number.length - replaced.length; number = replaced; if (same == 1) { delete s_1; } else { (arr.push(same)); var max = Math.max.apply(Math, arr); }
} alert( "||" + s_1 + "||" + max + "||" );
}


Ответ

В этом моменте у вас происходит что то не понятное
if (same == 1) { delete s_1; } else { (arr.push(same)); var max = Math.max.apply(Math, arr); }
Если вам не важно какую именно цифру выводить в случае когда несколько максимумов, то можно считать так.
function same_numbers() { var number = document.getElementById('number').value; var replaced; var same; var s_1; var arr = []; var digits = {}; while (number.length) { s_1 = number[0]; replaced = number.split(s_1).join(''); same = number.length - replaced.length; number = replaced; digits[s_1] = same; } var max_key = null; for(var key in digits) { if(max_key === null || digits[key] > digits[max_key]) { max_key = key; } } alert( "||" + max_key + "||" + digits[max_key] + "||" ); }

Что означает модификатор [In, Out] в C#?

Смотрел исходники и обнаружил, что один из аргументов метода Read класса FileStream имеет модификатор [In, Out] .
[System.Security.SecuritySafeCritical] // auto-generated public override int Read([In, Out] byte[] array, int offset, int count) { }
Что означает этот модификатор?
Если верить MSDN, то он используется для взаимодействия с COM, но вроде COM взаимодействия в данном методе нет.


Ответ

OutAttribute - показывает, что требуется маршалинг данных из вызываемого объекта в вызывающий.

InAttribute - показывает, маршалинг каких данных необходимо выполнить при передаче от вызывающего объекта — вызываемому, но не обратно.
Данные атрибуты могут быть применены к параметрам. Аргументы InAttribute и OutAttribute являются необязательными. Эти атрибуты поддерживается только для COM-взаимодействия и вызова неуправляемого кода.
Подробнее можно прочитать на MSDN:
OutAttribute - класс InAttribute - класс
Обратите внимание на Заметки и Примеры, перейдя по ссылкам.

mysql поиск дубликатов

Подскажите, как в MySQL можно организовать поиск дубликатов в поле? Вывести только неповторяющиеся значения можно с помощью DISTINCT, а вот как можно вывести только повторяющиеся значения?


Ответ

При помощи конструкции HAVING()
SELECT fld, COUNT(*) FROM mytable GROUP BY fld HAVING(*) > 1

Обработка событий

Все чаще вижу, что обработчики событий вешают не на сам элемент, который вызвал событие, и даже не на родителя, а на объект window. Получается так, что все обработчики находятся на window и работают через механизм делегации. Естественно, за исключением тех, которые не умеют всплывать.
Хотелось бы узнать как производительнее и легче браузеру? Или хотя бы как можно это правильно протестировать?
Давайте считать, что у нас на странице много (100-500) обработчиков и сама страница это лес из div'ов. Мне кажется, за счет того, что между самим событием и его всплытием до window происходит некоторое время + время расходуется на выбор правильного делегата. Это может влиять на производительность. Помогите разобраться или это я бешусь с жиру и всё равно как делать на самом деле.


Ответ

Браузеру производительней обрабатывать события на месте. Имхо не так много причин за вешать обработчик на window, зато куча против.
Событию нужно всплыть до window, его могут перехватить и отменить Обработчики будут возбуждаться на все события данного типа и проверять не возникло ли оно на нужном элементе и нужно ли его обработать Обработка будет откладываться пока событие всплывает и обрабатывается более конкретными обработчиками Нельзя будет выстроить порядок обработки повесив дополнительные обработчики на родительский элемент чтобы они выполнились после

RecyclerView - динамичное добавление header

Мне приходит JSON массив с сервера с датой и количеством сообщений. Мне нужно сделать список в котором дата будет находиться перед началом сообщений этой даты. Никак не пойму как можно научить адаптер строить такой список.
Помогите, покажите пальцем, куда копать?
Есть примерный способ реализации:
Завести массив с датами сообщений в формате HH:mm ???


Ответ

Организация данных такая, что нужно распарсить JSON в объекты модели, каждая модель будет содержать поле даты и поле записи. Коллекцию необходимо отсортировать по возрастанию даты.
Для начала нам понадобится два типа разметок. Первая будет отображать дату и запись (layout.header), вторая - только запись (layout.record). Разметка layout.header включает в себя разметку layout.record через include
layout.record:



layout.header:



Логика определения, какой тип View выводить основана на сравнении даты текущей записи и прошлой - если они не равны, то выводить заголовок с датой и записью, иначе только запись. Нулевой элемент обрабатывается отдельно и всегда содержит дату и запись.
Так же прошу обратить внимание на организацию блока switch - case в методе onBindViewHolder(). При выводе View с датой case выводит дату на разметку и "проваливается" на следующий case (нет оператора break), где на разметку выводится запись. Если заголовок не требуется, то на разметку дата не выводится (срабатывает второй case)
class SomeAdapter extends RecyclerView.Adapter {
private ArrayList mData; private final int TYPE_HEADER = 0; private final int TYPE_ITEM = 1;
public SomeAdapter (ArrayList data) { mData = data; }
@Override public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v;
switch (viewType) {
case TYPE_HEADER: v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false); break; default: v = LayoutInflater.from(parent.getContext()).inflate(R.layout.record, parent, false); } return new ItemHolder(v); }
@Override public void onBindViewHolder( ItemHolder holder, int position) {
int type = getItemViewType(position);
switch (type) {
case TYPE_HEADER: holder.mHeaderDate.setText(mData.get(position).getDate()));
case TYPE_ITEM: holder.mItemRecord.setText(mData.get(position).getRecord());
break; } }
@Override public int getItemCount() { return mData.size(); }
@Override public int getItemViewType(int position) {
if (isIdentType(position)) return TYPE_ITEM; return TYPE_HEADER; }
private boolean isIdentType (int position ){
if (!(position == 0)&&(mData.get(position).getDate()).equals(mData.get(position-1).getDate())) return true; return false; }
public static class ItemHolder extends RecyclerView.ViewHolder{
TextView mHeaderDate; TextView mItemRecord;
public ItemHolder(View v) { super(v);
mHeaderDate = (TextView) v.findViewById(R.id.date); mItemRecord = (TextView) v.findViewById(R.id.record); }
}
Естественно, разметки нужно оформить покрасивее как то, выделить дату можно другим фоном или другое. Данный пример - только идея.

Первичный ключ в таблице

Повышает ли наличие первичного ключа производительность работы с таблицей, если он не участвует в условии отбора?
Если не ошибаюсь, то наличие первичного ключа задает способ хранения строк.
Если первичного ключа нет, то все хранится в куче. Так же есть некоторые особенности в использовании некластиризированных индексов на куче и без кучи.


Ответ

Сам по себе первичный ключ служит для однозначной идентификации строк в таблице, а также для ссылок на строки данной таблицы из других таблиц.
Другое дело, что в SqlServer первичный ключ по умолчанию является кластерным индексом. То есть в определении таблицы
create table TableName (ID int primary key, ... );
primary key эквивалентно primary key clustered. При желании, однако, вполне можно сделать первичный ключ и некластерным индексом, указав primary key nonclustered
Если не ошибаюсь, то наличие первичного ключа задает способ хранения строк. Если первичного ключа нет, то все хранится в куче.
Правильнее будет сформулировать - наличие или отсутствие кластерного индекса (не обязательно, чтобы он был первичным ключом).
Если на таблице нет кластерного индекса, то данные таблицы хранятся в куче без какого либо определённого порядка.
Если же на таблице есть кластерный индекс, то в нём и содержатся данные таблицы. Кластерный индекс представляет собой "B+"-дерево, в страницах которого лежат ключи в логически упорядоченном виде. В дополнение к ключам в страницах верхних уровней лежат ссылки на страницы следующего уровня, а в страницах нижнего уровня ("листьях") - данные неключевых столбцов.

Повышает ли наличие первичного ключа производительность работы с таблицей, если он не участвует в условии отбора?
Может повышать.
Сканирование кластерного первичного ключа (вообще кластерного индекса) может быть более производительным по сравнению со сканированием кучи, т.к. в кластерном индексе, в отличие от кучи, при изменении данных не создаются переадресованные записи (т.н. forwarded records).
Переадресованная запись - это такая запись, которая, увеличившись в размере при обновлении, уже не может помещаться на той странице данных, где она располагалась. В этом случае запись перемещается на другую страницу данных, а на её месте остаётся указатель. Из-за таких указателей увеличивается число чтений, при доступе к данным.
Пример.
Создадим две почти одинаковых таблицы. Одну с некластерным первичным ключом:
create table Heap ( ID int, Name varchar(50), Comments varchar(1000), constraint PK_Heap primary key nonclustered (ID) );
Другую, с такими же столбцами, но с кластерным первичным ключом:
create table Cluster ( ID int, Name varchar(50), Comments varchar(1000), constraint PK_Cluster primary key clustered (ID) );
Добавим в таблицы данных:
;with nums as ( select N = row_number() over (order by @@spid) from sys.all_columns a cross join sys.all_columns b ) insert into Cluster (ID, Name, Comments) select top (10000) N, 'Name ' + cast((N - 1) % 1000 + 1 as varchar(10)), 'Comments' from nums order by N;
insert into Heap (ID, Name, Comments) select ID, Name, Comments from Cluster;
Включив statistics io, выполним одинаковый запрос к каждой из таблиц - такой, чтобы он вызывал их полное сканирование:
set statistics io on; declare @cnt int; select @cnt = count(1) from Heap where Name = 'Name 15'; select @cnt = count(1) from Cluster where Name = 'Name 15'; set statistics io off;
Статистика показывает, что число чтений пока примерно одинаково:
Table 'Heap'. Scan count 1, logical reads 61 ... Table 'Cluster'. Scan count 1, logical reads 63 ...
Изменим данные в таблицах (что вызовет появление переадресованных записей в таблице-куче):
update Heap set Comments = replicate('More ', 50) + Comments update Cluster set Comments = replicate('More ', 50) + Comments
И повторим запросы с count и статистикой к таблицам:
Table 'Heap'. Scan count 1, logical reads 9139 ... Table 'Cluster'. Scan count 1, logical reads 680 ...
Как видим, хоть данные одни и те же, и изменялись одинаково, и запросы - одинаковые, однако, число чтений, необходимое для сканировании кучи из-за появившихся переадресованных записей выросло на порядок по сравнению с числом чтений, необходимым для сканирования кластерного индекса.

Как реализовать блок, чтобы при прокрутке залипал у нижней границы окна браузера?

Как сделать, что бы при прокрутке блок прижимался к краю браузера, при этом блок находится далеко в подвале? Вот скролируемый блок Вот он в подвале Когда до скролировали до этого блока в подвале, скролируемый блок садится на место где он должен и быть.


Ответ

Например так:
$(window).scroll(function(){ if ($(this).scrollTop() > 100) { $('.arr').fadeIn(); } else{ $('.arr').fadeOut(); } }); $(document).scroll(function() { if ($(window).scrollTop() == $(document).height() - $(window).height()) { $('.arr').addClass('fix'); } else { $('.arr').removeClass('fix'); } }); .footer{ position: relative; } /* Стрелка изначальная */ .arr { display: none; position: fixed; bottom: 0; left: 0; right: 0; margin: 0 auto; } /* Стрелка при скролле до низа страницы */ .arr.fix { position: absolute; top: -50px; bottom: auto; } /* Стили для наглядности */ .arr { width: 0; height: 0; border-left: 25px solid transparent; border-right: 25px solid transparent; border-bottom: 50px solid #000; transition: all .27s ease-in-out; } .content { height: 1500px; border: 1px solid #ccc; } .footer { background: #333; text-align: center; padding: 1rem; color: #fff; }

Некоторый основной контент
Тут подвал

Многопоточность и параллельность при запуске внешнего EXE несколько раз

Добрый день, уважаемое сообщество.
Прошу провести code review и дать совет. Есть программа. Ей надо запустить один и тот же внешний EXE файл N раз с разными входными параметрами. Дождаться окончания выполнения всех запущенных процессов и собрать результаты в единый, допустим, массив.
Как использовать параллелизм и асинхронность (если последняя тут нужна) с максимальной эффективностью?
Я пока сделала такое решение:
List> processignDataList = ... //список кортежей с входными данными Task[] stackTasks = new Task[processignDataList.Count]; // массив всех тасков List> tasksResults = new List>(); //список кортежей с результатами отработки тасков int i = 0; // счетчик, нужен только для указания элемента массива
foreach (Tuple pair in processignDataList) { stackTasks[i] = Task.Factory.StartNew(() => tasksResults.Add(Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))); i++; } Task.WaitAll(stackTasks);
Метод string pairProcessing()
берет свойства из специального объекта cSettings, входные параметры из pair, запускает EXE как Process - process.Start(); там же находится ограничение от бесконечного зависания - process.WaitForExit(3000) потом проверка, что process.Close(); анализ StandardOutput, и на его основе формирование некой строки и ее возврат как результата работы метода pairProcessing
Достигаю ли я с таким решением запуска нескольких экземпляров EXE и параллельного их выполнения? Нужны ли какие-то дополнительные пометки методу pairProcessing(), есть к нему какие-то требования или он может быть любым? Знаете ли вы решения лучше? (а решение лучше есть всегда...) Есть ли косяки? Спасибо.


Ответ

Во-первых, у вас нет синхронизации доступа к tasksResults - тут может быть неприятная гонка.
Во-вторых, если процессов будет много - у вас могут закончиться потоки в пуле, что нежелательно ограничит параллелизм. Впрочем, слишком много процессов все равно не смогут работать параллельно.

Первая проблема решается довольно просто - вместо записи в список внутри задачи надо позволить задаче вернуть значение:
List>> stackTasks = new List>>();
foreach (Tuple pair in processignDataList) { stackTasks.Add(Task.Factory.StartNew(() => Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))); }
Tuple[] tasksResults = Task.WhenAll(stackTasks).Result;
Также можно воспользоваться linq:
Tuple[] tasksResults = Task.WhenAll( from pair in processignDataList select Tuple.Create(pair.Item1, pairProcessing(cSettings, pair))) ).Result;
или так:
Tuple[] tasksResults = Task.WhenAll( processignDataList.Select(pair => Tuple.Create(pair.Item1, pairProcessing(cSettings, pair)))) ).Result;

Для решения второй проблемы надо переходить от задач к потокам. Или увеличить размер пула потоков при помощи ThreadPool.SetMaxThreads и ThreadPool.SetMinThreads

Функторы и операторы в C++

Я не могу до конца понять, как работает operator() внутри класса и, в принципе, в чем его отличие от методов.
То есть в чем разница между
class ManOlderThan { int m_age; public: ManOlderThan( int age ) { m_age = age; }; bool operator()( Man &man ) { if( man.GetAge() > m_age ) { return true; } else { return false; } }; };
class ManOlderThan { int m_age; public: ManOlderThan( int age ) { m_age = age; }; bool compare( Man &man ) { if( man.GetAge() > m_age ) { return true; } else { return false; } }; };
В том, как потом это вызывать? Или оператор нужен, когда метод только один? Или это вообще разные вещи?
Вообще в данном случае оператор рассматривается в контексте векторов и for_each и find_if


Ответ

Многие алгоритмы используют функциональные объекты для обработки элементов последовательности. В качестве этих функциональных объектов могут выступать функции или классы, имеющие оператор-функцию. Синтаксис вызова для функций и классов - функциональных объектов выглядит идентично. Это позволяет писать обобщенный код. Преимущество классов - функциональных объектов состоит в том. что они позволяют хранить состояние, а также использовать дополнительные поля данных помимо аргументов вызова.
Посмотрите ниже демонстрационную программу, которая находит человека с возрастом больше заданного. Как видно из примера, использование функционального оператора-функции вместо вызова функции-члена класса значительно проще, а также функциональный объект может хранить дополнительную информацию - в данном случае значение 35.
#include #include #include
class ManOlderThan { int m_age; public: ManOlderThan( int age ) : m_age( age ) { }
int GetAge() const { return m_age; }
bool operator ()( const ManOlderThan &man ) const { return m_age < man.GetAge(); }
bool compare( const ManOlderThan &man ) const { return m_age < man.GetAge(); } };
int main() { std::vector v = { 20, 30, 40, 50, 60 };
auto man = std::find_if( v.begin(), v.end(), ManOlderThan( 35 ) );
std::cout << man->GetAge() << std::endl;
man = std::find_if( v.begin(), v.end(), []( const ManOlderThan &man ) { return ManOlderThan( 35 ).compare( man ); } );
std::cout << man->GetAge() << std::endl;
return 0; }
Вывод программы на консоль
40 40
То есть программа ищет первого человека в списке людей, заданного вектором, который старше 35 лет.

Как сохранить пропорции 2D объекта в окне рендера OpenGL?

Есть круг.

Но если изменить размеры окна(ресайз) например вот так:

или так:

То получается овал.
Вопрос: как сделать, чтобы при ресайзе, круг всегда оставался кругом?
Код:
#include "stdafx.h" #include #include
/* подключаем библиотеку GLUT */ #include
static GLfloat spin = 0.0;
void init(void) { //glClearColor() устанавливает черный цвет фона glClearColor(0.0, 0.0, 0.0, 0.0); //glShadeModel(GL_FLAT);////Режим без сглаживания glShadeModel(GL_SMOOTH); //Сглаживание. По умолчанию установлен режим GL_SMOOTH.
}
void display(void) { // glClear() очищает фон . //В дальнейшем, всякий раз, когда glClear() будет вызываться, //она будет очищать окно в черный цвет . glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); //glRotatef(Angle,Xtrue,Ytrue,Ztrue) отвечает за вращения объекта вдоль glRotatef(spin, 0.0, 0.0, 1.0);
//glColor3f() устанавливает цвет прорисовки - белый цвет. glColor3f(1.0, 1.0, 1.0); //glColor3f(1.0, 0.0, 0.0);//-красный.
#define PI 3.1415926535898 GLint circle_points = 25; //glBegin() и glEnd() определяют обьект, который будет прорисован . glBegin(GL_LINE_LOOP); for (int i = 0; i < circle_points; i++) { double angle = 2 * PI*i / circle_points; //glVertex2f() определяет вершины полигона, в качестве параметров - 2 координаты x, y. glVertex2f(cos(angle), sin(angle)); } glEnd();
glBegin(GL_LINE_LOOP); for (int i = 0; i < circle_points; i++) { double angle = 6 * PI*i / circle_points; glVertex2f(cos(angle), sin(angle)); } glEnd();
glBegin(GL_LINE_LOOP); for (int i = 0; i < circle_points; i++) { double angle = 20 * PI*i / circle_points; glVertex2f(cos(angle), sin(angle)); } glEnd();
glPopMatrix(); /*glutSwapBuffers(), делающий свопинг буффера . Имеется 2 буффера, и пока на экран не выводится полностью один из них, второй остается полностью за кадром, и не произойдет наложения одного надругой .*/ glutSwapBuffers(); }
void spinDisplay(void) { spin = spin + 0.05; if (spin > 360.0) spin = spin - 360.0; glutPostRedisplay(); }
void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); //glOrtho() определяет координатную систему . glOrtho(-2.0, 2.0, -2.0, 2.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
void mouse(int button, int state, int x, int y) { switch (button) { case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN) glutIdleFunc(spinDisplay); break; case GLUT_MIDDLE_BUTTON: if (state == GLUT_DOWN) glutIdleFunc(NULL); break; default: break; } }
/* * double buffer display mode. * Register mouse input callback functions */ int main(int argc, char** argv) { // glutInit(int *argc, char **argv) - самая первая команда инициализации glutInit(&argc, argv); //glutInitDisplayMode(unsigned int mode) - устанавливает цветовую модель - RGBA или color - index . //Например, glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) - //устанавливает двойной буфер, цветовую модель RGB . glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //glutInitWindowSize(int width, int size) - размер окна glutInitWindowSize(250, 250); //glutInitWindowPosition(int x, int y) - установливает начало координат окна. glutInitWindowPosition(100, 100); //int glutCreateWindow() - создает окно glutCreateWindow(argv[0]); init(); //glutDisplayFunc() - вызывается всякий раз при перерисовке окна . glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); //glutMainLoop(void) - эта функция вызывается после всех остальных . glutMainLoop(); return 0; }
UPDATE:
Добавил -
void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity();
double aspect = (double)w / h; //glOrtho() определяет координатную систему . glOrtho(-2.0 * aspect, 2.0 * aspect, -2.0, 2.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
Итого всё в порядке,но...
Так хорошо.

Так не хорошо.


Ответ

Вам нужно сделать изменение ширины/высоты в glOrtho при изменении размеров окна, чтобы сохранялись пропорции отрисовки. Попробуйте так:
double ww = w, hh = h; if (w > h) glOrtho(-2.0 * (ww/hh), 2.0 * (ww/hh), -2.0, 2.0, -1.0, 1.0); else glOrtho(-2.0, 2.0, -2.0 * (hh/ww), 2.0 * (hh/ww), -1.0, 1.0);
В книгах по OpenGL часто такой приём используется.

Что использовать абстрактный класс или интерфейс в фабричном методе?

Полностью осознаю, что это очередной вопрос из серии "Разница между абстрактным классом и интерфейсом". И должен сказать, что для меня эти две концепции ясны или возможно нет раз задаю этот вопрос :)
Наверное каждый второй, если не первый из девелоперов использует порождающий паттерн "Фабричный метод" и меня интересует вопрос, так какую абстракцию лучше использовать при реализация этого паттерна: абстрактный класс или интерфейс?
Туториалы в сети тоже не едины в этом: кто-то рассказывает этот паттерн при помощи абстрактного класса, а кто-то при помощи интерфесов. Хотя должен заметить, что большинство статей касаемо фабричного метода ведут свои рассуждения на абстрактных классах.
Вот пример из википедии, который рассказывает паттерн на
Абстрактных классах
namespace CSharpConsoleApp { abstract class AbstractProduct { public abstract string GetType(); }
class ConcreteProductA : AbstractProduct { public override string GetType() { return "ConcreteProductA"; } }
class ConcreteProductB : AbstractProduct { public override string GetType() { return "ConcreteProductB"; } }
abstract class AbstractCreator { public abstract AbstractProduct FactoryMethod(); }
class ConcreteCreatorA : AbstractCreator { public override AbstractProduct FactoryMethod() { return new ConcreteProductA(); } }
class ConcreteCreatorB : AbstractCreator { public override AbstractProduct FactoryMethod() { return new ConcreteProductB(); } } class Program { static void Main(string[] args) { AbstractCreator[] creators = { new ConcreteCreatorA(), new ConcreteCreatorB() }; foreach (AbstractCreator creator in creators) { AbstractProduct product = creator.FactoryMethod(); Console.WriteLine("Created {0}", product.GetType()); }
Console.Read(); } } }
а это переделанный мной вариант на
Интерфейсах
namespace CSharpConsoleApp { interface IProduct { string GetType(); }
class ProductA : IProduct { public string GetType() { return "ProductA"; } }
class ProductB : IProduct { public string GetType() { return "ProductB"; } } interface ICreatorProduct { IProduct FactoryMethod(); }
class CreatorProductA : ICreatorProduct { public IProduct FactoryMethod() { return new ProductA(); } }
class CreatorProductB : ICreatorProduct { public IProduct FactoryMethod() { return new ProductB(); } }
class Program { static void Main(string[] args) { ICreatorProduct[] productCreators = { new CreatorProductA(), new CreatorProductB() }; foreach (ICreatorProduct creatorProduct in productCreators) { IProduct product = creatorProduct.FactoryMethod(); Console.WriteLine("Created {0}", product.GetType()); }
Console.Read(); } } }

Так какова лучшая практика использования данного паттерна в плане абстракций?


Ответ

Смысл абстрактного класса — совместное использование кода классами-потомками. Абстрактный класс нужен для того, чтобы было легко создавать немного отличающиеся классы, общую часть которых вы выносите в реализованные методы абстрактного класса.
В вашем случае абстрактный класс не нужен. В вашем коде смысл AbstractProduct состоит лишь в том, чтобы в дочернем классе затребовать наличия метода string GetType(). Точно так же смысл AbstractCreator лишь в том, чтобы в дочернем классе затребовать наличия метода AbstractProduct FactoryMethod(). Это прекрасно ложится на предназначение интерфейсов.
Вывод: интерфейсы в данном коде намного более естественны.

Предположу, что код, который использует абстрактные классы, написан для языка C++, в котором интерфейсов просто нет.

Приведение типов в +!{}[0]

Подскажите, пожалуйста, как получилась единица?
+!{}[0] // =1
Спасибо!


Ответ

tl;dr; {}[0] → undefined, !undefined → true, +true → 1

Bracket notation
Первое, что используется в данном выражении: Bracket notation - возможность доступа к свойствам объектов, с использованием квадратных скобок и строкового ключа.
{}[0] - попытка взять свойство с именем 0 в пустом объекте. Так как такое свойство отсутствует - будет получен закономерный результат undefined

оператор логического отрицания(!)
Далее, к результату (undefined) применяется оператор логического отрицания(!). Данный оператор вернет применяет к операнду абстрактный метод ToBoolean, и инвертирует результат.
Как можно заметить из таблицы, для undefined результат ToBoolean(undefined) → false, после инвертирования получаем true

унарный оператор +
Далее, к результату (true) применяется унарный оператор +, который просто переводит результат в число. Для этого используется абстрактная функция ToNumber
Как можно заметить из таблицы, для true результат ToNumber(true) → 1

Как лучше сверстать подобный слайдер?

Саму анимацию то сделать не трудно, вот только стрелки эти, как сделать?


Ответ

Можно составить такую раздвигающуюся стрелку из трех компонентов: хвоста стрелки (.arrow__left), раздвигающейся части (.arrow__center) и острия стрелки (.arrow__right). Центральная часть изначально будет нулевой ширины, при наведении ширина будет увеличиваться. Хвост и остриё обрезать при помощи свойства clip-path. Только обратите внимание на ограниченную поддержку этого свойства браузерами, для фоллбека рекомендую использовать @supports
.arrow { display: flex; } .arrow__left { width: 100px; height: 400px; background-image: url(http://i.imgur.com/vt1Bu3m.jpg); clip-path: polygon(0% 0%, 100% 50%, 0 100%, 100% 100%, 100% 0); } .arrow__right { width: 100px; height: 400px; background-image: url(http://i.imgur.com/vt1Bu3m.jpg); background-position: -100px 0; transition: all .2s; clip-path: polygon(0% 0%, 100% 50%, 0 100%); } .arrow__center { background-image: url(http://i.imgur.com/vt1Bu3m.jpg); background-position: -100px 0; width: 0; transition: width .2s; } .arrow:hover .arrow__center { width: 400px; } .arrow:hover .arrow__right { background-position: -500px 0; }


Динамическая смена ItemsSource в TabControl (Коллекции разных типов)

Есть задача, отображения коллекции данных отфильтрованных и разделённых на 5 вкладок.
Например есть коллекция сериалов и в каждой вкладке либо запланированные к просмотру, либо просмотренные, либо брошенные на середине. Есть также коллекция комиксов, с точно такой же сортировкой по вкладкам. Приложение отображает либо список сериалов, либо список комиксов. У сериалов есть помимо прочего поле "Серии", у комиксов же вместо этого есть поля "Главы" и "Тома". То есть классы для них различаются.
Нужно, чтобы программа работала в одном из двух режимов (либо только для комиксов, либо только для сериалов). Кнопки переключения есть в главном меню.
Вопрос: можно ли подобное реализовать с помощью одного TabControl, при условии следования паттернам MVVM?
Сейчас это реализовано следующим образом:


То есть, я использую 2 TabControl'а и в зависимости от режима отображаю лишь один из них. Не знаю, на сколько правильный такой подход, поэтому и спрашиваю совета у SO.


Ответ

Можно обойтись с использованием ContentControl и заданием DataTemplate для каждого объекта(для сериалов и для комиксов).
Создадим базовый класс представляющий собой родителя для комиксов и сериалов.
public class BaseVM : INotifyPropertyChanged { //какие-то свойства //реализация INotifyPropertyChanged }
Создадим производные классы:
public class MangaVM : BaseVM { public ObservableCollection<тип_комикса> MangaList { get; set; } }
public class AnimeVM : BaseVM { public ObservableCollection<тип_сериала> AnimeList { get; set; } }
public List Entertainments { get; set; } //список который будет в меню выбора public BaseVM SelectedEntertainment { get; set; }//выбранный элемент в зависимости от которого //будет меняться шаблон представления
теперь XAML:




Элемент описывающий меню с выбором я сделал в качестве ListBox

Теперь сам ContentControl. ContentControl сам подберет нужный шаблон представления в зависимости от типа привязанного элемента свойству Content

P.S. Ваша реализация со сменой контролов через Visibility имеет право жить. Так как, при смене Visibility не происходит повторно отрисовка контрола, в отличие от ContentControl при смене Content'a.
Такое решение я применяю когда контрол сложный, содержит много дочерних элементов.

Реализация интерфейса в производном классе

Класс А реализует интерфейсы Serializable и Cloneable. От него наследуется класс B
Реализует ли производный класс интерфейсы базового класса?


Ответ

Реализует. В этом можно убедиться на примере:
public class A implements java.io.Serializable, Cloneable { }
public class B extends A { }
B b = new B(); System.out.println(b instanceof java.io.Serializable); System.out.println(b instanceof Cloneable);
В обоих случаях будет выведено true

По поводу комментария:
Если класс A будет абстрактным:
public abstract class A implements java.io.Serializable, Cloneable { }
то в данном конкретном случае для класса B ничего не поменяется.
Однако если, например, класс A будет выглядеть так:
public abstract class A implements AutoCloseable { }
то в классе public class B extends A придётся реализовать метод close из интерфейса AutoCloseable, потому что иначе возникнет ошибка компиляции:
java.lang.RuntimeException: Uncompilable source code - B is not abstract and does not override abstract method close() in java.lang.AutoCloseable

Есть ли возможность при запуске .bat файла установить факт был ли он запущен вручную или автоматически(Task sheduler)?

OC: Windows 2008 R2 Enterprise
Моя ситация: С помощью Task Sheduler ежедневно запускается определенный .bat файл. Иногда есть потребность запускать его вручную.
Моя задача: При ручном запуске от Юзера, после прохождения команд оставить консоль открытую (Pause) чтобы прочитать логи. При этом после запуска с помощью Task Sheduler процесс должен терминироваться без Паузы. %USERNAME% не подходит, так как TS запускает бптник от того же пользователя, что и запускает его вручную.
Есть ли возможность определить запускает батник Task Sheduler? Может можно терминировать программу другим способом? Например через минуту после того как она дойдет до конца?


Ответ

Вариант 1. Настройте запуск этого самого батника с параметром. В самом батнике проверяйте параметр:
if "%1"=="" pause
или вот так:
if not "%1"=="nopause" pause
Вариант 2. Научите пользователя запускать батник не двойным кликом - а как полагается, из командной строки. Командная строка не закрывается просто так.
Вариант 3. Научите пользователя запускать батник используя FAR Manager. Там можно посмотреть консольный вывод скрыв панели (Ctrl+O)

Но самым "красивым" способом я считаю создание отдельного ярлыка для батника. В этом ярлыке настраивается запуск файла через cmd с ключом /k. Ключ /k означает "выполнить команду и не закрывать консоль":

Как узнать сколько денег на счете сим карты в gsm модеме, Arduino?

Скажите, пожалуйста, как в arduino вы можете узнать, сколько денег на SIM-карте в gsm модеме. Я знаю команду AT ("AT+CUSD=1,\"#100#\""), но я не знаю, как записать ответ на переменную. Помогите сделать это.


Ответ

вопрос был решён довольно давно.. но вдруг кому-то пригодится
char inputGsmFullArr[200] = {0}; //массив для хранения всей смс short int indxGsm = 0; // счетчик символов для буфера String inputGsmFullStr = ""; //входящая строка с gsm модема - полная bool flagGsmActive = false; char ch = NULL;
void loop() { while (gprsSerial.available()) { delay(1); flagGsmActive = true; byte c = gprsSerial.read(); inputGsmFullArr[indxGsm++] = c; logger.LogPrintBytes(c); } if (!gprsSerial.available() && flagGsmActive) { for (int i = 0; i < indxGsm; i++) { inputGsmFullStr += inputGsmFullArr[i]; } ReadGsmStr(inputGsmFullStr); ClearBufferArray(inputGsmFullArr, indxGsm); indxGsm = 0; flagGsmActive = false; Serial.flush(); gprsSerial.flush(); } }
void ClearBufferArray(char *array, short int sizeBuf) // function to clear buffer array { for (byte i = 0; i < sizeBuf; i++) { array[i] = NULL; } }
//Обработка смс приемника-передатчика //---------------------------------- boolean isStringMessage = false; //Флаг, что данные с GSM содержат текстовое сообщение String lineFullStr = ""; // Входная строка построчно из inputGsmFullStr String currNumTmp = ""; //---------------------------------- //--------------------------------------------------------------------------------------------------// void ReadGsmStr(String fullGmsStr) { //Очистили подстроку главной строки lineFullStr = ""; //Читаем посимвольно нашу полную СМС и выдергиваем оттуда подстроки //А вид она имеет следующий
+CMT: "+79999999999","NAME","16/01/28,00:36:38+24"
// 1234567890
// 1234567890
for (int i = 0; i < fullGmsStr.length(); i++) { //Если находим символ возврата каретки if (fullGmsStr.charAt(i) == '
') { // если это продолжение полной смс - само тело без заголовка - 1234567890
- то обрабатываем то что внутри (команды) if (isStringMessage) { lineFullStr.toUpperCase();
CheckGsmSubString(lineFullStr);
isStringMessage = false; } else { CheckGsmSubString(lineFullStr); } lineFullStr = ""; } else if ('
' != fullGmsStr.charAt(i)) { lineFullStr += fullGmsStr.charAt(i); } else if ('
' == fullGmsStr.charAt(i)) { lineFullStr += '
'; } } inputGsmFullStr = ""; //Очищаем то что пришло с GSM } //--------------------------------------------------------------------------------------------------// void CheckGsmSubString(String gsmSubStr) { // если это текстовое сообщение if (StringContains(gsmSubStr, "+CMT")) { //Парсим смс } // если это звонок else if (StringContains(gsmSubStr, "+CLIP")) { //Парсим звонок } // этот блок отлавливает ответ на запрос баланса и отправляет его смской else if (StringContains(gsmSubStr, "+CUSD")) { IsRequestBalanceFromGsm(gsmSubStr); }
//---------------------КОМАНДЫ---------------------------- // делаем запрос баланса (мтс), ответ ловится +CUSD else if (StringContains(gsmSubStr, "BAL")) { gprsSerial.println("ATD#100#"); } } //--------------------------------------------------------------------------------------------------//
// запрос баланса и отправляет его смской +CUSD void IsRequestBalanceFromGsm(String str) { str = str.substring(str.indexOf("Balance"), str.indexOf("r")); delay(1500); str += " is your balance of number " + currentNumber; SendSms("ТУТ НОМЕР НА КОТОРЫЙ ОТПРАВЛЯТЬ ОТВЕТ", str); } //--------------------------------------------------------------------------------------------------// const char q = (char)34; // символ " const char r = (char)13; // символ возврата каретки (Enter) const char z = (char)26; // передача ^Z // Функция отправки смс void SendSms(String number, String text) { logger.LogPrintlnStr("SEND SMS FUNC RETURN: "); logger.LogPrintlnStr("NUMBER: " + number + "
TEXT: " + text); gprsSerial.println("AT+CMGD=4"); //удалить все сообщения на сим карте delay(1500); gprsSerial.print("AT+CMGS="); // send the SMS the number gprsSerial.print(q); // передача в порт символа " gprsSerial.print(number); // передача номера телефона gprsSerial.print(q); // передача в порт символа " gprsSerial.print(r); delay(1500); gprsSerial.println(text); // передача текста сообщения delay(500); gprsSerial.print(z); // передача ^Z gprsSerial.print(r); // передача Enter }

Как изменить цвет вкладки в Яндекс.Браузере?

Как изменить цвет вкладки в Яндекс.Браузере с того, который формируется по иконке сайта на произвольный?
На официальном сайте Яндекса есть только информация о виджетах, выводимых в закладках: https://tech.yandex.ru/browser/tableau/doc/dg/concepts/create-widget-docpage/
Он влияет на изменение цвета вкладки, но крайне нестабильно. После очистки кэша и перезагрузки страницы вкладка становится нужного цвета, но при открытии новых, ранее не открытых страниц в браузере заголовок по-прежнему формируется по иконке.
Сам код манифеста виджета:
{ "api_version": 4, "layout": { "logo": "/favicon.png", "color": "#fff" } }
Еще нашел такой код:

Но толку от него нет, он ничего не меняет.


Ответ

Официальный ответ от службы техподдержки Яндекса:
Цвет активной вкладки определяется автоматически его виджетом. Если у сайта есть свой логотип для виджета на странице Табло, то оформление вкладки будет ему соответствовать. Как сделать виджет, Вы можете прочитать здесь: https://tech.yandex.ru/browser/tableau/doc/dg/concepts/create-widget-docpage/ Если виджета у сайта нет, в этом случае цвет вкладки будет взят из фавиконки сайта.
P.S. От себя добавлю, что в ходе экспериментов выяснилось, что цвет фона в большинстве случаев берется из первого заполненного верхнего правого пикселя иконки и делается на несколько тонов темнее. В ряде случаев цвет формируется по другим алгоритмам (в частности, для очень светлых или очень темных иконок, а также для изображений с сочетанием разных цветов).
К сожалению, на практике Яндекс.Браузер работает с виджетом не совсем корректно, новые открываемые страницы красятся по цвету иконки сайта, а при манипуляциях с history браузера (ajax и т.д.) цвет берется из параметра "color" (цвет логотипа виджета не влияет на окрашивание). Будем надеяться, что в новых версиях браузера все будет работать правильно.

Вызов приватного конструктора родителя при наследовании

Почему я в данной ситуации могу унаследоваться от класса B? Ведь у класса B единственный конструктор, и тот закрытый.
public class Test { class B { private B() { } }
class C extends B { } }
class App { public static void main(String[] args) { Test.C b = new Test().new C(); } }
Несмотря на это я, как видите, могу создавать объекты класса С. Сразу скажу, что приватные методы и поля класса B класс-наследник С не видит, но каким-то образом видит приватный конструктор.
При этом не важно, имеют ли внутренние классы модификатор static или нет. Работает и так, и так.


Ответ

Дело в том, что классы C и B объявлены как внутренние классы в Test. Поэтому класс C вполне себе имеет доступ к приватному конструктору B, равно как и к private-методам B. Например, этот код прекрасно работает:
class B { private B() { }
private void print() { System.out.println("B print"); } }
class C extends B { public void print() { super.print(); } }
Если же эту "связь" между классами B и C убрать, например, разнеся их по разным файлам:
B.java
public class B { private B() { } }
C.java
public class C extends B { }
То возникнет ошибка компиляции:
error: B() has private access in B public class C extends B { }
Всё сводится к тому, что ошибка возникает только в случае, при котором в классе-наследнике нет возможности получить доступ ни к одному из конструкторов родителя. В вашем же случае такая возможность есть.

Как сделать пересекающиеся оси на d3.js?

Добрый день! Создаю стандартные оси OX, OY. Все замечательно, но нулевая точка обозначается по каждой из осей, причем текст пересекается самой осью. Есть ли хорошие способы либо в принципе убрать одну из засечек, либо передвинуть текст? (использую 4-ую версию библиотеки) Изменения в 4-ой версии
Документация
var xScale = d3.scaleLinear() .domain([-2, 2]) .range([-200, 200]); var yScale = d3.scaleLinear() .domain([-2, 2]) .range([200, -200]); var xAxis = d3.axisBottom() .scale(xScale) .ticks(8); var yAxis = d3.axisLeft() .scale(yScale) .ticks(8); d3.select("svg").append("g") .attr("transform", "translate(" + 250 + "," + 250 + ")") .call(xAxis); d3.select("svg").append("g") .attr("transform", "translate(" + 250 + "," + 250 + ")") .call(yAxis);


Ответ

Нашел такое решение. Добавляем метод tickFormat для осей. И далее в функции, если значение равно нулю, меняем на null. Остальные без изменений. При этом нули исчезают. В принцип можно пойти дальше и как-то нарисовать ноль рядом с пересечением, но меня и так устраивает уже.
var xScale = d3.scaleLinear() .domain([-2, 2]) .range([-200, 200]); var yScale = d3.scaleLinear() .domain([-2, 2]) .range([200, -200]); var xAxis = d3.axisBottom() .scale(xScale) .tickFormat(function(d) { if (d == 0) { return null } else { return d } }); var yAxis = d3.axisLeft() .scale(yScale) .tickFormat(function(d) { if (d == 0) { return null } else { return d } }); d3.select("svg").append("g") .attr("transform", "translate(" + 250 + "," + 250 + ")") .call(xAxis); d3.select("svg").append("g") .attr("transform", "translate(" + 250 + "," + 250 + ")") .call(yAxis);

Диагональный штриховой pattern с обоих сторон абзаца



Я пытаюсь добавить пунктирные боковые отступы, как на изображении. Только по бокам текста. Как мне это сделать?
Перевод вопроса: Diagonal dashed pattern on each the side of paragraph@Daniel


Ответ

Вы можете использовать линейный градиент для pattern и техники, описанные в статье: Line before and after title over image , чтобы расположить его по обе стороны заголовка.
Это будет примерно так:
.search { margin: .7em auto; overflow: hidden; padding-left:100px; } .search:before, .search:after { content: ""; display: inline-block; width: 100%; height:0.8em; margin: 0 .5em 0 -105%; vertical-align: middle; background-image: linear-gradient(-45deg, #E0E0E0 25%, transparent 25%, transparent 50%, #E0E0E0 50%, #E0E0E0 75%, transparent 75%, transparent); background-size: 0.5em 0.5em; } .search:after { margin: 0 -105% 0 .5em; } .search p { display: inline-block; vertical-align: middle; }


Перевод ответа: Diagonal dashed pattern on each the side of paragraph @web-tiki

Настройка gulp.spritesmith

Пытаюсь разобраться с плагином gulp.spritesmith, исходя и оф. документации самая простая задача выглядит так:
gulp.task('imgsprite', function() { var spriteData = gulp.src('dev/**/sprite/**/*.{jpg,jpeg,png,gif}') .pipe(spriteImg({ imgName: 'sprite.png', // название собраного спрайта cssName: 'sprite.css', // название css файла })); var stylStream = spriteData.css .pipe(gulp.dest('app/'));
var imgStream = spriteData.img .pipe(gulp.dest('app/'));
return merge (imgStream, stylStream); });
Что я хочу? Я хочу название спрайта генерировать автоматически, в зависимости от папки где лежат исходные картинки. Допустим путь к картинам такой: dev/project/img/sprites/social/ Значит собранный спрайт должен называться social.png путь: dev/project/img/sprites/menu-icon/ собранный спрайт должен называться menu-icon.png Для чего это нужно? В проекте может быть несколько спрайтов, соответственно исходные картинки размещаем в папке sprites и раскладываем по отдельным папкам, название которой и будет именем генерируемого спрайта. Вторая задача - это пути выходных файлов, лежат исходные картинки допустим по пути dev/project/img/sprites/menu-icon/, значит генерированный спрайт должен положиться app/project/img/menu-icon.png Насколько я понимаю нужно анализировать путь к файлам, которые попали в gulp.src, вытаскивать нужные фрагменты, обрабатывать, сохранять в переменные и уже затем использовать их в настройках gulp.spritesmith Но как это сделать придумать не могу. Может быть я сейчас велосипед пытаюсь придумать и уже все есть, буду признателен если кто поделится решением. В сети ничего подобного не встречал.


Ответ

И так, в поисках решения данной задачи я столкнулся с несколькими проблемами, и самая главная - это не очень хорошее понимание процессов NODE.js Вторая проблема - заключалась в получении путей к тем файлам, которые попали в gulp.src, дело в том, что у него нет метода который их возвращает. Он просто создает потоки данных используя vinyl fs и передает их для дальнейшей работы с ними..... Что ж, а что же мешает создать собственный экземпляр glob объекта, тем более последним аргументом конструктор принимает колбэк, в который первым приходит объект ошибки, если таковая случилась, а вторым массив с файлами попавшими в выборку. То что и нужно!. Осталось подключить модуль glob , ну и заодно merge-stream, понадобиться для объединения источников спрайта и файла стилей к нему в один выходной поток.
var glob = require("glob").Glob; var glob = require("glob").Glob;
Ну и создадим саму задачу, создадим в ней новый glob объект с интересующей выборкой и введем несколько переменных:
gulp.task('test', function() { var tmp = new glob('dev/**/sprite/**/*.{jpg,jpeg,png,gif}', {}, function(err, matches) { var dirnames = []; // имена каталогов с картинками для спрайтов var files = []; // массив с данными для spritesmith var destination = []; // каталоги назначения
}); });
Далее сформируем массивы с именами каталогов - его будем использовать для названий выходных файлов и массивом с путями назначения - там будут лежать пути, по которым необходимо разложить полученные спрайты. Для этого пройдемся в цикле по массиву matches и вытащим необходимую инфу, а в него попадает Красным выделено то, что попадет в dirnames, синим в destination
for(var i = 0; i < matches.length; i++) { var itemPath = matches[i].split('/'); dirnames.push(itemPath[itemPath.length - 2]); itemPath[0] = 'app'; itemPath.length = 4; itemPath.push('/'); destination.push(itemPath.join('/')); } dirnames = Array.from(new Set(dirnames)); destination = Array.from(new Set(destination));
теперь наши массивы выглядят так: Теперь необходимо заполнить массив files, для этого снова воспользуемся циклом, но теперь уже по массиву dirnames, ведь в нем лежат название каталогов с картинками для спрайтов и соответственно сколько каталогов, столько и необходимо объектов в массиве files ключи которого будут: data - это массив, в нем будут лежать пути к картинкам в определенном каталоге (этот же массив и будум передавать в gulp.src, он же принимает массив с путями... вот и чудно). filename - сюда положим название каталога и будем использовать для названия спрайта и стилей к нему.
for(var i = 0; i < dirnames.length; i++) { files[i] = {}; files[i].data = []; files[i].filename = dirnames[i]; for(var j = 0; j < matches.length; j++) { if(matches[j].indexOf(dirnames[i]) != -1) { files[i].data.push(matches[j]); } } }
И еще один цикл, что-бы добавить в каждый объект путь для спрайта:
for(var i = 0; i < files.length; i++) { files[i].destImg =''; for(var j = 0; j < destination.length; j++ ) { var str = destination[j].split('/'); if(files[i].data[0].indexOf(str[2]) != -1) { files[i].destImg = destination[j]; } } }
В итоге получим: Ну и осталось теперь пройтись по массиву files и для каждого объекта в нем выполнить функцию:
files.forEach(function(item, i, arr){ var spriteData = gulp.src(arr[i].data) .pipe(spriteImg({ imgName: arr[i].filename + '.png', cssName: arr[i].filename + '.css', imgPath: '../img/' + arr[i].filename + '.png' })); var stylStream = spriteData.css .pipe(gulp.dest('app/tmp/' + arr[i].filename + '/'));
var imgStream = spriteData.img .pipe(gulp.dest(arr[i].destImg))
return merge (imgStream, stylStream); });
В итоге все спрайты разложаться по нужным директория, названия спрайта будет соответствовать названию каталога в котором лежали исходные картинки, а стили сложатся в паку tmp в корне проекта и так же будут разложены по папкам с названиями каталогов. Весь код полностью:
gulp.task('test', function() { var tmp = new glob('dev/**/sprite/**/*.{jpg,jpeg,png,gif}', {}, function(err, matches) { var dirnames = []; // имена каталогов с картинками для спрайтов var files = []; // массив с данными для spritesmith var destination = []; // каталоги назначения for(var i = 0; i < matches.length; i++) { var itemPath = matches[i].split('/'); dirnames.push(itemPath[itemPath.length - 2]); itemPath[0] = 'app'; itemPath.length = 4; destination.push(itemPath.join('/')); } dirnames = Array.from(new Set(dirnames)); destination = Array.from(new Set(destination)); for(var i = 0; i < dirnames.length; i++) { files[i] = {}; files[i].data = []; files[i].filename = dirnames[i]; for(var j = 0; j < matches.length; j++) { if(matches[j].indexOf(dirnames[i]) != -1) { files[i].data.push(matches[j]); } } } for(var i = 0; i < files.length; i++) { files[i].destImg =''; for(var j = 0; j < destination.length; j++ ) { var str = destination[j].split('/'); if(files[i].data[0].indexOf(str[2]) != -1) { files[i].destImg = destination[j]; } } } console.log(files) files.forEach(function(item, i, arr){ var spriteData = gulp.src(arr[i].data) .pipe(spriteImg({ imgName: arr[i].filename + '.png', cssName: arr[i].filename + '.css', imgPath: '../img/' + arr[i].filename + '.png' })); var stylStream = spriteData.css .pipe(gulp.dest('app/tmp/' + arr[i].filename + '/'));
var imgStream = spriteData.img .pipe(gulp.dest(arr[i].destImg))
return merge (imgStream, stylStream); }); }); });
P.S Возможно кто-то более опытный сделает лучше и красивее, буду рад если поделится....